diff --git a/.clang-format b/.clang-format new file mode 100644 index 00000000..41b5a26c --- /dev/null +++ b/.clang-format @@ -0,0 +1,9 @@ +BasedOnStyle: WebKit +BreakConstructorInitializers: AfterColon +PointerAlignment: Right +AlignAfterOpenBracket: Align +AlignConsecutiveAssignments: true +AlignConsecutiveDeclarations: true +AlignTrailingComments: true +ColumnLimit: 120 +CompactNamespaces: true diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 00000000..886ff314 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,64 @@ +name: CI +on: + - push + - pull_request + - workflow_dispatch + +jobs: + test-linux: + runs-on: ubuntu-latest + env: + CCACHE_DIR: ${{github.workspace}}/ccache + CCACHE_MAXSIZE: 100M + strategy: + matrix: + tag: + - minimal + - system-libs + - bundled-libs + include: + - tag: minimal + deps: | + libb2-dev + libqca-qt5-2-dev + options: -DIRIS_ENABLE_JINGLE_SCTP=OFF + - tag: system-libs + deps: | + libb2-dev + libqca-qt5-2-dev + libusrsctp-dev + options: -DIRIS_BUNDLED_QCA=OFF -DIRIS_BUNDLED_USRSCTP=OFF + - tag: bundled-libs + deps: null + options: -DIRIS_BUNDLED_QCA=ON -DIRIS_BUNDLED_USRSCTP=ON + steps: + - name: Checkout + uses: actions/checkout@v3 + - name: Install dependencies + uses: awalsh128/cache-apt-pkgs-action@latest + with: + packages: | + ${{matrix.deps}} + ccache + ninja-build + qtbase5-dev + version: ${{matrix.tag}} + - name: Configure + run: | + cmake -B ${{github.workspace}}/build -G Ninja \ + -DCMAKE_C_COMPILER_LAUNCHER=ccache \ + -DCMAKE_CXX_COMPILER_LAUNCHER=ccache \ + ${{matrix.options}} + - name: Restore cache + uses: actions/cache/restore@v3 + with: + key: ccache-linux-${{matrix.tag}} + path: ${{github.workspace}}/ccache + - name: Build + run: | + cmake --build ${{github.workspace}}/build -v -j $(nproc) + - name: Save cache + uses: actions/cache@v3 + with: + key: ccache-linux-${{matrix.tag}} + path: ${{github.workspace}}/ccache diff --git a/.gitignore b/.gitignore index d764ff90..ceb5082b 100644 --- a/.gitignore +++ b/.gitignore @@ -18,3 +18,5 @@ object_script.* /lib /plugins *.user* +build-* +3rdparty/qca diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 00000000..57837bde --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,29 @@ +# See https://pre-commit.com for more information +# See https://pre-commit.com/hooks.html for more hooks + +exclude: '^3rdparty|COPYING.*|src/jdns' +repos: +- repo: https://github.com/pre-commit/pre-commit-hooks + rev: v2.5.0 + hooks: + - id: trailing-whitespace + - id: end-of-file-fixer + - id: check-added-large-files + - id: check-merge-conflict +- repo: https://github.com/doublify/pre-commit-clang-format + # for clang-tidy we can take github.com/pocc/pre-commit-hooks + rev: f4c4ac5948aff384af2b439bfabb2bdd65d2b3ac + hooks: + - id: clang-format +- repo: https://github.com/Lucas-C/pre-commit-hooks + rev: v1.1.7 + hooks: + - id: forbid-crlf + - id: remove-crlf + - id: forbid-tabs + - id: remove-tabs +- repo: https://github.com/openstack-dev/bashate + rev: 2.0.0 + hooks: + - id: bashate + args: ['--ignore', 'E006'] diff --git a/3rdparty/stringprep/CMakeLists.txt b/3rdparty/stringprep/CMakeLists.txt new file mode 100644 index 00000000..ff4bc9a6 --- /dev/null +++ b/3rdparty/stringprep/CMakeLists.txt @@ -0,0 +1,35 @@ +cmake_minimum_required(VERSION 3.10.0) + +project(stringprep + LANGUAGES CXX +) + +set(CMAKE_AUTOMOC ON) +set(CMAKE_AUTORCC ON) +set(CMAKE_AUTOUIC ON) + +if(QT_DEFAULT_MAJOR_VERSION LESS 6) + find_package(Qt5 5.10 REQUIRED COMPONENTS Core) +else() + find_package(Qt6 REQUIRED COMPONENTS Core) +endif() + +add_library(stringprep STATIC + stringprep.cpp + profiles.cpp + rfc3454.cpp + qstringprep.h +) + +target_compile_definitions(stringprep PRIVATE QSTRINGPREP_BUILDING) + +if(MSVC) + target_compile_definitions(stringprep PRIVATE _CRT_SECURE_NO_WARNINGS _GENERATED_STDINT_H) +endif() + +if(QT_DEFAULT_MAJOR_VERSION LESS 6) + target_link_libraries(stringprep PUBLIC Qt5::Core) +else() + target_link_libraries(stringprep PUBLIC Qt6::Core) +endif() +target_include_directories(stringprep PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) diff --git a/3rdparty/stringprep/COPYING b/3rdparty/stringprep/COPYING new file mode 100644 index 00000000..e6ea90ab --- /dev/null +++ b/3rdparty/stringprep/COPYING @@ -0,0 +1,30 @@ +Libidn COPYING -- Explanation of licensing conditions. +Copyright (C) 2002-2015 Simon Josefsson +See the end for copying conditions. + +The source code for the C library (libidn.a or libidn.so), the C# +library (Libidn.dll) and the Java library (libidn-*.jar) are licensed +under the terms of either the GNU General Public License version 2.0 +or later (see the file COPYINGv2) or the GNU Lesser General Public +License version 3.0 or later (see the file COPYING.LESSERv3), or both +in parallel as here. + +The author of the Java library has agreed to also distribute it under +the Apache License Version 2.0 (see the file java/LICENSE-2.0). + +The manual is licensed under the GNU Free Documentation License, +Version 1.3 or any later. + +The command line tool, self tests, examples, and other auxilliary +files, are licensed under the GNU General Public License version 3.0 +or later. + +Other files are licensed as indicated in each file. + +There may be exceptions to these general rules, see each file for +precise information. + +---------------------------------------------------------------------- +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. diff --git a/3rdparty/stringprep/README.md b/3rdparty/stringprep/README.md new file mode 100644 index 00000000..2ec00709 --- /dev/null +++ b/3rdparty/stringprep/README.md @@ -0,0 +1,7 @@ +This is a stringprep library extracted from LibIDN and slightly rewritten to use Qt's unicode support instead glib's one. + +The current version corresponds to 86e84739c5186faf3722a0f42e1e2db27870b3a5 commit of git://git.savannah.gnu.org/libidn.git + +The necessity of usage of separate stringprep library is described here: https://gitlab.com/libidn/libidn2/-/issues/28 + +Note this directory contains generated rfc3454 files from rfc3454.txt. It's very unlikely these files will ever be regenerated but just in case the directory also contains both rfc3454.txt and a perl script to generate the files. diff --git a/3rdparty/stringprep/gen-stringprep-tables.pl b/3rdparty/stringprep/gen-stringprep-tables.pl new file mode 100755 index 00000000..13eaef64 --- /dev/null +++ b/3rdparty/stringprep/gen-stringprep-tables.pl @@ -0,0 +1,130 @@ +#! /usr/bin/perl -w + +# Copyright (C) 2002-2016 Simon Josefsson + +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +# I consider the output of this program to be unrestricted. Use it as +# you will. + +use strict; + +my ($tab) = 59; +my ($intable) = 0; +my ($entries) = 0; +my ($tablename); +my ($varname); +my ($starheader, $header); +my ($profile) = "rfc3454"; +my ($filename) = "$profile.c"; +my ($headername) = "$profile.h"; +my ($line, $start, $end, @map); + +open(FH, ">$filename") or die "cannot open $filename for writing"; + +print FH "/* This file is automatically generated. DO NOT EDIT!\n"; +print FH " Instead, edit gen-stringprep-tables.pl and re-run. */\n\n"; + +print FH "#include \n"; +print FH "#include \"stringprep.h\"\n"; + +open(FHH, ">$headername") or die "cannot open $headername for writing"; +print FHH "/* This file is automatically generated. DO NOT EDIT!\n"; +print FHH " Instead, edit gen-stringprep-tables.pl and re-run. */\n\n"; + +while(<>) { + s/^ (.*)/$1/g; # for rfc + $line = $_; + + die "already in table" if $intable && m,^----- Start Table (.*) -----,; + die "not in table" if !$intable && m,^----- End Table (.*) -----,; + + if ($intable && m,^----- End Table (.*) -----,) { + die "table error" unless $1 eq $tablename || + ($1 eq "C.1.2" && $tablename eq "C.1.1"); # Typo in draft + print FH " { 0 },\n"; + print FH "};\n\n"; + print FHH "#define N_STRINGPREP_${profile}_${varname} ${entries}\n"; + $intable = 0; + $entries = 0; + } + + if (m,^[A-Z],) { + $header = $line; + } elsif (!m,^[ -],) { + $header .= $line; + } + + next unless ($intable || m,^----- Start Table (.*) -----,); + + if ($intable) { + $_ = $line; + chop $line; + + next if m,^$,; + next if m,^Hoffman & Blanchet Standards Track \[Page [0-9]+\]$,; + next if m,^ $,; + next if m,RFC 3454 Preparation of Internationalized Strings December 2002,; + + die "regexp failed on line: $line" unless + m,^([0-9A-F]+)(-([0-9A-F]+))?(; ([0-9A-F]+)( ([0-9A-F]+))?( ([0-9A-F]+))?( ([0-9A-F]+))?;)?,; + + die "too many mapping targets on line: $line" if $12; + + $start = $1; + $end = $3; + $map[0] = $5; + $map[1] = $7; + $map[2] = $9; + $map[3] = $11; + + die "tables tried to map a range" if $end && $map[0]; + + if ($map[3]) { + printf FH " { 0x%06s, 0x%06s, { 0x%06s,%*s/* %s */\n 0x%06s, 0x%06s, 0x%06s }},\n", + $start, $start, $map[0], $tab-length($line)-13, " ", $line, + $map[1], $map[2], $map[3]; + } elsif ($map[2]) { + printf FH " { 0x%06s, 0x%06s, { 0x%06s,%*s/* %s */\n 0x%06s, 0x%06s }},\n", + $start, $start, $map[0], $tab-length($line)-14, " ", $line, + $map[1], $map[2]; + } elsif ($map[1]) { + printf FH " { 0x%06s, 0x%06s, { 0x%06s,%*s/* %s */\n 0x%06s }},\n", + $start, $start, $map[0], $tab-length($line)-14, " ", $line, + $map[1]; + } elsif ($map[0]) { + printf FH " { 0x%06s, 0x%06s, { 0x%06s }},%*s/* %s */\n", + $start, $start, $map[0], $tab-length($line)-17, " ", $line; + } elsif ($end) { + printf FH " { 0x%06s, 0x%06s },%*s/* %s */\n", + $start, $end, $tab-length($line)-11, " ", $line; + } else { + printf FH " { 0x%06s, 0x%06s },%*s/* %s */\n", + $start, $start, $tab-length($line)-11, " ", $line; + } + $entries++; + } else { + $intable = 1 if !$intable; + $tablename = $1; + + ($varname = $tablename) =~ tr/./_/; + $header =~ s/\n/\n * /s; + + print FH "\n/*\n * $header */\n\n"; + print FH "const Stringprep_table_element stringprep_${profile}_${varname}\[\] = {\n"; + } +} + +close FHH or die "cannot close $headername"; +close FH or die "cannot close $filename"; diff --git a/3rdparty/stringprep/profiles.cpp b/3rdparty/stringprep/profiles.cpp new file mode 100644 index 00000000..f63a532a --- /dev/null +++ b/3rdparty/stringprep/profiles.cpp @@ -0,0 +1,246 @@ +/* profiles.c --- Definitions of stringprep profiles. + Copyright (C) 2002-2016 Simon Josefsson + + This file is part of GNU Libidn. + + GNU Libidn is free software: you can redistribute it and/or + modify it under the terms of either: + + * the GNU Lesser General Public License as published by the Free + Software Foundation; either version 3 of the License, or (at + your option) any later version. + + or + + * the GNU General Public License as published by the Free + Software Foundation; either version 2 of the License, or (at + your option) any later version. + + or both in parallel, as here. + + GNU Libidn is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received copies of the GNU General Public License and + the GNU Lesser General Public License along with this program. If + not, see . */ + +#include "qstringprep.h" +#include "rfc3454.h" + +const Stringprep_profiles stringprep_profiles[] = { { "Nameprep", stringprep_nameprep }, + { "KRBprep", stringprep_kerberos5 }, /* Deprecate? */ + { "Nodeprep", stringprep_xmpp_nodeprep }, + { "Resourceprep", stringprep_xmpp_resourceprep }, + { "plain", stringprep_plain }, /* sasl-anon-00. */ + { "trace", stringprep_trace }, /* sasl-anon-01,02,03. */ + { "SASLprep", stringprep_saslprep }, + { "ISCSIprep", stringprep_iscsi }, /* Obsolete. */ + { "iSCSI", stringprep_iscsi }, /* IANA. */ + { NULL, NULL } }; + +/* number of elements within an array */ +#define countof(a) (sizeof(a) / sizeof(*(a))) + +/* helper for profile definitions */ +#define TABLE(x) stringprep_rfc3454_##x, N_STRINGPREP_rfc3454_##x + +const Stringprep_profile stringprep_nameprep[] + = { { STRINGPREP_MAP_TABLE, 0, TABLE(B_1) }, + { STRINGPREP_MAP_TABLE, 0, TABLE(B_2) }, + { STRINGPREP_NFKC, 0, 0, 0 }, + { STRINGPREP_PROHIBIT_TABLE, 0, TABLE(C_1_2) }, + { STRINGPREP_PROHIBIT_TABLE, 0, TABLE(C_2_2) }, + { STRINGPREP_PROHIBIT_TABLE, 0, TABLE(C_3) }, + { STRINGPREP_PROHIBIT_TABLE, 0, TABLE(C_4) }, + { STRINGPREP_PROHIBIT_TABLE, 0, TABLE(C_5) }, + { STRINGPREP_PROHIBIT_TABLE, 0, TABLE(C_6) }, + { STRINGPREP_PROHIBIT_TABLE, 0, TABLE(C_7) }, + { STRINGPREP_PROHIBIT_TABLE, 0, TABLE(C_8) }, + { STRINGPREP_PROHIBIT_TABLE, 0, TABLE(C_9) }, + { STRINGPREP_BIDI, 0, 0, 0 }, + { STRINGPREP_BIDI_PROHIBIT_TABLE, ~STRINGPREP_NO_BIDI, TABLE(C_8) }, + { STRINGPREP_BIDI_RAL_TABLE, 0, TABLE(D_1) }, + { STRINGPREP_BIDI_L_TABLE, 0, TABLE(D_2) }, + { STRINGPREP_UNASSIGNED_TABLE, ~STRINGPREP_NO_UNASSIGNED, TABLE(A_1) }, + { 0 } }; + +const Stringprep_profile stringprep_kerberos5[] = { + /* XXX this is likely to be wrong as the specification is + a rough draft. */ + { STRINGPREP_MAP_TABLE, 0, TABLE(B_1) }, + { STRINGPREP_MAP_TABLE, 0, TABLE(B_3) }, + { STRINGPREP_NFKC, 0, 0, 0 }, + { STRINGPREP_PROHIBIT_TABLE, 0, TABLE(C_1_2) }, + { STRINGPREP_PROHIBIT_TABLE, 0, TABLE(C_2_2) }, + { STRINGPREP_PROHIBIT_TABLE, 0, TABLE(C_3) }, + { STRINGPREP_PROHIBIT_TABLE, 0, TABLE(C_4) }, + { STRINGPREP_PROHIBIT_TABLE, 0, TABLE(C_5) }, + { STRINGPREP_PROHIBIT_TABLE, 0, TABLE(C_6) }, + { STRINGPREP_PROHIBIT_TABLE, 0, TABLE(C_7) }, + { STRINGPREP_PROHIBIT_TABLE, 0, TABLE(C_8) }, + { STRINGPREP_PROHIBIT_TABLE, 0, TABLE(C_9) }, + { STRINGPREP_BIDI, 0, 0, 0 }, + { STRINGPREP_BIDI_PROHIBIT_TABLE, ~STRINGPREP_NO_BIDI, TABLE(C_8) }, + { STRINGPREP_BIDI_RAL_TABLE, 0, TABLE(D_1) }, + { STRINGPREP_BIDI_L_TABLE, 0, TABLE(D_2) }, + { STRINGPREP_UNASSIGNED_TABLE, ~STRINGPREP_NO_UNASSIGNED, TABLE(A_1) }, + { 0 } +}; + +const Stringprep_table_element stringprep_xmpp_nodeprep_prohibit[] = { { 0x000022, 0x000022 }, /* #x22 (") */ + { 0x000026, 0x000026 }, /* #x26 (&) */ + { 0x000027, 0x000027 }, /* #x27 (') */ + { 0x00002F, 0x00002F }, /* #x2F (/) */ + { 0x00003A, 0x00003A }, /* #x3A (:) */ + { 0x00003C, 0x00003C }, /* #x3C (<) */ + { 0x00003E, 0x00003E }, /* #x3E (>) */ + { 0x000040, 0x000040 }, /* #x40 (@) */ + { 0 } }; + +const Stringprep_profile stringprep_xmpp_nodeprep[] + = { { STRINGPREP_MAP_TABLE, 0, TABLE(B_1) }, + { STRINGPREP_MAP_TABLE, 0, TABLE(B_2) }, + { STRINGPREP_NFKC, 0, 0, 0 }, + { STRINGPREP_PROHIBIT_TABLE, 0, TABLE(C_1_1) }, + { STRINGPREP_PROHIBIT_TABLE, 0, TABLE(C_1_2) }, + { STRINGPREP_PROHIBIT_TABLE, 0, TABLE(C_2_1) }, + { STRINGPREP_PROHIBIT_TABLE, 0, TABLE(C_2_2) }, + { STRINGPREP_PROHIBIT_TABLE, 0, TABLE(C_3) }, + { STRINGPREP_PROHIBIT_TABLE, 0, TABLE(C_4) }, + { STRINGPREP_PROHIBIT_TABLE, 0, TABLE(C_5) }, + { STRINGPREP_PROHIBIT_TABLE, 0, TABLE(C_6) }, + { STRINGPREP_PROHIBIT_TABLE, 0, TABLE(C_7) }, + { STRINGPREP_PROHIBIT_TABLE, 0, TABLE(C_8) }, + { STRINGPREP_PROHIBIT_TABLE, 0, TABLE(C_9) }, + { STRINGPREP_PROHIBIT_TABLE, 0, stringprep_xmpp_nodeprep_prohibit, + countof(stringprep_xmpp_nodeprep_prohibit) - 1 }, + { STRINGPREP_BIDI, 0, 0, 0 }, + { STRINGPREP_BIDI_PROHIBIT_TABLE, 0, TABLE(C_8) }, + { STRINGPREP_BIDI_RAL_TABLE, 0, TABLE(D_1) }, + { STRINGPREP_BIDI_L_TABLE, 0, TABLE(D_2) }, + { STRINGPREP_UNASSIGNED_TABLE, ~STRINGPREP_NO_UNASSIGNED, TABLE(A_1) }, + { 0 } }; + +const Stringprep_profile stringprep_xmpp_resourceprep[] + = { { STRINGPREP_MAP_TABLE, 0, TABLE(B_1) }, + { STRINGPREP_NFKC, 0, 0, 0 }, + { STRINGPREP_PROHIBIT_TABLE, 0, TABLE(C_1_2) }, + { STRINGPREP_PROHIBIT_TABLE, 0, TABLE(C_2_1) }, + { STRINGPREP_PROHIBIT_TABLE, 0, TABLE(C_2_2) }, + { STRINGPREP_PROHIBIT_TABLE, 0, TABLE(C_3) }, + { STRINGPREP_PROHIBIT_TABLE, 0, TABLE(C_4) }, + { STRINGPREP_PROHIBIT_TABLE, 0, TABLE(C_5) }, + { STRINGPREP_PROHIBIT_TABLE, 0, TABLE(C_6) }, + { STRINGPREP_PROHIBIT_TABLE, 0, TABLE(C_7) }, + { STRINGPREP_PROHIBIT_TABLE, 0, TABLE(C_8) }, + { STRINGPREP_PROHIBIT_TABLE, 0, TABLE(C_9) }, + { STRINGPREP_BIDI, 0, 0, 0 }, + { STRINGPREP_BIDI_PROHIBIT_TABLE, 0, TABLE(C_8) }, + { STRINGPREP_BIDI_RAL_TABLE, ~STRINGPREP_NO_BIDI, TABLE(D_1) }, + { STRINGPREP_BIDI_L_TABLE, ~STRINGPREP_NO_BIDI, TABLE(D_2) }, + { STRINGPREP_UNASSIGNED_TABLE, ~STRINGPREP_NO_UNASSIGNED, TABLE(A_1) }, + { 0 } }; + +const Stringprep_profile stringprep_plain[] = { { STRINGPREP_PROHIBIT_TABLE, 0, TABLE(C_2_1) }, + { STRINGPREP_PROHIBIT_TABLE, 0, TABLE(C_2_2) }, + { STRINGPREP_PROHIBIT_TABLE, 0, TABLE(C_3) }, + { STRINGPREP_PROHIBIT_TABLE, 0, TABLE(C_4) }, + { STRINGPREP_PROHIBIT_TABLE, 0, TABLE(C_5) }, + { STRINGPREP_PROHIBIT_TABLE, 0, TABLE(C_6) }, + { STRINGPREP_PROHIBIT_TABLE, 0, TABLE(C_8) }, + { STRINGPREP_PROHIBIT_TABLE, 0, TABLE(C_9) }, + { STRINGPREP_BIDI, 0, 0, 0 }, + { STRINGPREP_BIDI_PROHIBIT_TABLE, 0, TABLE(C_8) }, + { STRINGPREP_BIDI_RAL_TABLE, ~STRINGPREP_NO_BIDI, TABLE(D_1) }, + { STRINGPREP_BIDI_L_TABLE, ~STRINGPREP_NO_BIDI, TABLE(D_2) }, + { 0 } }; + +const Stringprep_profile stringprep_trace[] = { { STRINGPREP_PROHIBIT_TABLE, 0, TABLE(C_2_1) }, + { STRINGPREP_PROHIBIT_TABLE, 0, TABLE(C_2_2) }, + { STRINGPREP_PROHIBIT_TABLE, 0, TABLE(C_3) }, + { STRINGPREP_PROHIBIT_TABLE, 0, TABLE(C_4) }, + { STRINGPREP_PROHIBIT_TABLE, 0, TABLE(C_5) }, + { STRINGPREP_PROHIBIT_TABLE, 0, TABLE(C_6) }, + { STRINGPREP_PROHIBIT_TABLE, 0, TABLE(C_8) }, + { STRINGPREP_PROHIBIT_TABLE, 0, TABLE(C_9) }, + { STRINGPREP_BIDI, 0, 0, 0 }, + { STRINGPREP_BIDI_PROHIBIT_TABLE, 0, TABLE(C_8) }, + { STRINGPREP_BIDI_RAL_TABLE, ~STRINGPREP_NO_BIDI, TABLE(D_1) }, + { STRINGPREP_BIDI_L_TABLE, ~STRINGPREP_NO_BIDI, TABLE(D_2) }, + { 0 } }; + +const Stringprep_table_element stringprep_iscsi_prohibit[] + = { { 0x0000, 0x002C }, /* [ASCII CONTROL CHARACTERS and SPACE through ,] */ + { 0x002F, 0x002F }, /* [ASCII /] */ + { 0x003B, 0x0040 }, /* [ASCII ; through @] */ + { 0x005B, 0x0060 }, /* [ASCII [ through `] */ + { 0x007B, 0x007F }, /* [ASCII { through DEL] */ + { 0x3002, 0x3002 }, /* ideographic full stop */ + { 0 } }; + +const Stringprep_profile stringprep_iscsi[] + = { { STRINGPREP_MAP_TABLE, 0, TABLE(B_1) }, + { STRINGPREP_MAP_TABLE, 0, TABLE(B_2) }, + { STRINGPREP_NFKC, 0, 0, 0 }, + { STRINGPREP_PROHIBIT_TABLE, 0, TABLE(C_1_1) }, + { STRINGPREP_PROHIBIT_TABLE, 0, TABLE(C_1_2) }, + { STRINGPREP_PROHIBIT_TABLE, 0, TABLE(C_2_1) }, + { STRINGPREP_PROHIBIT_TABLE, 0, TABLE(C_2_2) }, + { STRINGPREP_PROHIBIT_TABLE, 0, TABLE(C_3) }, + { STRINGPREP_PROHIBIT_TABLE, 0, TABLE(C_4) }, + { STRINGPREP_PROHIBIT_TABLE, 0, TABLE(C_5) }, + { STRINGPREP_PROHIBIT_TABLE, 0, TABLE(C_6) }, + { STRINGPREP_PROHIBIT_TABLE, 0, TABLE(C_7) }, + { STRINGPREP_PROHIBIT_TABLE, 0, TABLE(C_8) }, + { STRINGPREP_PROHIBIT_TABLE, 0, TABLE(C_9) }, + { STRINGPREP_PROHIBIT_TABLE, 0, stringprep_iscsi_prohibit, countof(stringprep_iscsi_prohibit) - 1 }, + { STRINGPREP_BIDI, 0, 0, 0 }, + { STRINGPREP_BIDI_PROHIBIT_TABLE, 0, TABLE(C_8) }, + { STRINGPREP_BIDI_RAL_TABLE, ~STRINGPREP_NO_BIDI, TABLE(D_1) }, + { STRINGPREP_BIDI_L_TABLE, ~STRINGPREP_NO_BIDI, TABLE(D_2) }, + { STRINGPREP_UNASSIGNED_TABLE, ~STRINGPREP_NO_UNASSIGNED, TABLE(A_1) }, + { 0 } }; + +const Stringprep_table_element stringprep_saslprep_space_map[] + = { { 0x00A0, 0x00A0, { 0x0020 } }, /* 00A0; NO-BREAK SPACE */ + { 0x1680, 0x1680, { 0x0020 } }, /* 1680; OGHAM SPACE MARK */ + { 0x2000, 0x200B, { 0x0020 } }, /* 2000; EN QUAD */ + /* 2001; EM QUAD */ + /* 2002; EN SPACE */ + /* 2003; EM SPACE */ + /* 2004; THREE-PER-EM SPACE */ + /* 2005; FOUR-PER-EM SPACE */ + /* 2006; SIX-PER-EM SPACE */ + /* 2007; FIGURE SPACE */ + /* 2008; PUNCTUATION SPACE */ + /* 2009; THIN SPACE */ + /* 200A; HAIR SPACE */ + /* 200B; ZERO WIDTH SPACE */ + { 0x202F, 0x202F, { 0x0020 } }, /* 202F; NARROW NO-BREAK SPACE */ + { 0x205F, 0x205F, { 0x0020 } }, /* 205F; MEDIUM MATHEMATICAL SPACE */ + { 0x3000, 0x3000, { 0x0020 } }, /* 3000; IDEOGRAPHIC SPACE */ + { 0 } }; + +const Stringprep_profile stringprep_saslprep[] + = { { STRINGPREP_MAP_TABLE, 0, stringprep_saslprep_space_map, countof(stringprep_saslprep_space_map) - 1 }, + { STRINGPREP_MAP_TABLE, 0, TABLE(B_1) }, + { STRINGPREP_NFKC, 0, 0, 0 }, + { STRINGPREP_PROHIBIT_TABLE, 0, TABLE(C_1_2) }, + { STRINGPREP_PROHIBIT_TABLE, 0, TABLE(C_2_1) }, + { STRINGPREP_PROHIBIT_TABLE, 0, TABLE(C_2_2) }, + { STRINGPREP_PROHIBIT_TABLE, 0, TABLE(C_3) }, + { STRINGPREP_PROHIBIT_TABLE, 0, TABLE(C_4) }, + { STRINGPREP_PROHIBIT_TABLE, 0, TABLE(C_5) }, + { STRINGPREP_PROHIBIT_TABLE, 0, TABLE(C_6) }, + { STRINGPREP_PROHIBIT_TABLE, 0, TABLE(C_7) }, + { STRINGPREP_PROHIBIT_TABLE, 0, TABLE(C_8) }, + { STRINGPREP_PROHIBIT_TABLE, 0, TABLE(C_9) }, + { STRINGPREP_BIDI, 0, 0, 0 }, + { STRINGPREP_BIDI_PROHIBIT_TABLE, 0, TABLE(C_8) }, + { STRINGPREP_BIDI_RAL_TABLE, ~STRINGPREP_NO_BIDI, TABLE(D_1) }, + { STRINGPREP_BIDI_L_TABLE, ~STRINGPREP_NO_BIDI, TABLE(D_2) }, + { STRINGPREP_UNASSIGNED_TABLE, ~STRINGPREP_NO_UNASSIGNED, TABLE(A_1) }, + { 0 } }; diff --git a/3rdparty/stringprep/qstringprep.h b/3rdparty/stringprep/qstringprep.h new file mode 100644 index 00000000..7bb84f0c --- /dev/null +++ b/3rdparty/stringprep/qstringprep.h @@ -0,0 +1,180 @@ +/* stringprep.h --- Header file for stringprep functions. + Copyright (C) 2002-2016 Simon Josefsson + + This file is part of GNU Libidn. + + GNU Libidn is free software: you can redistribute it and/or + modify it under the terms of either: + + * the GNU Lesser General Public License as published by the Free + Software Foundation; either version 3 of the License, or (at + your option) any later version. + + or + + * the GNU General Public License as published by the Free + Software Foundation; either version 2 of the License, or (at + your option) any later version. + + or both in parallel, as here. + + GNU Libidn is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received copies of the GNU General Public License and + the GNU Lesser General Public License along with this program. If + not, see . */ + +#ifndef STRINGPREP_H +#define STRINGPREP_H + +#include + +#if defined(QSTRINGPREP_BUILDING) +#define IDNAPI Q_DECL_EXPORT +#else +#define IDNAPI Q_DECL_IMPORT +#endif + +#include /* size_t */ +#include /* uint32_t */ + +class QString; + +#define STRINGPREP_VERSION "1.35" + +/* Error codes. */ +typedef enum { + STRINGPREP_OK = 0, + /* Stringprep errors. */ + STRINGPREP_CONTAINS_UNASSIGNED = 1, + STRINGPREP_CONTAINS_PROHIBITED = 2, + STRINGPREP_BIDI_BOTH_L_AND_RAL = 3, + STRINGPREP_BIDI_LEADTRAIL_NOT_RAL = 4, + STRINGPREP_BIDI_CONTAINS_PROHIBITED = 5, + /* Error in calling application. */ + STRINGPREP_TOO_SMALL_BUFFER = 100, + STRINGPREP_PROFILE_ERROR = 101, + STRINGPREP_FLAG_ERROR = 102, + STRINGPREP_UNKNOWN_PROFILE = 103, + STRINGPREP_ICONV_ERROR = 104, + /* Internal errors. */ + STRINGPREP_NFKC_FAILED = 200, + STRINGPREP_MALLOC_ERROR = 201 +} Stringprep_rc; + +enum Stringprep_profile_flags { STRINGPREP_NO_NFKC = 1, STRINGPREP_NO_BIDI = 2, STRINGPREP_NO_UNASSIGNED = 4 }; + +/* Steps in a stringprep profile. */ +typedef enum { + STRINGPREP_NFKC = 1, + STRINGPREP_BIDI = 2, + STRINGPREP_MAP_TABLE = 3, + STRINGPREP_UNASSIGNED_TABLE = 4, + STRINGPREP_PROHIBIT_TABLE = 5, + STRINGPREP_BIDI_PROHIBIT_TABLE = 6, + STRINGPREP_BIDI_RAL_TABLE = 7, + STRINGPREP_BIDI_L_TABLE = 8 +} Stringprep_profile_steps; + +#define STRINGPREP_MAX_MAP_CHARS 4 + +struct Stringprep_table_element { + uint32_t start; + uint32_t end = 0; /* 0 if only one character */ + uint32_t map[STRINGPREP_MAX_MAP_CHARS] = {}; /* NULL if end is not 0 */ +}; +typedef struct Stringprep_table_element Stringprep_table_element; + +struct Stringprep_table { + Stringprep_table(int operation, long flags = 0, const Stringprep_table_element *table = nullptr, + size_t table_size = 0) : + operation(Stringprep_profile_steps(operation)), + flags(Stringprep_profile_flags(flags)), table(table), table_size(table_size) + { + } + + Stringprep_profile_steps operation = Stringprep_profile_steps(0); + Stringprep_profile_flags flags = Stringprep_profile_flags(0); + const Stringprep_table_element *table = nullptr; + size_t table_size = 0; +}; +typedef struct Stringprep_table Stringprep_profile; + +struct Stringprep_profiles { + const char * name; + const Stringprep_profile *tables; +}; +typedef struct Stringprep_profiles Stringprep_profiles; + +extern IDNAPI const Stringprep_profiles stringprep_profiles[]; + +/* Profiles */ +extern IDNAPI const Stringprep_table_element stringprep_rfc3454_A_1[]; +extern IDNAPI const Stringprep_table_element stringprep_rfc3454_B_1[]; +extern IDNAPI const Stringprep_table_element stringprep_rfc3454_B_2[]; +extern IDNAPI const Stringprep_table_element stringprep_rfc3454_B_3[]; +extern IDNAPI const Stringprep_table_element stringprep_rfc3454_C_1_1[]; +extern IDNAPI const Stringprep_table_element stringprep_rfc3454_C_1_2[]; +extern IDNAPI const Stringprep_table_element stringprep_rfc3454_C_2_1[]; +extern IDNAPI const Stringprep_table_element stringprep_rfc3454_C_2_2[]; +extern IDNAPI const Stringprep_table_element stringprep_rfc3454_C_3[]; +extern IDNAPI const Stringprep_table_element stringprep_rfc3454_C_4[]; +extern IDNAPI const Stringprep_table_element stringprep_rfc3454_C_5[]; +extern IDNAPI const Stringprep_table_element stringprep_rfc3454_C_6[]; +extern IDNAPI const Stringprep_table_element stringprep_rfc3454_C_7[]; +extern IDNAPI const Stringprep_table_element stringprep_rfc3454_C_8[]; +extern IDNAPI const Stringprep_table_element stringprep_rfc3454_C_9[]; +extern IDNAPI const Stringprep_table_element stringprep_rfc3454_D_1[]; +extern IDNAPI const Stringprep_table_element stringprep_rfc3454_D_2[]; + +/* Nameprep */ + +extern IDNAPI const Stringprep_profile stringprep_nameprep[]; + +#define stringprep_nameprep(in, maxlen) stringprep(in, maxlen, 0, stringprep_nameprep) + +#define stringprep_nameprep_no_unassigned(in, maxlen) \ + stringprep(in, maxlen, STRINGPREP_NO_UNASSIGNED, stringprep_nameprep) + +/* SASL */ + +extern IDNAPI const Stringprep_profile stringprep_saslprep[]; +extern IDNAPI const Stringprep_table_element stringprep_saslprep_space_map[]; +extern IDNAPI const Stringprep_profile stringprep_plain[]; +extern IDNAPI const Stringprep_profile stringprep_trace[]; + +#define stringprep_plain(in, maxlen) stringprep(in, maxlen, 0, stringprep_plain) + +/* Kerberos */ + +extern IDNAPI const Stringprep_profile stringprep_kerberos5[]; + +#define stringprep_kerberos5(in, maxlen) stringprep(in, maxlen, 0, stringprep_kerberos5) + +/* XMPP */ + +extern IDNAPI const Stringprep_profile stringprep_xmpp_nodeprep[]; +extern IDNAPI const Stringprep_profile stringprep_xmpp_resourceprep[]; +extern IDNAPI const Stringprep_table_element stringprep_xmpp_nodeprep_prohibit[]; + +#define stringprep_xmpp_nodeprep(in, maxlen) stringprep(in, maxlen, 0, stringprep_xmpp_nodeprep) +#define stringprep_xmpp_resourceprep(in, maxlen) stringprep(in, maxlen, 0, stringprep_xmpp_resourceprep) + +/* iSCSI */ + +extern IDNAPI const Stringprep_profile stringprep_iscsi[]; +extern IDNAPI const Stringprep_table_element stringprep_iscsi_prohibit[]; + +#define stringprep_iscsi(in, maxlen) stringprep(in, maxlen, 0, stringprep_iscsi) + +/* API */ + +extern IDNAPI int stringprep_4i(QString &input, Stringprep_profile_flags flags, const Stringprep_profile *profile); +extern IDNAPI int stringprep(QString &input, Stringprep_profile_flags flags, const Stringprep_profile *profile); + +extern IDNAPI int stringprep_profile(const char *in, char **out, const char *profile, Stringprep_profile_flags flags); + +#endif /* STRINGPREP_H */ diff --git a/3rdparty/stringprep/rfc3454.cpp b/3rdparty/stringprep/rfc3454.cpp new file mode 100644 index 00000000..ac74b41e --- /dev/null +++ b/3rdparty/stringprep/rfc3454.cpp @@ -0,0 +1,4763 @@ +/* This file is automatically generated. DO NOT EDIT! + Instead, edit gen-stringprep-tables.pl and re-run. */ + +#include "qstringprep.h" + +/* + * So we can use this material. There may be other legal analysis that + * also reach the same conclusion. + + */ + +const Stringprep_table_element stringprep_rfc3454_A_1[] = { + { 0x000221, 0x000221 }, /* 0221 */ + { 0x000234, 0x00024F }, /* 0234-024F */ + { 0x0002AE, 0x0002AF }, /* 02AE-02AF */ + { 0x0002EF, 0x0002FF }, /* 02EF-02FF */ + { 0x000350, 0x00035F }, /* 0350-035F */ + { 0x000370, 0x000373 }, /* 0370-0373 */ + { 0x000376, 0x000379 }, /* 0376-0379 */ + { 0x00037B, 0x00037D }, /* 037B-037D */ + { 0x00037F, 0x000383 }, /* 037F-0383 */ + { 0x00038B, 0x00038B }, /* 038B */ + { 0x00038D, 0x00038D }, /* 038D */ + { 0x0003A2, 0x0003A2 }, /* 03A2 */ + { 0x0003CF, 0x0003CF }, /* 03CF */ + { 0x0003F7, 0x0003FF }, /* 03F7-03FF */ + { 0x000487, 0x000487 }, /* 0487 */ + { 0x0004CF, 0x0004CF }, /* 04CF */ + { 0x0004F6, 0x0004F7 }, /* 04F6-04F7 */ + { 0x0004FA, 0x0004FF }, /* 04FA-04FF */ + { 0x000510, 0x000530 }, /* 0510-0530 */ + { 0x000557, 0x000558 }, /* 0557-0558 */ + { 0x000560, 0x000560 }, /* 0560 */ + { 0x000588, 0x000588 }, /* 0588 */ + { 0x00058B, 0x000590 }, /* 058B-0590 */ + { 0x0005A2, 0x0005A2 }, /* 05A2 */ + { 0x0005BA, 0x0005BA }, /* 05BA */ + { 0x0005C5, 0x0005CF }, /* 05C5-05CF */ + { 0x0005EB, 0x0005EF }, /* 05EB-05EF */ + { 0x0005F5, 0x00060B }, /* 05F5-060B */ + { 0x00060D, 0x00061A }, /* 060D-061A */ + { 0x00061C, 0x00061E }, /* 061C-061E */ + { 0x000620, 0x000620 }, /* 0620 */ + { 0x00063B, 0x00063F }, /* 063B-063F */ + { 0x000656, 0x00065F }, /* 0656-065F */ + { 0x0006EE, 0x0006EF }, /* 06EE-06EF */ + { 0x0006FF, 0x0006FF }, /* 06FF */ + { 0x00070E, 0x00070E }, /* 070E */ + { 0x00072D, 0x00072F }, /* 072D-072F */ + { 0x00074B, 0x00077F }, /* 074B-077F */ + { 0x0007B2, 0x000900 }, /* 07B2-0900 */ + { 0x000904, 0x000904 }, /* 0904 */ + { 0x00093A, 0x00093B }, /* 093A-093B */ + { 0x00094E, 0x00094F }, /* 094E-094F */ + { 0x000955, 0x000957 }, /* 0955-0957 */ + { 0x000971, 0x000980 }, /* 0971-0980 */ + { 0x000984, 0x000984 }, /* 0984 */ + { 0x00098D, 0x00098E }, /* 098D-098E */ + { 0x000991, 0x000992 }, /* 0991-0992 */ + { 0x0009A9, 0x0009A9 }, /* 09A9 */ + { 0x0009B1, 0x0009B1 }, /* 09B1 */ + { 0x0009B3, 0x0009B5 }, /* 09B3-09B5 */ + { 0x0009BA, 0x0009BB }, /* 09BA-09BB */ + { 0x0009BD, 0x0009BD }, /* 09BD */ + { 0x0009C5, 0x0009C6 }, /* 09C5-09C6 */ + { 0x0009C9, 0x0009CA }, /* 09C9-09CA */ + { 0x0009CE, 0x0009D6 }, /* 09CE-09D6 */ + { 0x0009D8, 0x0009DB }, /* 09D8-09DB */ + { 0x0009DE, 0x0009DE }, /* 09DE */ + { 0x0009E4, 0x0009E5 }, /* 09E4-09E5 */ + { 0x0009FB, 0x000A01 }, /* 09FB-0A01 */ + { 0x000A03, 0x000A04 }, /* 0A03-0A04 */ + { 0x000A0B, 0x000A0E }, /* 0A0B-0A0E */ + { 0x000A11, 0x000A12 }, /* 0A11-0A12 */ + { 0x000A29, 0x000A29 }, /* 0A29 */ + { 0x000A31, 0x000A31 }, /* 0A31 */ + { 0x000A34, 0x000A34 }, /* 0A34 */ + { 0x000A37, 0x000A37 }, /* 0A37 */ + { 0x000A3A, 0x000A3B }, /* 0A3A-0A3B */ + { 0x000A3D, 0x000A3D }, /* 0A3D */ + { 0x000A43, 0x000A46 }, /* 0A43-0A46 */ + { 0x000A49, 0x000A4A }, /* 0A49-0A4A */ + { 0x000A4E, 0x000A58 }, /* 0A4E-0A58 */ + { 0x000A5D, 0x000A5D }, /* 0A5D */ + { 0x000A5F, 0x000A65 }, /* 0A5F-0A65 */ + { 0x000A75, 0x000A80 }, /* 0A75-0A80 */ + { 0x000A84, 0x000A84 }, /* 0A84 */ + { 0x000A8C, 0x000A8C }, /* 0A8C */ + { 0x000A8E, 0x000A8E }, /* 0A8E */ + { 0x000A92, 0x000A92 }, /* 0A92 */ + { 0x000AA9, 0x000AA9 }, /* 0AA9 */ + { 0x000AB1, 0x000AB1 }, /* 0AB1 */ + { 0x000AB4, 0x000AB4 }, /* 0AB4 */ + { 0x000ABA, 0x000ABB }, /* 0ABA-0ABB */ + { 0x000AC6, 0x000AC6 }, /* 0AC6 */ + { 0x000ACA, 0x000ACA }, /* 0ACA */ + { 0x000ACE, 0x000ACF }, /* 0ACE-0ACF */ + { 0x000AD1, 0x000ADF }, /* 0AD1-0ADF */ + { 0x000AE1, 0x000AE5 }, /* 0AE1-0AE5 */ + { 0x000AF0, 0x000B00 }, /* 0AF0-0B00 */ + { 0x000B04, 0x000B04 }, /* 0B04 */ + { 0x000B0D, 0x000B0E }, /* 0B0D-0B0E */ + { 0x000B11, 0x000B12 }, /* 0B11-0B12 */ + { 0x000B29, 0x000B29 }, /* 0B29 */ + { 0x000B31, 0x000B31 }, /* 0B31 */ + { 0x000B34, 0x000B35 }, /* 0B34-0B35 */ + { 0x000B3A, 0x000B3B }, /* 0B3A-0B3B */ + { 0x000B44, 0x000B46 }, /* 0B44-0B46 */ + { 0x000B49, 0x000B4A }, /* 0B49-0B4A */ + { 0x000B4E, 0x000B55 }, /* 0B4E-0B55 */ + { 0x000B58, 0x000B5B }, /* 0B58-0B5B */ + { 0x000B5E, 0x000B5E }, /* 0B5E */ + { 0x000B62, 0x000B65 }, /* 0B62-0B65 */ + { 0x000B71, 0x000B81 }, /* 0B71-0B81 */ + { 0x000B84, 0x000B84 }, /* 0B84 */ + { 0x000B8B, 0x000B8D }, /* 0B8B-0B8D */ + { 0x000B91, 0x000B91 }, /* 0B91 */ + { 0x000B96, 0x000B98 }, /* 0B96-0B98 */ + { 0x000B9B, 0x000B9B }, /* 0B9B */ + { 0x000B9D, 0x000B9D }, /* 0B9D */ + { 0x000BA0, 0x000BA2 }, /* 0BA0-0BA2 */ + { 0x000BA5, 0x000BA7 }, /* 0BA5-0BA7 */ + { 0x000BAB, 0x000BAD }, /* 0BAB-0BAD */ + { 0x000BB6, 0x000BB6 }, /* 0BB6 */ + { 0x000BBA, 0x000BBD }, /* 0BBA-0BBD */ + { 0x000BC3, 0x000BC5 }, /* 0BC3-0BC5 */ + { 0x000BC9, 0x000BC9 }, /* 0BC9 */ + { 0x000BCE, 0x000BD6 }, /* 0BCE-0BD6 */ + { 0x000BD8, 0x000BE6 }, /* 0BD8-0BE6 */ + { 0x000BF3, 0x000C00 }, /* 0BF3-0C00 */ + { 0x000C04, 0x000C04 }, /* 0C04 */ + { 0x000C0D, 0x000C0D }, /* 0C0D */ + { 0x000C11, 0x000C11 }, /* 0C11 */ + { 0x000C29, 0x000C29 }, /* 0C29 */ + { 0x000C34, 0x000C34 }, /* 0C34 */ + { 0x000C3A, 0x000C3D }, /* 0C3A-0C3D */ + { 0x000C45, 0x000C45 }, /* 0C45 */ + { 0x000C49, 0x000C49 }, /* 0C49 */ + { 0x000C4E, 0x000C54 }, /* 0C4E-0C54 */ + { 0x000C57, 0x000C5F }, /* 0C57-0C5F */ + { 0x000C62, 0x000C65 }, /* 0C62-0C65 */ + { 0x000C70, 0x000C81 }, /* 0C70-0C81 */ + { 0x000C84, 0x000C84 }, /* 0C84 */ + { 0x000C8D, 0x000C8D }, /* 0C8D */ + { 0x000C91, 0x000C91 }, /* 0C91 */ + { 0x000CA9, 0x000CA9 }, /* 0CA9 */ + { 0x000CB4, 0x000CB4 }, /* 0CB4 */ + { 0x000CBA, 0x000CBD }, /* 0CBA-0CBD */ + { 0x000CC5, 0x000CC5 }, /* 0CC5 */ + { 0x000CC9, 0x000CC9 }, /* 0CC9 */ + { 0x000CCE, 0x000CD4 }, /* 0CCE-0CD4 */ + { 0x000CD7, 0x000CDD }, /* 0CD7-0CDD */ + { 0x000CDF, 0x000CDF }, /* 0CDF */ + { 0x000CE2, 0x000CE5 }, /* 0CE2-0CE5 */ + { 0x000CF0, 0x000D01 }, /* 0CF0-0D01 */ + { 0x000D04, 0x000D04 }, /* 0D04 */ + { 0x000D0D, 0x000D0D }, /* 0D0D */ + { 0x000D11, 0x000D11 }, /* 0D11 */ + { 0x000D29, 0x000D29 }, /* 0D29 */ + { 0x000D3A, 0x000D3D }, /* 0D3A-0D3D */ + { 0x000D44, 0x000D45 }, /* 0D44-0D45 */ + { 0x000D49, 0x000D49 }, /* 0D49 */ + { 0x000D4E, 0x000D56 }, /* 0D4E-0D56 */ + { 0x000D58, 0x000D5F }, /* 0D58-0D5F */ + { 0x000D62, 0x000D65 }, /* 0D62-0D65 */ + { 0x000D70, 0x000D81 }, /* 0D70-0D81 */ + { 0x000D84, 0x000D84 }, /* 0D84 */ + { 0x000D97, 0x000D99 }, /* 0D97-0D99 */ + { 0x000DB2, 0x000DB2 }, /* 0DB2 */ + { 0x000DBC, 0x000DBC }, /* 0DBC */ + { 0x000DBE, 0x000DBF }, /* 0DBE-0DBF */ + { 0x000DC7, 0x000DC9 }, /* 0DC7-0DC9 */ + { 0x000DCB, 0x000DCE }, /* 0DCB-0DCE */ + { 0x000DD5, 0x000DD5 }, /* 0DD5 */ + { 0x000DD7, 0x000DD7 }, /* 0DD7 */ + { 0x000DE0, 0x000DF1 }, /* 0DE0-0DF1 */ + { 0x000DF5, 0x000E00 }, /* 0DF5-0E00 */ + { 0x000E3B, 0x000E3E }, /* 0E3B-0E3E */ + { 0x000E5C, 0x000E80 }, /* 0E5C-0E80 */ + { 0x000E83, 0x000E83 }, /* 0E83 */ + { 0x000E85, 0x000E86 }, /* 0E85-0E86 */ + { 0x000E89, 0x000E89 }, /* 0E89 */ + { 0x000E8B, 0x000E8C }, /* 0E8B-0E8C */ + { 0x000E8E, 0x000E93 }, /* 0E8E-0E93 */ + { 0x000E98, 0x000E98 }, /* 0E98 */ + { 0x000EA0, 0x000EA0 }, /* 0EA0 */ + { 0x000EA4, 0x000EA4 }, /* 0EA4 */ + { 0x000EA6, 0x000EA6 }, /* 0EA6 */ + { 0x000EA8, 0x000EA9 }, /* 0EA8-0EA9 */ + { 0x000EAC, 0x000EAC }, /* 0EAC */ + { 0x000EBA, 0x000EBA }, /* 0EBA */ + { 0x000EBE, 0x000EBF }, /* 0EBE-0EBF */ + { 0x000EC5, 0x000EC5 }, /* 0EC5 */ + { 0x000EC7, 0x000EC7 }, /* 0EC7 */ + { 0x000ECE, 0x000ECF }, /* 0ECE-0ECF */ + { 0x000EDA, 0x000EDB }, /* 0EDA-0EDB */ + { 0x000EDE, 0x000EFF }, /* 0EDE-0EFF */ + { 0x000F48, 0x000F48 }, /* 0F48 */ + { 0x000F6B, 0x000F70 }, /* 0F6B-0F70 */ + { 0x000F8C, 0x000F8F }, /* 0F8C-0F8F */ + { 0x000F98, 0x000F98 }, /* 0F98 */ + { 0x000FBD, 0x000FBD }, /* 0FBD */ + { 0x000FCD, 0x000FCE }, /* 0FCD-0FCE */ + { 0x000FD0, 0x000FFF }, /* 0FD0-0FFF */ + { 0x001022, 0x001022 }, /* 1022 */ + { 0x001028, 0x001028 }, /* 1028 */ + { 0x00102B, 0x00102B }, /* 102B */ + { 0x001033, 0x001035 }, /* 1033-1035 */ + { 0x00103A, 0x00103F }, /* 103A-103F */ + { 0x00105A, 0x00109F }, /* 105A-109F */ + { 0x0010C6, 0x0010CF }, /* 10C6-10CF */ + { 0x0010F9, 0x0010FA }, /* 10F9-10FA */ + { 0x0010FC, 0x0010FF }, /* 10FC-10FF */ + { 0x00115A, 0x00115E }, /* 115A-115E */ + { 0x0011A3, 0x0011A7 }, /* 11A3-11A7 */ + { 0x0011FA, 0x0011FF }, /* 11FA-11FF */ + { 0x001207, 0x001207 }, /* 1207 */ + { 0x001247, 0x001247 }, /* 1247 */ + { 0x001249, 0x001249 }, /* 1249 */ + { 0x00124E, 0x00124F }, /* 124E-124F */ + { 0x001257, 0x001257 }, /* 1257 */ + { 0x001259, 0x001259 }, /* 1259 */ + { 0x00125E, 0x00125F }, /* 125E-125F */ + { 0x001287, 0x001287 }, /* 1287 */ + { 0x001289, 0x001289 }, /* 1289 */ + { 0x00128E, 0x00128F }, /* 128E-128F */ + { 0x0012AF, 0x0012AF }, /* 12AF */ + { 0x0012B1, 0x0012B1 }, /* 12B1 */ + { 0x0012B6, 0x0012B7 }, /* 12B6-12B7 */ + { 0x0012BF, 0x0012BF }, /* 12BF */ + { 0x0012C1, 0x0012C1 }, /* 12C1 */ + { 0x0012C6, 0x0012C7 }, /* 12C6-12C7 */ + { 0x0012CF, 0x0012CF }, /* 12CF */ + { 0x0012D7, 0x0012D7 }, /* 12D7 */ + { 0x0012EF, 0x0012EF }, /* 12EF */ + { 0x00130F, 0x00130F }, /* 130F */ + { 0x001311, 0x001311 }, /* 1311 */ + { 0x001316, 0x001317 }, /* 1316-1317 */ + { 0x00131F, 0x00131F }, /* 131F */ + { 0x001347, 0x001347 }, /* 1347 */ + { 0x00135B, 0x001360 }, /* 135B-1360 */ + { 0x00137D, 0x00139F }, /* 137D-139F */ + { 0x0013F5, 0x001400 }, /* 13F5-1400 */ + { 0x001677, 0x00167F }, /* 1677-167F */ + { 0x00169D, 0x00169F }, /* 169D-169F */ + { 0x0016F1, 0x0016FF }, /* 16F1-16FF */ + { 0x00170D, 0x00170D }, /* 170D */ + { 0x001715, 0x00171F }, /* 1715-171F */ + { 0x001737, 0x00173F }, /* 1737-173F */ + { 0x001754, 0x00175F }, /* 1754-175F */ + { 0x00176D, 0x00176D }, /* 176D */ + { 0x001771, 0x001771 }, /* 1771 */ + { 0x001774, 0x00177F }, /* 1774-177F */ + { 0x0017DD, 0x0017DF }, /* 17DD-17DF */ + { 0x0017EA, 0x0017FF }, /* 17EA-17FF */ + { 0x00180F, 0x00180F }, /* 180F */ + { 0x00181A, 0x00181F }, /* 181A-181F */ + { 0x001878, 0x00187F }, /* 1878-187F */ + { 0x0018AA, 0x001DFF }, /* 18AA-1DFF */ + { 0x001E9C, 0x001E9F }, /* 1E9C-1E9F */ + { 0x001EFA, 0x001EFF }, /* 1EFA-1EFF */ + { 0x001F16, 0x001F17 }, /* 1F16-1F17 */ + { 0x001F1E, 0x001F1F }, /* 1F1E-1F1F */ + { 0x001F46, 0x001F47 }, /* 1F46-1F47 */ + { 0x001F4E, 0x001F4F }, /* 1F4E-1F4F */ + { 0x001F58, 0x001F58 }, /* 1F58 */ + { 0x001F5A, 0x001F5A }, /* 1F5A */ + { 0x001F5C, 0x001F5C }, /* 1F5C */ + { 0x001F5E, 0x001F5E }, /* 1F5E */ + { 0x001F7E, 0x001F7F }, /* 1F7E-1F7F */ + { 0x001FB5, 0x001FB5 }, /* 1FB5 */ + { 0x001FC5, 0x001FC5 }, /* 1FC5 */ + { 0x001FD4, 0x001FD5 }, /* 1FD4-1FD5 */ + { 0x001FDC, 0x001FDC }, /* 1FDC */ + { 0x001FF0, 0x001FF1 }, /* 1FF0-1FF1 */ + { 0x001FF5, 0x001FF5 }, /* 1FF5 */ + { 0x001FFF, 0x001FFF }, /* 1FFF */ + { 0x002053, 0x002056 }, /* 2053-2056 */ + { 0x002058, 0x00205E }, /* 2058-205E */ + { 0x002064, 0x002069 }, /* 2064-2069 */ + { 0x002072, 0x002073 }, /* 2072-2073 */ + { 0x00208F, 0x00209F }, /* 208F-209F */ + { 0x0020B2, 0x0020CF }, /* 20B2-20CF */ + { 0x0020EB, 0x0020FF }, /* 20EB-20FF */ + { 0x00213B, 0x00213C }, /* 213B-213C */ + { 0x00214C, 0x002152 }, /* 214C-2152 */ + { 0x002184, 0x00218F }, /* 2184-218F */ + { 0x0023CF, 0x0023FF }, /* 23CF-23FF */ + { 0x002427, 0x00243F }, /* 2427-243F */ + { 0x00244B, 0x00245F }, /* 244B-245F */ + { 0x0024FF, 0x0024FF }, /* 24FF */ + { 0x002614, 0x002615 }, /* 2614-2615 */ + { 0x002618, 0x002618 }, /* 2618 */ + { 0x00267E, 0x00267F }, /* 267E-267F */ + { 0x00268A, 0x002700 }, /* 268A-2700 */ + { 0x002705, 0x002705 }, /* 2705 */ + { 0x00270A, 0x00270B }, /* 270A-270B */ + { 0x002728, 0x002728 }, /* 2728 */ + { 0x00274C, 0x00274C }, /* 274C */ + { 0x00274E, 0x00274E }, /* 274E */ + { 0x002753, 0x002755 }, /* 2753-2755 */ + { 0x002757, 0x002757 }, /* 2757 */ + { 0x00275F, 0x002760 }, /* 275F-2760 */ + { 0x002795, 0x002797 }, /* 2795-2797 */ + { 0x0027B0, 0x0027B0 }, /* 27B0 */ + { 0x0027BF, 0x0027CF }, /* 27BF-27CF */ + { 0x0027EC, 0x0027EF }, /* 27EC-27EF */ + { 0x002B00, 0x002E7F }, /* 2B00-2E7F */ + { 0x002E9A, 0x002E9A }, /* 2E9A */ + { 0x002EF4, 0x002EFF }, /* 2EF4-2EFF */ + { 0x002FD6, 0x002FEF }, /* 2FD6-2FEF */ + { 0x002FFC, 0x002FFF }, /* 2FFC-2FFF */ + { 0x003040, 0x003040 }, /* 3040 */ + { 0x003097, 0x003098 }, /* 3097-3098 */ + { 0x003100, 0x003104 }, /* 3100-3104 */ + { 0x00312D, 0x003130 }, /* 312D-3130 */ + { 0x00318F, 0x00318F }, /* 318F */ + { 0x0031B8, 0x0031EF }, /* 31B8-31EF */ + { 0x00321D, 0x00321F }, /* 321D-321F */ + { 0x003244, 0x003250 }, /* 3244-3250 */ + { 0x00327C, 0x00327E }, /* 327C-327E */ + { 0x0032CC, 0x0032CF }, /* 32CC-32CF */ + { 0x0032FF, 0x0032FF }, /* 32FF */ + { 0x003377, 0x00337A }, /* 3377-337A */ + { 0x0033DE, 0x0033DF }, /* 33DE-33DF */ + { 0x0033FF, 0x0033FF }, /* 33FF */ + { 0x004DB6, 0x004DFF }, /* 4DB6-4DFF */ + { 0x009FA6, 0x009FFF }, /* 9FA6-9FFF */ + { 0x00A48D, 0x00A48F }, /* A48D-A48F */ + { 0x00A4C7, 0x00ABFF }, /* A4C7-ABFF */ + { 0x00D7A4, 0x00D7FF }, /* D7A4-D7FF */ + { 0x00FA2E, 0x00FA2F }, /* FA2E-FA2F */ + { 0x00FA6B, 0x00FAFF }, /* FA6B-FAFF */ + { 0x00FB07, 0x00FB12 }, /* FB07-FB12 */ + { 0x00FB18, 0x00FB1C }, /* FB18-FB1C */ + { 0x00FB37, 0x00FB37 }, /* FB37 */ + { 0x00FB3D, 0x00FB3D }, /* FB3D */ + { 0x00FB3F, 0x00FB3F }, /* FB3F */ + { 0x00FB42, 0x00FB42 }, /* FB42 */ + { 0x00FB45, 0x00FB45 }, /* FB45 */ + { 0x00FBB2, 0x00FBD2 }, /* FBB2-FBD2 */ + { 0x00FD40, 0x00FD4F }, /* FD40-FD4F */ + { 0x00FD90, 0x00FD91 }, /* FD90-FD91 */ + { 0x00FDC8, 0x00FDCF }, /* FDC8-FDCF */ + { 0x00FDFD, 0x00FDFF }, /* FDFD-FDFF */ + { 0x00FE10, 0x00FE1F }, /* FE10-FE1F */ + { 0x00FE24, 0x00FE2F }, /* FE24-FE2F */ + { 0x00FE47, 0x00FE48 }, /* FE47-FE48 */ + { 0x00FE53, 0x00FE53 }, /* FE53 */ + { 0x00FE67, 0x00FE67 }, /* FE67 */ + { 0x00FE6C, 0x00FE6F }, /* FE6C-FE6F */ + { 0x00FE75, 0x00FE75 }, /* FE75 */ + { 0x00FEFD, 0x00FEFE }, /* FEFD-FEFE */ + { 0x00FF00, 0x00FF00 }, /* FF00 */ + { 0x00FFBF, 0x00FFC1 }, /* FFBF-FFC1 */ + { 0x00FFC8, 0x00FFC9 }, /* FFC8-FFC9 */ + { 0x00FFD0, 0x00FFD1 }, /* FFD0-FFD1 */ + { 0x00FFD8, 0x00FFD9 }, /* FFD8-FFD9 */ + { 0x00FFDD, 0x00FFDF }, /* FFDD-FFDF */ + { 0x00FFE7, 0x00FFE7 }, /* FFE7 */ + { 0x00FFEF, 0x00FFF8 }, /* FFEF-FFF8 */ + { 0x010000, 0x0102FF }, /* 10000-102FF */ + { 0x01031F, 0x01031F }, /* 1031F */ + { 0x010324, 0x01032F }, /* 10324-1032F */ + { 0x01034B, 0x0103FF }, /* 1034B-103FF */ + { 0x010426, 0x010427 }, /* 10426-10427 */ + { 0x01044E, 0x01CFFF }, /* 1044E-1CFFF */ + { 0x01D0F6, 0x01D0FF }, /* 1D0F6-1D0FF */ + { 0x01D127, 0x01D129 }, /* 1D127-1D129 */ + { 0x01D1DE, 0x01D3FF }, /* 1D1DE-1D3FF */ + { 0x01D455, 0x01D455 }, /* 1D455 */ + { 0x01D49D, 0x01D49D }, /* 1D49D */ + { 0x01D4A0, 0x01D4A1 }, /* 1D4A0-1D4A1 */ + { 0x01D4A3, 0x01D4A4 }, /* 1D4A3-1D4A4 */ + { 0x01D4A7, 0x01D4A8 }, /* 1D4A7-1D4A8 */ + { 0x01D4AD, 0x01D4AD }, /* 1D4AD */ + { 0x01D4BA, 0x01D4BA }, /* 1D4BA */ + { 0x01D4BC, 0x01D4BC }, /* 1D4BC */ + { 0x01D4C1, 0x01D4C1 }, /* 1D4C1 */ + { 0x01D4C4, 0x01D4C4 }, /* 1D4C4 */ + { 0x01D506, 0x01D506 }, /* 1D506 */ + { 0x01D50B, 0x01D50C }, /* 1D50B-1D50C */ + { 0x01D515, 0x01D515 }, /* 1D515 */ + { 0x01D51D, 0x01D51D }, /* 1D51D */ + { 0x01D53A, 0x01D53A }, /* 1D53A */ + { 0x01D53F, 0x01D53F }, /* 1D53F */ + { 0x01D545, 0x01D545 }, /* 1D545 */ + { 0x01D547, 0x01D549 }, /* 1D547-1D549 */ + { 0x01D551, 0x01D551 }, /* 1D551 */ + { 0x01D6A4, 0x01D6A7 }, /* 1D6A4-1D6A7 */ + { 0x01D7CA, 0x01D7CD }, /* 1D7CA-1D7CD */ + { 0x01D800, 0x01FFFD }, /* 1D800-1FFFD */ + { 0x02A6D7, 0x02F7FF }, /* 2A6D7-2F7FF */ + { 0x02FA1E, 0x02FFFD }, /* 2FA1E-2FFFD */ + { 0x030000, 0x03FFFD }, /* 30000-3FFFD */ + { 0x040000, 0x04FFFD }, /* 40000-4FFFD */ + { 0x050000, 0x05FFFD }, /* 50000-5FFFD */ + { 0x060000, 0x06FFFD }, /* 60000-6FFFD */ + { 0x070000, 0x07FFFD }, /* 70000-7FFFD */ + { 0x080000, 0x08FFFD }, /* 80000-8FFFD */ + { 0x090000, 0x09FFFD }, /* 90000-9FFFD */ + { 0x0A0000, 0x0AFFFD }, /* A0000-AFFFD */ + { 0x0B0000, 0x0BFFFD }, /* B0000-BFFFD */ + { 0x0C0000, 0x0CFFFD }, /* C0000-CFFFD */ + { 0x0D0000, 0x0DFFFD }, /* D0000-DFFFD */ + { 0x0E0000, 0x0E0000 }, /* E0000 */ + { 0x0E0002, 0x0E001F }, /* E0002-E001F */ + { 0x0E0080, 0x0EFFFD }, /* E0080-EFFFD */ + { 0 }, +}; + +/* + * E0080-EFFFD + * + */ + +const Stringprep_table_element stringprep_rfc3454_B_1[] = { + { 0x0000AD, 0x0000AD }, /* 00AD; ; Map to nothing */ + { 0x00034F, 0x00034F }, /* 034F; ; Map to nothing */ + { 0x001806, 0x001806 }, /* 1806; ; Map to nothing */ + { 0x00180B, 0x00180B }, /* 180B; ; Map to nothing */ + { 0x00180C, 0x00180C }, /* 180C; ; Map to nothing */ + { 0x00180D, 0x00180D }, /* 180D; ; Map to nothing */ + { 0x00200B, 0x00200B }, /* 200B; ; Map to nothing */ + { 0x00200C, 0x00200C }, /* 200C; ; Map to nothing */ + { 0x00200D, 0x00200D }, /* 200D; ; Map to nothing */ + { 0x002060, 0x002060 }, /* 2060; ; Map to nothing */ + { 0x00FE00, 0x00FE00 }, /* FE00; ; Map to nothing */ + { 0x00FE01, 0x00FE01 }, /* FE01; ; Map to nothing */ + { 0x00FE02, 0x00FE02 }, /* FE02; ; Map to nothing */ + { 0x00FE03, 0x00FE03 }, /* FE03; ; Map to nothing */ + { 0x00FE04, 0x00FE04 }, /* FE04; ; Map to nothing */ + { 0x00FE05, 0x00FE05 }, /* FE05; ; Map to nothing */ + { 0x00FE06, 0x00FE06 }, /* FE06; ; Map to nothing */ + { 0x00FE07, 0x00FE07 }, /* FE07; ; Map to nothing */ + { 0x00FE08, 0x00FE08 }, /* FE08; ; Map to nothing */ + { 0x00FE09, 0x00FE09 }, /* FE09; ; Map to nothing */ + { 0x00FE0A, 0x00FE0A }, /* FE0A; ; Map to nothing */ + { 0x00FE0B, 0x00FE0B }, /* FE0B; ; Map to nothing */ + { 0x00FE0C, 0x00FE0C }, /* FE0C; ; Map to nothing */ + { 0x00FE0D, 0x00FE0D }, /* FE0D; ; Map to nothing */ + { 0x00FE0E, 0x00FE0E }, /* FE0E; ; Map to nothing */ + { 0x00FE0F, 0x00FE0F }, /* FE0F; ; Map to nothing */ + { 0x00FEFF, 0x00FEFF }, /* FEFF; ; Map to nothing */ + { 0 }, +}; + +/* + * FEFF; ; Map to nothing + * + */ + +const Stringprep_table_element stringprep_rfc3454_B_2[] = { + { 0x000041, 0x000041, { 0x000061 } }, /* 0041; 0061; Case map */ + { 0x000042, 0x000042, { 0x000062 } }, /* 0042; 0062; Case map */ + { 0x000043, 0x000043, { 0x000063 } }, /* 0043; 0063; Case map */ + { 0x000044, 0x000044, { 0x000064 } }, /* 0044; 0064; Case map */ + { 0x000045, 0x000045, { 0x000065 } }, /* 0045; 0065; Case map */ + { 0x000046, 0x000046, { 0x000066 } }, /* 0046; 0066; Case map */ + { 0x000047, 0x000047, { 0x000067 } }, /* 0047; 0067; Case map */ + { 0x000048, 0x000048, { 0x000068 } }, /* 0048; 0068; Case map */ + { 0x000049, 0x000049, { 0x000069 } }, /* 0049; 0069; Case map */ + { 0x00004A, 0x00004A, { 0x00006A } }, /* 004A; 006A; Case map */ + { 0x00004B, 0x00004B, { 0x00006B } }, /* 004B; 006B; Case map */ + { 0x00004C, 0x00004C, { 0x00006C } }, /* 004C; 006C; Case map */ + { 0x00004D, 0x00004D, { 0x00006D } }, /* 004D; 006D; Case map */ + { 0x00004E, 0x00004E, { 0x00006E } }, /* 004E; 006E; Case map */ + { 0x00004F, 0x00004F, { 0x00006F } }, /* 004F; 006F; Case map */ + { 0x000050, 0x000050, { 0x000070 } }, /* 0050; 0070; Case map */ + { 0x000051, 0x000051, { 0x000071 } }, /* 0051; 0071; Case map */ + { 0x000052, 0x000052, { 0x000072 } }, /* 0052; 0072; Case map */ + { 0x000053, 0x000053, { 0x000073 } }, /* 0053; 0073; Case map */ + { 0x000054, 0x000054, { 0x000074 } }, /* 0054; 0074; Case map */ + { 0x000055, 0x000055, { 0x000075 } }, /* 0055; 0075; Case map */ + { 0x000056, 0x000056, { 0x000076 } }, /* 0056; 0076; Case map */ + { 0x000057, 0x000057, { 0x000077 } }, /* 0057; 0077; Case map */ + { 0x000058, 0x000058, { 0x000078 } }, /* 0058; 0078; Case map */ + { 0x000059, 0x000059, { 0x000079 } }, /* 0059; 0079; Case map */ + { 0x00005A, 0x00005A, { 0x00007A } }, /* 005A; 007A; Case map */ + { 0x0000B5, 0x0000B5, { 0x0003BC } }, /* 00B5; 03BC; Case map */ + { 0x0000C0, 0x0000C0, { 0x0000E0 } }, /* 00C0; 00E0; Case map */ + { 0x0000C1, 0x0000C1, { 0x0000E1 } }, /* 00C1; 00E1; Case map */ + { 0x0000C2, 0x0000C2, { 0x0000E2 } }, /* 00C2; 00E2; Case map */ + { 0x0000C3, 0x0000C3, { 0x0000E3 } }, /* 00C3; 00E3; Case map */ + { 0x0000C4, 0x0000C4, { 0x0000E4 } }, /* 00C4; 00E4; Case map */ + { 0x0000C5, 0x0000C5, { 0x0000E5 } }, /* 00C5; 00E5; Case map */ + { 0x0000C6, 0x0000C6, { 0x0000E6 } }, /* 00C6; 00E6; Case map */ + { 0x0000C7, 0x0000C7, { 0x0000E7 } }, /* 00C7; 00E7; Case map */ + { 0x0000C8, 0x0000C8, { 0x0000E8 } }, /* 00C8; 00E8; Case map */ + { 0x0000C9, 0x0000C9, { 0x0000E9 } }, /* 00C9; 00E9; Case map */ + { 0x0000CA, 0x0000CA, { 0x0000EA } }, /* 00CA; 00EA; Case map */ + { 0x0000CB, 0x0000CB, { 0x0000EB } }, /* 00CB; 00EB; Case map */ + { 0x0000CC, 0x0000CC, { 0x0000EC } }, /* 00CC; 00EC; Case map */ + { 0x0000CD, 0x0000CD, { 0x0000ED } }, /* 00CD; 00ED; Case map */ + { 0x0000CE, 0x0000CE, { 0x0000EE } }, /* 00CE; 00EE; Case map */ + { 0x0000CF, 0x0000CF, { 0x0000EF } }, /* 00CF; 00EF; Case map */ + { 0x0000D0, 0x0000D0, { 0x0000F0 } }, /* 00D0; 00F0; Case map */ + { 0x0000D1, 0x0000D1, { 0x0000F1 } }, /* 00D1; 00F1; Case map */ + { 0x0000D2, 0x0000D2, { 0x0000F2 } }, /* 00D2; 00F2; Case map */ + { 0x0000D3, 0x0000D3, { 0x0000F3 } }, /* 00D3; 00F3; Case map */ + { 0x0000D4, 0x0000D4, { 0x0000F4 } }, /* 00D4; 00F4; Case map */ + { 0x0000D5, 0x0000D5, { 0x0000F5 } }, /* 00D5; 00F5; Case map */ + { 0x0000D6, 0x0000D6, { 0x0000F6 } }, /* 00D6; 00F6; Case map */ + { 0x0000D8, 0x0000D8, { 0x0000F8 } }, /* 00D8; 00F8; Case map */ + { 0x0000D9, 0x0000D9, { 0x0000F9 } }, /* 00D9; 00F9; Case map */ + { 0x0000DA, 0x0000DA, { 0x0000FA } }, /* 00DA; 00FA; Case map */ + { 0x0000DB, 0x0000DB, { 0x0000FB } }, /* 00DB; 00FB; Case map */ + { 0x0000DC, 0x0000DC, { 0x0000FC } }, /* 00DC; 00FC; Case map */ + { 0x0000DD, 0x0000DD, { 0x0000FD } }, /* 00DD; 00FD; Case map */ + { 0x0000DE, 0x0000DE, { 0x0000FE } }, /* 00DE; 00FE; Case map */ + { 0x0000DF, + 0x0000DF, + { 0x000073, /* 00DF; 0073 0073; Case map */ + 0x000073 } }, + { 0x000100, 0x000100, { 0x000101 } }, /* 0100; 0101; Case map */ + { 0x000102, 0x000102, { 0x000103 } }, /* 0102; 0103; Case map */ + { 0x000104, 0x000104, { 0x000105 } }, /* 0104; 0105; Case map */ + { 0x000106, 0x000106, { 0x000107 } }, /* 0106; 0107; Case map */ + { 0x000108, 0x000108, { 0x000109 } }, /* 0108; 0109; Case map */ + { 0x00010A, 0x00010A, { 0x00010B } }, /* 010A; 010B; Case map */ + { 0x00010C, 0x00010C, { 0x00010D } }, /* 010C; 010D; Case map */ + { 0x00010E, 0x00010E, { 0x00010F } }, /* 010E; 010F; Case map */ + { 0x000110, 0x000110, { 0x000111 } }, /* 0110; 0111; Case map */ + { 0x000112, 0x000112, { 0x000113 } }, /* 0112; 0113; Case map */ + { 0x000114, 0x000114, { 0x000115 } }, /* 0114; 0115; Case map */ + { 0x000116, 0x000116, { 0x000117 } }, /* 0116; 0117; Case map */ + { 0x000118, 0x000118, { 0x000119 } }, /* 0118; 0119; Case map */ + { 0x00011A, 0x00011A, { 0x00011B } }, /* 011A; 011B; Case map */ + { 0x00011C, 0x00011C, { 0x00011D } }, /* 011C; 011D; Case map */ + { 0x00011E, 0x00011E, { 0x00011F } }, /* 011E; 011F; Case map */ + { 0x000120, 0x000120, { 0x000121 } }, /* 0120; 0121; Case map */ + { 0x000122, 0x000122, { 0x000123 } }, /* 0122; 0123; Case map */ + { 0x000124, 0x000124, { 0x000125 } }, /* 0124; 0125; Case map */ + { 0x000126, 0x000126, { 0x000127 } }, /* 0126; 0127; Case map */ + { 0x000128, 0x000128, { 0x000129 } }, /* 0128; 0129; Case map */ + { 0x00012A, 0x00012A, { 0x00012B } }, /* 012A; 012B; Case map */ + { 0x00012C, 0x00012C, { 0x00012D } }, /* 012C; 012D; Case map */ + { 0x00012E, 0x00012E, { 0x00012F } }, /* 012E; 012F; Case map */ + { 0x000130, + 0x000130, + { 0x000069, /* 0130; 0069 0307; Case map */ + 0x000307 } }, + { 0x000132, 0x000132, { 0x000133 } }, /* 0132; 0133; Case map */ + { 0x000134, 0x000134, { 0x000135 } }, /* 0134; 0135; Case map */ + { 0x000136, 0x000136, { 0x000137 } }, /* 0136; 0137; Case map */ + { 0x000139, 0x000139, { 0x00013A } }, /* 0139; 013A; Case map */ + { 0x00013B, 0x00013B, { 0x00013C } }, /* 013B; 013C; Case map */ + { 0x00013D, 0x00013D, { 0x00013E } }, /* 013D; 013E; Case map */ + { 0x00013F, 0x00013F, { 0x000140 } }, /* 013F; 0140; Case map */ + { 0x000141, 0x000141, { 0x000142 } }, /* 0141; 0142; Case map */ + { 0x000143, 0x000143, { 0x000144 } }, /* 0143; 0144; Case map */ + { 0x000145, 0x000145, { 0x000146 } }, /* 0145; 0146; Case map */ + { 0x000147, 0x000147, { 0x000148 } }, /* 0147; 0148; Case map */ + { 0x000149, + 0x000149, + { 0x0002BC, /* 0149; 02BC 006E; Case map */ + 0x00006E } }, + { 0x00014A, 0x00014A, { 0x00014B } }, /* 014A; 014B; Case map */ + { 0x00014C, 0x00014C, { 0x00014D } }, /* 014C; 014D; Case map */ + { 0x00014E, 0x00014E, { 0x00014F } }, /* 014E; 014F; Case map */ + { 0x000150, 0x000150, { 0x000151 } }, /* 0150; 0151; Case map */ + { 0x000152, 0x000152, { 0x000153 } }, /* 0152; 0153; Case map */ + { 0x000154, 0x000154, { 0x000155 } }, /* 0154; 0155; Case map */ + { 0x000156, 0x000156, { 0x000157 } }, /* 0156; 0157; Case map */ + { 0x000158, 0x000158, { 0x000159 } }, /* 0158; 0159; Case map */ + { 0x00015A, 0x00015A, { 0x00015B } }, /* 015A; 015B; Case map */ + { 0x00015C, 0x00015C, { 0x00015D } }, /* 015C; 015D; Case map */ + { 0x00015E, 0x00015E, { 0x00015F } }, /* 015E; 015F; Case map */ + { 0x000160, 0x000160, { 0x000161 } }, /* 0160; 0161; Case map */ + { 0x000162, 0x000162, { 0x000163 } }, /* 0162; 0163; Case map */ + { 0x000164, 0x000164, { 0x000165 } }, /* 0164; 0165; Case map */ + { 0x000166, 0x000166, { 0x000167 } }, /* 0166; 0167; Case map */ + { 0x000168, 0x000168, { 0x000169 } }, /* 0168; 0169; Case map */ + { 0x00016A, 0x00016A, { 0x00016B } }, /* 016A; 016B; Case map */ + { 0x00016C, 0x00016C, { 0x00016D } }, /* 016C; 016D; Case map */ + { 0x00016E, 0x00016E, { 0x00016F } }, /* 016E; 016F; Case map */ + { 0x000170, 0x000170, { 0x000171 } }, /* 0170; 0171; Case map */ + { 0x000172, 0x000172, { 0x000173 } }, /* 0172; 0173; Case map */ + { 0x000174, 0x000174, { 0x000175 } }, /* 0174; 0175; Case map */ + { 0x000176, 0x000176, { 0x000177 } }, /* 0176; 0177; Case map */ + { 0x000178, 0x000178, { 0x0000FF } }, /* 0178; 00FF; Case map */ + { 0x000179, 0x000179, { 0x00017A } }, /* 0179; 017A; Case map */ + { 0x00017B, 0x00017B, { 0x00017C } }, /* 017B; 017C; Case map */ + { 0x00017D, 0x00017D, { 0x00017E } }, /* 017D; 017E; Case map */ + { 0x00017F, 0x00017F, { 0x000073 } }, /* 017F; 0073; Case map */ + { 0x000181, 0x000181, { 0x000253 } }, /* 0181; 0253; Case map */ + { 0x000182, 0x000182, { 0x000183 } }, /* 0182; 0183; Case map */ + { 0x000184, 0x000184, { 0x000185 } }, /* 0184; 0185; Case map */ + { 0x000186, 0x000186, { 0x000254 } }, /* 0186; 0254; Case map */ + { 0x000187, 0x000187, { 0x000188 } }, /* 0187; 0188; Case map */ + { 0x000189, 0x000189, { 0x000256 } }, /* 0189; 0256; Case map */ + { 0x00018A, 0x00018A, { 0x000257 } }, /* 018A; 0257; Case map */ + { 0x00018B, 0x00018B, { 0x00018C } }, /* 018B; 018C; Case map */ + { 0x00018E, 0x00018E, { 0x0001DD } }, /* 018E; 01DD; Case map */ + { 0x00018F, 0x00018F, { 0x000259 } }, /* 018F; 0259; Case map */ + { 0x000190, 0x000190, { 0x00025B } }, /* 0190; 025B; Case map */ + { 0x000191, 0x000191, { 0x000192 } }, /* 0191; 0192; Case map */ + { 0x000193, 0x000193, { 0x000260 } }, /* 0193; 0260; Case map */ + { 0x000194, 0x000194, { 0x000263 } }, /* 0194; 0263; Case map */ + { 0x000196, 0x000196, { 0x000269 } }, /* 0196; 0269; Case map */ + { 0x000197, 0x000197, { 0x000268 } }, /* 0197; 0268; Case map */ + { 0x000198, 0x000198, { 0x000199 } }, /* 0198; 0199; Case map */ + { 0x00019C, 0x00019C, { 0x00026F } }, /* 019C; 026F; Case map */ + { 0x00019D, 0x00019D, { 0x000272 } }, /* 019D; 0272; Case map */ + { 0x00019F, 0x00019F, { 0x000275 } }, /* 019F; 0275; Case map */ + { 0x0001A0, 0x0001A0, { 0x0001A1 } }, /* 01A0; 01A1; Case map */ + { 0x0001A2, 0x0001A2, { 0x0001A3 } }, /* 01A2; 01A3; Case map */ + { 0x0001A4, 0x0001A4, { 0x0001A5 } }, /* 01A4; 01A5; Case map */ + { 0x0001A6, 0x0001A6, { 0x000280 } }, /* 01A6; 0280; Case map */ + { 0x0001A7, 0x0001A7, { 0x0001A8 } }, /* 01A7; 01A8; Case map */ + { 0x0001A9, 0x0001A9, { 0x000283 } }, /* 01A9; 0283; Case map */ + { 0x0001AC, 0x0001AC, { 0x0001AD } }, /* 01AC; 01AD; Case map */ + { 0x0001AE, 0x0001AE, { 0x000288 } }, /* 01AE; 0288; Case map */ + { 0x0001AF, 0x0001AF, { 0x0001B0 } }, /* 01AF; 01B0; Case map */ + { 0x0001B1, 0x0001B1, { 0x00028A } }, /* 01B1; 028A; Case map */ + { 0x0001B2, 0x0001B2, { 0x00028B } }, /* 01B2; 028B; Case map */ + { 0x0001B3, 0x0001B3, { 0x0001B4 } }, /* 01B3; 01B4; Case map */ + { 0x0001B5, 0x0001B5, { 0x0001B6 } }, /* 01B5; 01B6; Case map */ + { 0x0001B7, 0x0001B7, { 0x000292 } }, /* 01B7; 0292; Case map */ + { 0x0001B8, 0x0001B8, { 0x0001B9 } }, /* 01B8; 01B9; Case map */ + { 0x0001BC, 0x0001BC, { 0x0001BD } }, /* 01BC; 01BD; Case map */ + { 0x0001C4, 0x0001C4, { 0x0001C6 } }, /* 01C4; 01C6; Case map */ + { 0x0001C5, 0x0001C5, { 0x0001C6 } }, /* 01C5; 01C6; Case map */ + { 0x0001C7, 0x0001C7, { 0x0001C9 } }, /* 01C7; 01C9; Case map */ + { 0x0001C8, 0x0001C8, { 0x0001C9 } }, /* 01C8; 01C9; Case map */ + { 0x0001CA, 0x0001CA, { 0x0001CC } }, /* 01CA; 01CC; Case map */ + { 0x0001CB, 0x0001CB, { 0x0001CC } }, /* 01CB; 01CC; Case map */ + { 0x0001CD, 0x0001CD, { 0x0001CE } }, /* 01CD; 01CE; Case map */ + { 0x0001CF, 0x0001CF, { 0x0001D0 } }, /* 01CF; 01D0; Case map */ + { 0x0001D1, 0x0001D1, { 0x0001D2 } }, /* 01D1; 01D2; Case map */ + { 0x0001D3, 0x0001D3, { 0x0001D4 } }, /* 01D3; 01D4; Case map */ + { 0x0001D5, 0x0001D5, { 0x0001D6 } }, /* 01D5; 01D6; Case map */ + { 0x0001D7, 0x0001D7, { 0x0001D8 } }, /* 01D7; 01D8; Case map */ + { 0x0001D9, 0x0001D9, { 0x0001DA } }, /* 01D9; 01DA; Case map */ + { 0x0001DB, 0x0001DB, { 0x0001DC } }, /* 01DB; 01DC; Case map */ + { 0x0001DE, 0x0001DE, { 0x0001DF } }, /* 01DE; 01DF; Case map */ + { 0x0001E0, 0x0001E0, { 0x0001E1 } }, /* 01E0; 01E1; Case map */ + { 0x0001E2, 0x0001E2, { 0x0001E3 } }, /* 01E2; 01E3; Case map */ + { 0x0001E4, 0x0001E4, { 0x0001E5 } }, /* 01E4; 01E5; Case map */ + { 0x0001E6, 0x0001E6, { 0x0001E7 } }, /* 01E6; 01E7; Case map */ + { 0x0001E8, 0x0001E8, { 0x0001E9 } }, /* 01E8; 01E9; Case map */ + { 0x0001EA, 0x0001EA, { 0x0001EB } }, /* 01EA; 01EB; Case map */ + { 0x0001EC, 0x0001EC, { 0x0001ED } }, /* 01EC; 01ED; Case map */ + { 0x0001EE, 0x0001EE, { 0x0001EF } }, /* 01EE; 01EF; Case map */ + { 0x0001F0, + 0x0001F0, + { 0x00006A, /* 01F0; 006A 030C; Case map */ + 0x00030C } }, + { 0x0001F1, 0x0001F1, { 0x0001F3 } }, /* 01F1; 01F3; Case map */ + { 0x0001F2, 0x0001F2, { 0x0001F3 } }, /* 01F2; 01F3; Case map */ + { 0x0001F4, 0x0001F4, { 0x0001F5 } }, /* 01F4; 01F5; Case map */ + { 0x0001F6, 0x0001F6, { 0x000195 } }, /* 01F6; 0195; Case map */ + { 0x0001F7, 0x0001F7, { 0x0001BF } }, /* 01F7; 01BF; Case map */ + { 0x0001F8, 0x0001F8, { 0x0001F9 } }, /* 01F8; 01F9; Case map */ + { 0x0001FA, 0x0001FA, { 0x0001FB } }, /* 01FA; 01FB; Case map */ + { 0x0001FC, 0x0001FC, { 0x0001FD } }, /* 01FC; 01FD; Case map */ + { 0x0001FE, 0x0001FE, { 0x0001FF } }, /* 01FE; 01FF; Case map */ + { 0x000200, 0x000200, { 0x000201 } }, /* 0200; 0201; Case map */ + { 0x000202, 0x000202, { 0x000203 } }, /* 0202; 0203; Case map */ + { 0x000204, 0x000204, { 0x000205 } }, /* 0204; 0205; Case map */ + { 0x000206, 0x000206, { 0x000207 } }, /* 0206; 0207; Case map */ + { 0x000208, 0x000208, { 0x000209 } }, /* 0208; 0209; Case map */ + { 0x00020A, 0x00020A, { 0x00020B } }, /* 020A; 020B; Case map */ + { 0x00020C, 0x00020C, { 0x00020D } }, /* 020C; 020D; Case map */ + { 0x00020E, 0x00020E, { 0x00020F } }, /* 020E; 020F; Case map */ + { 0x000210, 0x000210, { 0x000211 } }, /* 0210; 0211; Case map */ + { 0x000212, 0x000212, { 0x000213 } }, /* 0212; 0213; Case map */ + { 0x000214, 0x000214, { 0x000215 } }, /* 0214; 0215; Case map */ + { 0x000216, 0x000216, { 0x000217 } }, /* 0216; 0217; Case map */ + { 0x000218, 0x000218, { 0x000219 } }, /* 0218; 0219; Case map */ + { 0x00021A, 0x00021A, { 0x00021B } }, /* 021A; 021B; Case map */ + { 0x00021C, 0x00021C, { 0x00021D } }, /* 021C; 021D; Case map */ + { 0x00021E, 0x00021E, { 0x00021F } }, /* 021E; 021F; Case map */ + { 0x000220, 0x000220, { 0x00019E } }, /* 0220; 019E; Case map */ + { 0x000222, 0x000222, { 0x000223 } }, /* 0222; 0223; Case map */ + { 0x000224, 0x000224, { 0x000225 } }, /* 0224; 0225; Case map */ + { 0x000226, 0x000226, { 0x000227 } }, /* 0226; 0227; Case map */ + { 0x000228, 0x000228, { 0x000229 } }, /* 0228; 0229; Case map */ + { 0x00022A, 0x00022A, { 0x00022B } }, /* 022A; 022B; Case map */ + { 0x00022C, 0x00022C, { 0x00022D } }, /* 022C; 022D; Case map */ + { 0x00022E, 0x00022E, { 0x00022F } }, /* 022E; 022F; Case map */ + { 0x000230, 0x000230, { 0x000231 } }, /* 0230; 0231; Case map */ + { 0x000232, 0x000232, { 0x000233 } }, /* 0232; 0233; Case map */ + { 0x000345, 0x000345, { 0x0003B9 } }, /* 0345; 03B9; Case map */ + { 0x00037A, + 0x00037A, + { 0x000020, /* 037A; 0020 03B9; Additional folding */ + 0x0003B9 } }, + { 0x000386, 0x000386, { 0x0003AC } }, /* 0386; 03AC; Case map */ + { 0x000388, 0x000388, { 0x0003AD } }, /* 0388; 03AD; Case map */ + { 0x000389, 0x000389, { 0x0003AE } }, /* 0389; 03AE; Case map */ + { 0x00038A, 0x00038A, { 0x0003AF } }, /* 038A; 03AF; Case map */ + { 0x00038C, 0x00038C, { 0x0003CC } }, /* 038C; 03CC; Case map */ + { 0x00038E, 0x00038E, { 0x0003CD } }, /* 038E; 03CD; Case map */ + { 0x00038F, 0x00038F, { 0x0003CE } }, /* 038F; 03CE; Case map */ + { 0x000390, + 0x000390, + { 0x0003B9, /* 0390; 03B9 0308 0301; Case map */ + 0x000308, 0x000301 } }, + { 0x000391, 0x000391, { 0x0003B1 } }, /* 0391; 03B1; Case map */ + { 0x000392, 0x000392, { 0x0003B2 } }, /* 0392; 03B2; Case map */ + { 0x000393, 0x000393, { 0x0003B3 } }, /* 0393; 03B3; Case map */ + { 0x000394, 0x000394, { 0x0003B4 } }, /* 0394; 03B4; Case map */ + { 0x000395, 0x000395, { 0x0003B5 } }, /* 0395; 03B5; Case map */ + { 0x000396, 0x000396, { 0x0003B6 } }, /* 0396; 03B6; Case map */ + { 0x000397, 0x000397, { 0x0003B7 } }, /* 0397; 03B7; Case map */ + { 0x000398, 0x000398, { 0x0003B8 } }, /* 0398; 03B8; Case map */ + { 0x000399, 0x000399, { 0x0003B9 } }, /* 0399; 03B9; Case map */ + { 0x00039A, 0x00039A, { 0x0003BA } }, /* 039A; 03BA; Case map */ + { 0x00039B, 0x00039B, { 0x0003BB } }, /* 039B; 03BB; Case map */ + { 0x00039C, 0x00039C, { 0x0003BC } }, /* 039C; 03BC; Case map */ + { 0x00039D, 0x00039D, { 0x0003BD } }, /* 039D; 03BD; Case map */ + { 0x00039E, 0x00039E, { 0x0003BE } }, /* 039E; 03BE; Case map */ + { 0x00039F, 0x00039F, { 0x0003BF } }, /* 039F; 03BF; Case map */ + { 0x0003A0, 0x0003A0, { 0x0003C0 } }, /* 03A0; 03C0; Case map */ + { 0x0003A1, 0x0003A1, { 0x0003C1 } }, /* 03A1; 03C1; Case map */ + { 0x0003A3, 0x0003A3, { 0x0003C3 } }, /* 03A3; 03C3; Case map */ + { 0x0003A4, 0x0003A4, { 0x0003C4 } }, /* 03A4; 03C4; Case map */ + { 0x0003A5, 0x0003A5, { 0x0003C5 } }, /* 03A5; 03C5; Case map */ + { 0x0003A6, 0x0003A6, { 0x0003C6 } }, /* 03A6; 03C6; Case map */ + { 0x0003A7, 0x0003A7, { 0x0003C7 } }, /* 03A7; 03C7; Case map */ + { 0x0003A8, 0x0003A8, { 0x0003C8 } }, /* 03A8; 03C8; Case map */ + { 0x0003A9, 0x0003A9, { 0x0003C9 } }, /* 03A9; 03C9; Case map */ + { 0x0003AA, 0x0003AA, { 0x0003CA } }, /* 03AA; 03CA; Case map */ + { 0x0003AB, 0x0003AB, { 0x0003CB } }, /* 03AB; 03CB; Case map */ + { 0x0003B0, + 0x0003B0, + { 0x0003C5, /* 03B0; 03C5 0308 0301; Case map */ + 0x000308, 0x000301 } }, + { 0x0003C2, 0x0003C2, { 0x0003C3 } }, /* 03C2; 03C3; Case map */ + { 0x0003D0, 0x0003D0, { 0x0003B2 } }, /* 03D0; 03B2; Case map */ + { 0x0003D1, 0x0003D1, { 0x0003B8 } }, /* 03D1; 03B8; Case map */ + { 0x0003D2, 0x0003D2, { 0x0003C5 } }, /* 03D2; 03C5; Additional folding */ + { 0x0003D3, 0x0003D3, { 0x0003CD } }, /* 03D3; 03CD; Additional folding */ + { 0x0003D4, 0x0003D4, { 0x0003CB } }, /* 03D4; 03CB; Additional folding */ + { 0x0003D5, 0x0003D5, { 0x0003C6 } }, /* 03D5; 03C6; Case map */ + { 0x0003D6, 0x0003D6, { 0x0003C0 } }, /* 03D6; 03C0; Case map */ + { 0x0003D8, 0x0003D8, { 0x0003D9 } }, /* 03D8; 03D9; Case map */ + { 0x0003DA, 0x0003DA, { 0x0003DB } }, /* 03DA; 03DB; Case map */ + { 0x0003DC, 0x0003DC, { 0x0003DD } }, /* 03DC; 03DD; Case map */ + { 0x0003DE, 0x0003DE, { 0x0003DF } }, /* 03DE; 03DF; Case map */ + { 0x0003E0, 0x0003E0, { 0x0003E1 } }, /* 03E0; 03E1; Case map */ + { 0x0003E2, 0x0003E2, { 0x0003E3 } }, /* 03E2; 03E3; Case map */ + { 0x0003E4, 0x0003E4, { 0x0003E5 } }, /* 03E4; 03E5; Case map */ + { 0x0003E6, 0x0003E6, { 0x0003E7 } }, /* 03E6; 03E7; Case map */ + { 0x0003E8, 0x0003E8, { 0x0003E9 } }, /* 03E8; 03E9; Case map */ + { 0x0003EA, 0x0003EA, { 0x0003EB } }, /* 03EA; 03EB; Case map */ + { 0x0003EC, 0x0003EC, { 0x0003ED } }, /* 03EC; 03ED; Case map */ + { 0x0003EE, 0x0003EE, { 0x0003EF } }, /* 03EE; 03EF; Case map */ + { 0x0003F0, 0x0003F0, { 0x0003BA } }, /* 03F0; 03BA; Case map */ + { 0x0003F1, 0x0003F1, { 0x0003C1 } }, /* 03F1; 03C1; Case map */ + { 0x0003F2, 0x0003F2, { 0x0003C3 } }, /* 03F2; 03C3; Case map */ + { 0x0003F4, 0x0003F4, { 0x0003B8 } }, /* 03F4; 03B8; Case map */ + { 0x0003F5, 0x0003F5, { 0x0003B5 } }, /* 03F5; 03B5; Case map */ + { 0x000400, 0x000400, { 0x000450 } }, /* 0400; 0450; Case map */ + { 0x000401, 0x000401, { 0x000451 } }, /* 0401; 0451; Case map */ + { 0x000402, 0x000402, { 0x000452 } }, /* 0402; 0452; Case map */ + { 0x000403, 0x000403, { 0x000453 } }, /* 0403; 0453; Case map */ + { 0x000404, 0x000404, { 0x000454 } }, /* 0404; 0454; Case map */ + { 0x000405, 0x000405, { 0x000455 } }, /* 0405; 0455; Case map */ + { 0x000406, 0x000406, { 0x000456 } }, /* 0406; 0456; Case map */ + { 0x000407, 0x000407, { 0x000457 } }, /* 0407; 0457; Case map */ + { 0x000408, 0x000408, { 0x000458 } }, /* 0408; 0458; Case map */ + { 0x000409, 0x000409, { 0x000459 } }, /* 0409; 0459; Case map */ + { 0x00040A, 0x00040A, { 0x00045A } }, /* 040A; 045A; Case map */ + { 0x00040B, 0x00040B, { 0x00045B } }, /* 040B; 045B; Case map */ + { 0x00040C, 0x00040C, { 0x00045C } }, /* 040C; 045C; Case map */ + { 0x00040D, 0x00040D, { 0x00045D } }, /* 040D; 045D; Case map */ + { 0x00040E, 0x00040E, { 0x00045E } }, /* 040E; 045E; Case map */ + { 0x00040F, 0x00040F, { 0x00045F } }, /* 040F; 045F; Case map */ + { 0x000410, 0x000410, { 0x000430 } }, /* 0410; 0430; Case map */ + { 0x000411, 0x000411, { 0x000431 } }, /* 0411; 0431; Case map */ + { 0x000412, 0x000412, { 0x000432 } }, /* 0412; 0432; Case map */ + { 0x000413, 0x000413, { 0x000433 } }, /* 0413; 0433; Case map */ + { 0x000414, 0x000414, { 0x000434 } }, /* 0414; 0434; Case map */ + { 0x000415, 0x000415, { 0x000435 } }, /* 0415; 0435; Case map */ + { 0x000416, 0x000416, { 0x000436 } }, /* 0416; 0436; Case map */ + { 0x000417, 0x000417, { 0x000437 } }, /* 0417; 0437; Case map */ + { 0x000418, 0x000418, { 0x000438 } }, /* 0418; 0438; Case map */ + { 0x000419, 0x000419, { 0x000439 } }, /* 0419; 0439; Case map */ + { 0x00041A, 0x00041A, { 0x00043A } }, /* 041A; 043A; Case map */ + { 0x00041B, 0x00041B, { 0x00043B } }, /* 041B; 043B; Case map */ + { 0x00041C, 0x00041C, { 0x00043C } }, /* 041C; 043C; Case map */ + { 0x00041D, 0x00041D, { 0x00043D } }, /* 041D; 043D; Case map */ + { 0x00041E, 0x00041E, { 0x00043E } }, /* 041E; 043E; Case map */ + { 0x00041F, 0x00041F, { 0x00043F } }, /* 041F; 043F; Case map */ + { 0x000420, 0x000420, { 0x000440 } }, /* 0420; 0440; Case map */ + { 0x000421, 0x000421, { 0x000441 } }, /* 0421; 0441; Case map */ + { 0x000422, 0x000422, { 0x000442 } }, /* 0422; 0442; Case map */ + { 0x000423, 0x000423, { 0x000443 } }, /* 0423; 0443; Case map */ + { 0x000424, 0x000424, { 0x000444 } }, /* 0424; 0444; Case map */ + { 0x000425, 0x000425, { 0x000445 } }, /* 0425; 0445; Case map */ + { 0x000426, 0x000426, { 0x000446 } }, /* 0426; 0446; Case map */ + { 0x000427, 0x000427, { 0x000447 } }, /* 0427; 0447; Case map */ + { 0x000428, 0x000428, { 0x000448 } }, /* 0428; 0448; Case map */ + { 0x000429, 0x000429, { 0x000449 } }, /* 0429; 0449; Case map */ + { 0x00042A, 0x00042A, { 0x00044A } }, /* 042A; 044A; Case map */ + { 0x00042B, 0x00042B, { 0x00044B } }, /* 042B; 044B; Case map */ + { 0x00042C, 0x00042C, { 0x00044C } }, /* 042C; 044C; Case map */ + { 0x00042D, 0x00042D, { 0x00044D } }, /* 042D; 044D; Case map */ + { 0x00042E, 0x00042E, { 0x00044E } }, /* 042E; 044E; Case map */ + { 0x00042F, 0x00042F, { 0x00044F } }, /* 042F; 044F; Case map */ + { 0x000460, 0x000460, { 0x000461 } }, /* 0460; 0461; Case map */ + { 0x000462, 0x000462, { 0x000463 } }, /* 0462; 0463; Case map */ + { 0x000464, 0x000464, { 0x000465 } }, /* 0464; 0465; Case map */ + { 0x000466, 0x000466, { 0x000467 } }, /* 0466; 0467; Case map */ + { 0x000468, 0x000468, { 0x000469 } }, /* 0468; 0469; Case map */ + { 0x00046A, 0x00046A, { 0x00046B } }, /* 046A; 046B; Case map */ + { 0x00046C, 0x00046C, { 0x00046D } }, /* 046C; 046D; Case map */ + { 0x00046E, 0x00046E, { 0x00046F } }, /* 046E; 046F; Case map */ + { 0x000470, 0x000470, { 0x000471 } }, /* 0470; 0471; Case map */ + { 0x000472, 0x000472, { 0x000473 } }, /* 0472; 0473; Case map */ + { 0x000474, 0x000474, { 0x000475 } }, /* 0474; 0475; Case map */ + { 0x000476, 0x000476, { 0x000477 } }, /* 0476; 0477; Case map */ + { 0x000478, 0x000478, { 0x000479 } }, /* 0478; 0479; Case map */ + { 0x00047A, 0x00047A, { 0x00047B } }, /* 047A; 047B; Case map */ + { 0x00047C, 0x00047C, { 0x00047D } }, /* 047C; 047D; Case map */ + { 0x00047E, 0x00047E, { 0x00047F } }, /* 047E; 047F; Case map */ + { 0x000480, 0x000480, { 0x000481 } }, /* 0480; 0481; Case map */ + { 0x00048A, 0x00048A, { 0x00048B } }, /* 048A; 048B; Case map */ + { 0x00048C, 0x00048C, { 0x00048D } }, /* 048C; 048D; Case map */ + { 0x00048E, 0x00048E, { 0x00048F } }, /* 048E; 048F; Case map */ + { 0x000490, 0x000490, { 0x000491 } }, /* 0490; 0491; Case map */ + { 0x000492, 0x000492, { 0x000493 } }, /* 0492; 0493; Case map */ + { 0x000494, 0x000494, { 0x000495 } }, /* 0494; 0495; Case map */ + { 0x000496, 0x000496, { 0x000497 } }, /* 0496; 0497; Case map */ + { 0x000498, 0x000498, { 0x000499 } }, /* 0498; 0499; Case map */ + { 0x00049A, 0x00049A, { 0x00049B } }, /* 049A; 049B; Case map */ + { 0x00049C, 0x00049C, { 0x00049D } }, /* 049C; 049D; Case map */ + { 0x00049E, 0x00049E, { 0x00049F } }, /* 049E; 049F; Case map */ + { 0x0004A0, 0x0004A0, { 0x0004A1 } }, /* 04A0; 04A1; Case map */ + { 0x0004A2, 0x0004A2, { 0x0004A3 } }, /* 04A2; 04A3; Case map */ + { 0x0004A4, 0x0004A4, { 0x0004A5 } }, /* 04A4; 04A5; Case map */ + { 0x0004A6, 0x0004A6, { 0x0004A7 } }, /* 04A6; 04A7; Case map */ + { 0x0004A8, 0x0004A8, { 0x0004A9 } }, /* 04A8; 04A9; Case map */ + { 0x0004AA, 0x0004AA, { 0x0004AB } }, /* 04AA; 04AB; Case map */ + { 0x0004AC, 0x0004AC, { 0x0004AD } }, /* 04AC; 04AD; Case map */ + { 0x0004AE, 0x0004AE, { 0x0004AF } }, /* 04AE; 04AF; Case map */ + { 0x0004B0, 0x0004B0, { 0x0004B1 } }, /* 04B0; 04B1; Case map */ + { 0x0004B2, 0x0004B2, { 0x0004B3 } }, /* 04B2; 04B3; Case map */ + { 0x0004B4, 0x0004B4, { 0x0004B5 } }, /* 04B4; 04B5; Case map */ + { 0x0004B6, 0x0004B6, { 0x0004B7 } }, /* 04B6; 04B7; Case map */ + { 0x0004B8, 0x0004B8, { 0x0004B9 } }, /* 04B8; 04B9; Case map */ + { 0x0004BA, 0x0004BA, { 0x0004BB } }, /* 04BA; 04BB; Case map */ + { 0x0004BC, 0x0004BC, { 0x0004BD } }, /* 04BC; 04BD; Case map */ + { 0x0004BE, 0x0004BE, { 0x0004BF } }, /* 04BE; 04BF; Case map */ + { 0x0004C1, 0x0004C1, { 0x0004C2 } }, /* 04C1; 04C2; Case map */ + { 0x0004C3, 0x0004C3, { 0x0004C4 } }, /* 04C3; 04C4; Case map */ + { 0x0004C5, 0x0004C5, { 0x0004C6 } }, /* 04C5; 04C6; Case map */ + { 0x0004C7, 0x0004C7, { 0x0004C8 } }, /* 04C7; 04C8; Case map */ + { 0x0004C9, 0x0004C9, { 0x0004CA } }, /* 04C9; 04CA; Case map */ + { 0x0004CB, 0x0004CB, { 0x0004CC } }, /* 04CB; 04CC; Case map */ + { 0x0004CD, 0x0004CD, { 0x0004CE } }, /* 04CD; 04CE; Case map */ + { 0x0004D0, 0x0004D0, { 0x0004D1 } }, /* 04D0; 04D1; Case map */ + { 0x0004D2, 0x0004D2, { 0x0004D3 } }, /* 04D2; 04D3; Case map */ + { 0x0004D4, 0x0004D4, { 0x0004D5 } }, /* 04D4; 04D5; Case map */ + { 0x0004D6, 0x0004D6, { 0x0004D7 } }, /* 04D6; 04D7; Case map */ + { 0x0004D8, 0x0004D8, { 0x0004D9 } }, /* 04D8; 04D9; Case map */ + { 0x0004DA, 0x0004DA, { 0x0004DB } }, /* 04DA; 04DB; Case map */ + { 0x0004DC, 0x0004DC, { 0x0004DD } }, /* 04DC; 04DD; Case map */ + { 0x0004DE, 0x0004DE, { 0x0004DF } }, /* 04DE; 04DF; Case map */ + { 0x0004E0, 0x0004E0, { 0x0004E1 } }, /* 04E0; 04E1; Case map */ + { 0x0004E2, 0x0004E2, { 0x0004E3 } }, /* 04E2; 04E3; Case map */ + { 0x0004E4, 0x0004E4, { 0x0004E5 } }, /* 04E4; 04E5; Case map */ + { 0x0004E6, 0x0004E6, { 0x0004E7 } }, /* 04E6; 04E7; Case map */ + { 0x0004E8, 0x0004E8, { 0x0004E9 } }, /* 04E8; 04E9; Case map */ + { 0x0004EA, 0x0004EA, { 0x0004EB } }, /* 04EA; 04EB; Case map */ + { 0x0004EC, 0x0004EC, { 0x0004ED } }, /* 04EC; 04ED; Case map */ + { 0x0004EE, 0x0004EE, { 0x0004EF } }, /* 04EE; 04EF; Case map */ + { 0x0004F0, 0x0004F0, { 0x0004F1 } }, /* 04F0; 04F1; Case map */ + { 0x0004F2, 0x0004F2, { 0x0004F3 } }, /* 04F2; 04F3; Case map */ + { 0x0004F4, 0x0004F4, { 0x0004F5 } }, /* 04F4; 04F5; Case map */ + { 0x0004F8, 0x0004F8, { 0x0004F9 } }, /* 04F8; 04F9; Case map */ + { 0x000500, 0x000500, { 0x000501 } }, /* 0500; 0501; Case map */ + { 0x000502, 0x000502, { 0x000503 } }, /* 0502; 0503; Case map */ + { 0x000504, 0x000504, { 0x000505 } }, /* 0504; 0505; Case map */ + { 0x000506, 0x000506, { 0x000507 } }, /* 0506; 0507; Case map */ + { 0x000508, 0x000508, { 0x000509 } }, /* 0508; 0509; Case map */ + { 0x00050A, 0x00050A, { 0x00050B } }, /* 050A; 050B; Case map */ + { 0x00050C, 0x00050C, { 0x00050D } }, /* 050C; 050D; Case map */ + { 0x00050E, 0x00050E, { 0x00050F } }, /* 050E; 050F; Case map */ + { 0x000531, 0x000531, { 0x000561 } }, /* 0531; 0561; Case map */ + { 0x000532, 0x000532, { 0x000562 } }, /* 0532; 0562; Case map */ + { 0x000533, 0x000533, { 0x000563 } }, /* 0533; 0563; Case map */ + { 0x000534, 0x000534, { 0x000564 } }, /* 0534; 0564; Case map */ + { 0x000535, 0x000535, { 0x000565 } }, /* 0535; 0565; Case map */ + { 0x000536, 0x000536, { 0x000566 } }, /* 0536; 0566; Case map */ + { 0x000537, 0x000537, { 0x000567 } }, /* 0537; 0567; Case map */ + { 0x000538, 0x000538, { 0x000568 } }, /* 0538; 0568; Case map */ + { 0x000539, 0x000539, { 0x000569 } }, /* 0539; 0569; Case map */ + { 0x00053A, 0x00053A, { 0x00056A } }, /* 053A; 056A; Case map */ + { 0x00053B, 0x00053B, { 0x00056B } }, /* 053B; 056B; Case map */ + { 0x00053C, 0x00053C, { 0x00056C } }, /* 053C; 056C; Case map */ + { 0x00053D, 0x00053D, { 0x00056D } }, /* 053D; 056D; Case map */ + { 0x00053E, 0x00053E, { 0x00056E } }, /* 053E; 056E; Case map */ + { 0x00053F, 0x00053F, { 0x00056F } }, /* 053F; 056F; Case map */ + { 0x000540, 0x000540, { 0x000570 } }, /* 0540; 0570; Case map */ + { 0x000541, 0x000541, { 0x000571 } }, /* 0541; 0571; Case map */ + { 0x000542, 0x000542, { 0x000572 } }, /* 0542; 0572; Case map */ + { 0x000543, 0x000543, { 0x000573 } }, /* 0543; 0573; Case map */ + { 0x000544, 0x000544, { 0x000574 } }, /* 0544; 0574; Case map */ + { 0x000545, 0x000545, { 0x000575 } }, /* 0545; 0575; Case map */ + { 0x000546, 0x000546, { 0x000576 } }, /* 0546; 0576; Case map */ + { 0x000547, 0x000547, { 0x000577 } }, /* 0547; 0577; Case map */ + { 0x000548, 0x000548, { 0x000578 } }, /* 0548; 0578; Case map */ + { 0x000549, 0x000549, { 0x000579 } }, /* 0549; 0579; Case map */ + { 0x00054A, 0x00054A, { 0x00057A } }, /* 054A; 057A; Case map */ + { 0x00054B, 0x00054B, { 0x00057B } }, /* 054B; 057B; Case map */ + { 0x00054C, 0x00054C, { 0x00057C } }, /* 054C; 057C; Case map */ + { 0x00054D, 0x00054D, { 0x00057D } }, /* 054D; 057D; Case map */ + { 0x00054E, 0x00054E, { 0x00057E } }, /* 054E; 057E; Case map */ + { 0x00054F, 0x00054F, { 0x00057F } }, /* 054F; 057F; Case map */ + { 0x000550, 0x000550, { 0x000580 } }, /* 0550; 0580; Case map */ + { 0x000551, 0x000551, { 0x000581 } }, /* 0551; 0581; Case map */ + { 0x000552, 0x000552, { 0x000582 } }, /* 0552; 0582; Case map */ + { 0x000553, 0x000553, { 0x000583 } }, /* 0553; 0583; Case map */ + { 0x000554, 0x000554, { 0x000584 } }, /* 0554; 0584; Case map */ + { 0x000555, 0x000555, { 0x000585 } }, /* 0555; 0585; Case map */ + { 0x000556, 0x000556, { 0x000586 } }, /* 0556; 0586; Case map */ + { 0x000587, + 0x000587, + { 0x000565, /* 0587; 0565 0582; Case map */ + 0x000582 } }, + { 0x001E00, 0x001E00, { 0x001E01 } }, /* 1E00; 1E01; Case map */ + { 0x001E02, 0x001E02, { 0x001E03 } }, /* 1E02; 1E03; Case map */ + { 0x001E04, 0x001E04, { 0x001E05 } }, /* 1E04; 1E05; Case map */ + { 0x001E06, 0x001E06, { 0x001E07 } }, /* 1E06; 1E07; Case map */ + { 0x001E08, 0x001E08, { 0x001E09 } }, /* 1E08; 1E09; Case map */ + { 0x001E0A, 0x001E0A, { 0x001E0B } }, /* 1E0A; 1E0B; Case map */ + { 0x001E0C, 0x001E0C, { 0x001E0D } }, /* 1E0C; 1E0D; Case map */ + { 0x001E0E, 0x001E0E, { 0x001E0F } }, /* 1E0E; 1E0F; Case map */ + { 0x001E10, 0x001E10, { 0x001E11 } }, /* 1E10; 1E11; Case map */ + { 0x001E12, 0x001E12, { 0x001E13 } }, /* 1E12; 1E13; Case map */ + { 0x001E14, 0x001E14, { 0x001E15 } }, /* 1E14; 1E15; Case map */ + { 0x001E16, 0x001E16, { 0x001E17 } }, /* 1E16; 1E17; Case map */ + { 0x001E18, 0x001E18, { 0x001E19 } }, /* 1E18; 1E19; Case map */ + { 0x001E1A, 0x001E1A, { 0x001E1B } }, /* 1E1A; 1E1B; Case map */ + { 0x001E1C, 0x001E1C, { 0x001E1D } }, /* 1E1C; 1E1D; Case map */ + { 0x001E1E, 0x001E1E, { 0x001E1F } }, /* 1E1E; 1E1F; Case map */ + { 0x001E20, 0x001E20, { 0x001E21 } }, /* 1E20; 1E21; Case map */ + { 0x001E22, 0x001E22, { 0x001E23 } }, /* 1E22; 1E23; Case map */ + { 0x001E24, 0x001E24, { 0x001E25 } }, /* 1E24; 1E25; Case map */ + { 0x001E26, 0x001E26, { 0x001E27 } }, /* 1E26; 1E27; Case map */ + { 0x001E28, 0x001E28, { 0x001E29 } }, /* 1E28; 1E29; Case map */ + { 0x001E2A, 0x001E2A, { 0x001E2B } }, /* 1E2A; 1E2B; Case map */ + { 0x001E2C, 0x001E2C, { 0x001E2D } }, /* 1E2C; 1E2D; Case map */ + { 0x001E2E, 0x001E2E, { 0x001E2F } }, /* 1E2E; 1E2F; Case map */ + { 0x001E30, 0x001E30, { 0x001E31 } }, /* 1E30; 1E31; Case map */ + { 0x001E32, 0x001E32, { 0x001E33 } }, /* 1E32; 1E33; Case map */ + { 0x001E34, 0x001E34, { 0x001E35 } }, /* 1E34; 1E35; Case map */ + { 0x001E36, 0x001E36, { 0x001E37 } }, /* 1E36; 1E37; Case map */ + { 0x001E38, 0x001E38, { 0x001E39 } }, /* 1E38; 1E39; Case map */ + { 0x001E3A, 0x001E3A, { 0x001E3B } }, /* 1E3A; 1E3B; Case map */ + { 0x001E3C, 0x001E3C, { 0x001E3D } }, /* 1E3C; 1E3D; Case map */ + { 0x001E3E, 0x001E3E, { 0x001E3F } }, /* 1E3E; 1E3F; Case map */ + { 0x001E40, 0x001E40, { 0x001E41 } }, /* 1E40; 1E41; Case map */ + { 0x001E42, 0x001E42, { 0x001E43 } }, /* 1E42; 1E43; Case map */ + { 0x001E44, 0x001E44, { 0x001E45 } }, /* 1E44; 1E45; Case map */ + { 0x001E46, 0x001E46, { 0x001E47 } }, /* 1E46; 1E47; Case map */ + { 0x001E48, 0x001E48, { 0x001E49 } }, /* 1E48; 1E49; Case map */ + { 0x001E4A, 0x001E4A, { 0x001E4B } }, /* 1E4A; 1E4B; Case map */ + { 0x001E4C, 0x001E4C, { 0x001E4D } }, /* 1E4C; 1E4D; Case map */ + { 0x001E4E, 0x001E4E, { 0x001E4F } }, /* 1E4E; 1E4F; Case map */ + { 0x001E50, 0x001E50, { 0x001E51 } }, /* 1E50; 1E51; Case map */ + { 0x001E52, 0x001E52, { 0x001E53 } }, /* 1E52; 1E53; Case map */ + { 0x001E54, 0x001E54, { 0x001E55 } }, /* 1E54; 1E55; Case map */ + { 0x001E56, 0x001E56, { 0x001E57 } }, /* 1E56; 1E57; Case map */ + { 0x001E58, 0x001E58, { 0x001E59 } }, /* 1E58; 1E59; Case map */ + { 0x001E5A, 0x001E5A, { 0x001E5B } }, /* 1E5A; 1E5B; Case map */ + { 0x001E5C, 0x001E5C, { 0x001E5D } }, /* 1E5C; 1E5D; Case map */ + { 0x001E5E, 0x001E5E, { 0x001E5F } }, /* 1E5E; 1E5F; Case map */ + { 0x001E60, 0x001E60, { 0x001E61 } }, /* 1E60; 1E61; Case map */ + { 0x001E62, 0x001E62, { 0x001E63 } }, /* 1E62; 1E63; Case map */ + { 0x001E64, 0x001E64, { 0x001E65 } }, /* 1E64; 1E65; Case map */ + { 0x001E66, 0x001E66, { 0x001E67 } }, /* 1E66; 1E67; Case map */ + { 0x001E68, 0x001E68, { 0x001E69 } }, /* 1E68; 1E69; Case map */ + { 0x001E6A, 0x001E6A, { 0x001E6B } }, /* 1E6A; 1E6B; Case map */ + { 0x001E6C, 0x001E6C, { 0x001E6D } }, /* 1E6C; 1E6D; Case map */ + { 0x001E6E, 0x001E6E, { 0x001E6F } }, /* 1E6E; 1E6F; Case map */ + { 0x001E70, 0x001E70, { 0x001E71 } }, /* 1E70; 1E71; Case map */ + { 0x001E72, 0x001E72, { 0x001E73 } }, /* 1E72; 1E73; Case map */ + { 0x001E74, 0x001E74, { 0x001E75 } }, /* 1E74; 1E75; Case map */ + { 0x001E76, 0x001E76, { 0x001E77 } }, /* 1E76; 1E77; Case map */ + { 0x001E78, 0x001E78, { 0x001E79 } }, /* 1E78; 1E79; Case map */ + { 0x001E7A, 0x001E7A, { 0x001E7B } }, /* 1E7A; 1E7B; Case map */ + { 0x001E7C, 0x001E7C, { 0x001E7D } }, /* 1E7C; 1E7D; Case map */ + { 0x001E7E, 0x001E7E, { 0x001E7F } }, /* 1E7E; 1E7F; Case map */ + { 0x001E80, 0x001E80, { 0x001E81 } }, /* 1E80; 1E81; Case map */ + { 0x001E82, 0x001E82, { 0x001E83 } }, /* 1E82; 1E83; Case map */ + { 0x001E84, 0x001E84, { 0x001E85 } }, /* 1E84; 1E85; Case map */ + { 0x001E86, 0x001E86, { 0x001E87 } }, /* 1E86; 1E87; Case map */ + { 0x001E88, 0x001E88, { 0x001E89 } }, /* 1E88; 1E89; Case map */ + { 0x001E8A, 0x001E8A, { 0x001E8B } }, /* 1E8A; 1E8B; Case map */ + { 0x001E8C, 0x001E8C, { 0x001E8D } }, /* 1E8C; 1E8D; Case map */ + { 0x001E8E, 0x001E8E, { 0x001E8F } }, /* 1E8E; 1E8F; Case map */ + { 0x001E90, 0x001E90, { 0x001E91 } }, /* 1E90; 1E91; Case map */ + { 0x001E92, 0x001E92, { 0x001E93 } }, /* 1E92; 1E93; Case map */ + { 0x001E94, 0x001E94, { 0x001E95 } }, /* 1E94; 1E95; Case map */ + { 0x001E96, + 0x001E96, + { 0x000068, /* 1E96; 0068 0331; Case map */ + 0x000331 } }, + { 0x001E97, + 0x001E97, + { 0x000074, /* 1E97; 0074 0308; Case map */ + 0x000308 } }, + { 0x001E98, + 0x001E98, + { 0x000077, /* 1E98; 0077 030A; Case map */ + 0x00030A } }, + { 0x001E99, + 0x001E99, + { 0x000079, /* 1E99; 0079 030A; Case map */ + 0x00030A } }, + { 0x001E9A, + 0x001E9A, + { 0x000061, /* 1E9A; 0061 02BE; Case map */ + 0x0002BE } }, + { 0x001E9B, 0x001E9B, { 0x001E61 } }, /* 1E9B; 1E61; Case map */ + { 0x001EA0, 0x001EA0, { 0x001EA1 } }, /* 1EA0; 1EA1; Case map */ + { 0x001EA2, 0x001EA2, { 0x001EA3 } }, /* 1EA2; 1EA3; Case map */ + { 0x001EA4, 0x001EA4, { 0x001EA5 } }, /* 1EA4; 1EA5; Case map */ + { 0x001EA6, 0x001EA6, { 0x001EA7 } }, /* 1EA6; 1EA7; Case map */ + { 0x001EA8, 0x001EA8, { 0x001EA9 } }, /* 1EA8; 1EA9; Case map */ + { 0x001EAA, 0x001EAA, { 0x001EAB } }, /* 1EAA; 1EAB; Case map */ + { 0x001EAC, 0x001EAC, { 0x001EAD } }, /* 1EAC; 1EAD; Case map */ + { 0x001EAE, 0x001EAE, { 0x001EAF } }, /* 1EAE; 1EAF; Case map */ + { 0x001EB0, 0x001EB0, { 0x001EB1 } }, /* 1EB0; 1EB1; Case map */ + { 0x001EB2, 0x001EB2, { 0x001EB3 } }, /* 1EB2; 1EB3; Case map */ + { 0x001EB4, 0x001EB4, { 0x001EB5 } }, /* 1EB4; 1EB5; Case map */ + { 0x001EB6, 0x001EB6, { 0x001EB7 } }, /* 1EB6; 1EB7; Case map */ + { 0x001EB8, 0x001EB8, { 0x001EB9 } }, /* 1EB8; 1EB9; Case map */ + { 0x001EBA, 0x001EBA, { 0x001EBB } }, /* 1EBA; 1EBB; Case map */ + { 0x001EBC, 0x001EBC, { 0x001EBD } }, /* 1EBC; 1EBD; Case map */ + { 0x001EBE, 0x001EBE, { 0x001EBF } }, /* 1EBE; 1EBF; Case map */ + { 0x001EC0, 0x001EC0, { 0x001EC1 } }, /* 1EC0; 1EC1; Case map */ + { 0x001EC2, 0x001EC2, { 0x001EC3 } }, /* 1EC2; 1EC3; Case map */ + { 0x001EC4, 0x001EC4, { 0x001EC5 } }, /* 1EC4; 1EC5; Case map */ + { 0x001EC6, 0x001EC6, { 0x001EC7 } }, /* 1EC6; 1EC7; Case map */ + { 0x001EC8, 0x001EC8, { 0x001EC9 } }, /* 1EC8; 1EC9; Case map */ + { 0x001ECA, 0x001ECA, { 0x001ECB } }, /* 1ECA; 1ECB; Case map */ + { 0x001ECC, 0x001ECC, { 0x001ECD } }, /* 1ECC; 1ECD; Case map */ + { 0x001ECE, 0x001ECE, { 0x001ECF } }, /* 1ECE; 1ECF; Case map */ + { 0x001ED0, 0x001ED0, { 0x001ED1 } }, /* 1ED0; 1ED1; Case map */ + { 0x001ED2, 0x001ED2, { 0x001ED3 } }, /* 1ED2; 1ED3; Case map */ + { 0x001ED4, 0x001ED4, { 0x001ED5 } }, /* 1ED4; 1ED5; Case map */ + { 0x001ED6, 0x001ED6, { 0x001ED7 } }, /* 1ED6; 1ED7; Case map */ + { 0x001ED8, 0x001ED8, { 0x001ED9 } }, /* 1ED8; 1ED9; Case map */ + { 0x001EDA, 0x001EDA, { 0x001EDB } }, /* 1EDA; 1EDB; Case map */ + { 0x001EDC, 0x001EDC, { 0x001EDD } }, /* 1EDC; 1EDD; Case map */ + { 0x001EDE, 0x001EDE, { 0x001EDF } }, /* 1EDE; 1EDF; Case map */ + { 0x001EE0, 0x001EE0, { 0x001EE1 } }, /* 1EE0; 1EE1; Case map */ + { 0x001EE2, 0x001EE2, { 0x001EE3 } }, /* 1EE2; 1EE3; Case map */ + { 0x001EE4, 0x001EE4, { 0x001EE5 } }, /* 1EE4; 1EE5; Case map */ + { 0x001EE6, 0x001EE6, { 0x001EE7 } }, /* 1EE6; 1EE7; Case map */ + { 0x001EE8, 0x001EE8, { 0x001EE9 } }, /* 1EE8; 1EE9; Case map */ + { 0x001EEA, 0x001EEA, { 0x001EEB } }, /* 1EEA; 1EEB; Case map */ + { 0x001EEC, 0x001EEC, { 0x001EED } }, /* 1EEC; 1EED; Case map */ + { 0x001EEE, 0x001EEE, { 0x001EEF } }, /* 1EEE; 1EEF; Case map */ + { 0x001EF0, 0x001EF0, { 0x001EF1 } }, /* 1EF0; 1EF1; Case map */ + { 0x001EF2, 0x001EF2, { 0x001EF3 } }, /* 1EF2; 1EF3; Case map */ + { 0x001EF4, 0x001EF4, { 0x001EF5 } }, /* 1EF4; 1EF5; Case map */ + { 0x001EF6, 0x001EF6, { 0x001EF7 } }, /* 1EF6; 1EF7; Case map */ + { 0x001EF8, 0x001EF8, { 0x001EF9 } }, /* 1EF8; 1EF9; Case map */ + { 0x001F08, 0x001F08, { 0x001F00 } }, /* 1F08; 1F00; Case map */ + { 0x001F09, 0x001F09, { 0x001F01 } }, /* 1F09; 1F01; Case map */ + { 0x001F0A, 0x001F0A, { 0x001F02 } }, /* 1F0A; 1F02; Case map */ + { 0x001F0B, 0x001F0B, { 0x001F03 } }, /* 1F0B; 1F03; Case map */ + { 0x001F0C, 0x001F0C, { 0x001F04 } }, /* 1F0C; 1F04; Case map */ + { 0x001F0D, 0x001F0D, { 0x001F05 } }, /* 1F0D; 1F05; Case map */ + { 0x001F0E, 0x001F0E, { 0x001F06 } }, /* 1F0E; 1F06; Case map */ + { 0x001F0F, 0x001F0F, { 0x001F07 } }, /* 1F0F; 1F07; Case map */ + { 0x001F18, 0x001F18, { 0x001F10 } }, /* 1F18; 1F10; Case map */ + { 0x001F19, 0x001F19, { 0x001F11 } }, /* 1F19; 1F11; Case map */ + { 0x001F1A, 0x001F1A, { 0x001F12 } }, /* 1F1A; 1F12; Case map */ + { 0x001F1B, 0x001F1B, { 0x001F13 } }, /* 1F1B; 1F13; Case map */ + { 0x001F1C, 0x001F1C, { 0x001F14 } }, /* 1F1C; 1F14; Case map */ + { 0x001F1D, 0x001F1D, { 0x001F15 } }, /* 1F1D; 1F15; Case map */ + { 0x001F28, 0x001F28, { 0x001F20 } }, /* 1F28; 1F20; Case map */ + { 0x001F29, 0x001F29, { 0x001F21 } }, /* 1F29; 1F21; Case map */ + { 0x001F2A, 0x001F2A, { 0x001F22 } }, /* 1F2A; 1F22; Case map */ + { 0x001F2B, 0x001F2B, { 0x001F23 } }, /* 1F2B; 1F23; Case map */ + { 0x001F2C, 0x001F2C, { 0x001F24 } }, /* 1F2C; 1F24; Case map */ + { 0x001F2D, 0x001F2D, { 0x001F25 } }, /* 1F2D; 1F25; Case map */ + { 0x001F2E, 0x001F2E, { 0x001F26 } }, /* 1F2E; 1F26; Case map */ + { 0x001F2F, 0x001F2F, { 0x001F27 } }, /* 1F2F; 1F27; Case map */ + { 0x001F38, 0x001F38, { 0x001F30 } }, /* 1F38; 1F30; Case map */ + { 0x001F39, 0x001F39, { 0x001F31 } }, /* 1F39; 1F31; Case map */ + { 0x001F3A, 0x001F3A, { 0x001F32 } }, /* 1F3A; 1F32; Case map */ + { 0x001F3B, 0x001F3B, { 0x001F33 } }, /* 1F3B; 1F33; Case map */ + { 0x001F3C, 0x001F3C, { 0x001F34 } }, /* 1F3C; 1F34; Case map */ + { 0x001F3D, 0x001F3D, { 0x001F35 } }, /* 1F3D; 1F35; Case map */ + { 0x001F3E, 0x001F3E, { 0x001F36 } }, /* 1F3E; 1F36; Case map */ + { 0x001F3F, 0x001F3F, { 0x001F37 } }, /* 1F3F; 1F37; Case map */ + { 0x001F48, 0x001F48, { 0x001F40 } }, /* 1F48; 1F40; Case map */ + { 0x001F49, 0x001F49, { 0x001F41 } }, /* 1F49; 1F41; Case map */ + { 0x001F4A, 0x001F4A, { 0x001F42 } }, /* 1F4A; 1F42; Case map */ + { 0x001F4B, 0x001F4B, { 0x001F43 } }, /* 1F4B; 1F43; Case map */ + { 0x001F4C, 0x001F4C, { 0x001F44 } }, /* 1F4C; 1F44; Case map */ + { 0x001F4D, 0x001F4D, { 0x001F45 } }, /* 1F4D; 1F45; Case map */ + { 0x001F50, + 0x001F50, + { 0x0003C5, /* 1F50; 03C5 0313; Case map */ + 0x000313 } }, + { 0x001F52, + 0x001F52, + { 0x0003C5, /* 1F52; 03C5 0313 0300; Case map */ + 0x000313, 0x000300 } }, + { 0x001F54, + 0x001F54, + { 0x0003C5, /* 1F54; 03C5 0313 0301; Case map */ + 0x000313, 0x000301 } }, + { 0x001F56, + 0x001F56, + { 0x0003C5, /* 1F56; 03C5 0313 0342; Case map */ + 0x000313, 0x000342 } }, + { 0x001F59, 0x001F59, { 0x001F51 } }, /* 1F59; 1F51; Case map */ + { 0x001F5B, 0x001F5B, { 0x001F53 } }, /* 1F5B; 1F53; Case map */ + { 0x001F5D, 0x001F5D, { 0x001F55 } }, /* 1F5D; 1F55; Case map */ + { 0x001F5F, 0x001F5F, { 0x001F57 } }, /* 1F5F; 1F57; Case map */ + { 0x001F68, 0x001F68, { 0x001F60 } }, /* 1F68; 1F60; Case map */ + { 0x001F69, 0x001F69, { 0x001F61 } }, /* 1F69; 1F61; Case map */ + { 0x001F6A, 0x001F6A, { 0x001F62 } }, /* 1F6A; 1F62; Case map */ + { 0x001F6B, 0x001F6B, { 0x001F63 } }, /* 1F6B; 1F63; Case map */ + { 0x001F6C, 0x001F6C, { 0x001F64 } }, /* 1F6C; 1F64; Case map */ + { 0x001F6D, 0x001F6D, { 0x001F65 } }, /* 1F6D; 1F65; Case map */ + { 0x001F6E, 0x001F6E, { 0x001F66 } }, /* 1F6E; 1F66; Case map */ + { 0x001F6F, 0x001F6F, { 0x001F67 } }, /* 1F6F; 1F67; Case map */ + { 0x001F80, + 0x001F80, + { 0x001F00, /* 1F80; 1F00 03B9; Case map */ + 0x0003B9 } }, + { 0x001F81, + 0x001F81, + { 0x001F01, /* 1F81; 1F01 03B9; Case map */ + 0x0003B9 } }, + { 0x001F82, + 0x001F82, + { 0x001F02, /* 1F82; 1F02 03B9; Case map */ + 0x0003B9 } }, + { 0x001F83, + 0x001F83, + { 0x001F03, /* 1F83; 1F03 03B9; Case map */ + 0x0003B9 } }, + { 0x001F84, + 0x001F84, + { 0x001F04, /* 1F84; 1F04 03B9; Case map */ + 0x0003B9 } }, + { 0x001F85, + 0x001F85, + { 0x001F05, /* 1F85; 1F05 03B9; Case map */ + 0x0003B9 } }, + { 0x001F86, + 0x001F86, + { 0x001F06, /* 1F86; 1F06 03B9; Case map */ + 0x0003B9 } }, + { 0x001F87, + 0x001F87, + { 0x001F07, /* 1F87; 1F07 03B9; Case map */ + 0x0003B9 } }, + { 0x001F88, + 0x001F88, + { 0x001F00, /* 1F88; 1F00 03B9; Case map */ + 0x0003B9 } }, + { 0x001F89, + 0x001F89, + { 0x001F01, /* 1F89; 1F01 03B9; Case map */ + 0x0003B9 } }, + { 0x001F8A, + 0x001F8A, + { 0x001F02, /* 1F8A; 1F02 03B9; Case map */ + 0x0003B9 } }, + { 0x001F8B, + 0x001F8B, + { 0x001F03, /* 1F8B; 1F03 03B9; Case map */ + 0x0003B9 } }, + { 0x001F8C, + 0x001F8C, + { 0x001F04, /* 1F8C; 1F04 03B9; Case map */ + 0x0003B9 } }, + { 0x001F8D, + 0x001F8D, + { 0x001F05, /* 1F8D; 1F05 03B9; Case map */ + 0x0003B9 } }, + { 0x001F8E, + 0x001F8E, + { 0x001F06, /* 1F8E; 1F06 03B9; Case map */ + 0x0003B9 } }, + { 0x001F8F, + 0x001F8F, + { 0x001F07, /* 1F8F; 1F07 03B9; Case map */ + 0x0003B9 } }, + { 0x001F90, + 0x001F90, + { 0x001F20, /* 1F90; 1F20 03B9; Case map */ + 0x0003B9 } }, + { 0x001F91, + 0x001F91, + { 0x001F21, /* 1F91; 1F21 03B9; Case map */ + 0x0003B9 } }, + { 0x001F92, + 0x001F92, + { 0x001F22, /* 1F92; 1F22 03B9; Case map */ + 0x0003B9 } }, + { 0x001F93, + 0x001F93, + { 0x001F23, /* 1F93; 1F23 03B9; Case map */ + 0x0003B9 } }, + { 0x001F94, + 0x001F94, + { 0x001F24, /* 1F94; 1F24 03B9; Case map */ + 0x0003B9 } }, + { 0x001F95, + 0x001F95, + { 0x001F25, /* 1F95; 1F25 03B9; Case map */ + 0x0003B9 } }, + { 0x001F96, + 0x001F96, + { 0x001F26, /* 1F96; 1F26 03B9; Case map */ + 0x0003B9 } }, + { 0x001F97, + 0x001F97, + { 0x001F27, /* 1F97; 1F27 03B9; Case map */ + 0x0003B9 } }, + { 0x001F98, + 0x001F98, + { 0x001F20, /* 1F98; 1F20 03B9; Case map */ + 0x0003B9 } }, + { 0x001F99, + 0x001F99, + { 0x001F21, /* 1F99; 1F21 03B9; Case map */ + 0x0003B9 } }, + { 0x001F9A, + 0x001F9A, + { 0x001F22, /* 1F9A; 1F22 03B9; Case map */ + 0x0003B9 } }, + { 0x001F9B, + 0x001F9B, + { 0x001F23, /* 1F9B; 1F23 03B9; Case map */ + 0x0003B9 } }, + { 0x001F9C, + 0x001F9C, + { 0x001F24, /* 1F9C; 1F24 03B9; Case map */ + 0x0003B9 } }, + { 0x001F9D, + 0x001F9D, + { 0x001F25, /* 1F9D; 1F25 03B9; Case map */ + 0x0003B9 } }, + { 0x001F9E, + 0x001F9E, + { 0x001F26, /* 1F9E; 1F26 03B9; Case map */ + 0x0003B9 } }, + { 0x001F9F, + 0x001F9F, + { 0x001F27, /* 1F9F; 1F27 03B9; Case map */ + 0x0003B9 } }, + { 0x001FA0, + 0x001FA0, + { 0x001F60, /* 1FA0; 1F60 03B9; Case map */ + 0x0003B9 } }, + { 0x001FA1, + 0x001FA1, + { 0x001F61, /* 1FA1; 1F61 03B9; Case map */ + 0x0003B9 } }, + { 0x001FA2, + 0x001FA2, + { 0x001F62, /* 1FA2; 1F62 03B9; Case map */ + 0x0003B9 } }, + { 0x001FA3, + 0x001FA3, + { 0x001F63, /* 1FA3; 1F63 03B9; Case map */ + 0x0003B9 } }, + { 0x001FA4, + 0x001FA4, + { 0x001F64, /* 1FA4; 1F64 03B9; Case map */ + 0x0003B9 } }, + { 0x001FA5, + 0x001FA5, + { 0x001F65, /* 1FA5; 1F65 03B9; Case map */ + 0x0003B9 } }, + { 0x001FA6, + 0x001FA6, + { 0x001F66, /* 1FA6; 1F66 03B9; Case map */ + 0x0003B9 } }, + { 0x001FA7, + 0x001FA7, + { 0x001F67, /* 1FA7; 1F67 03B9; Case map */ + 0x0003B9 } }, + { 0x001FA8, + 0x001FA8, + { 0x001F60, /* 1FA8; 1F60 03B9; Case map */ + 0x0003B9 } }, + { 0x001FA9, + 0x001FA9, + { 0x001F61, /* 1FA9; 1F61 03B9; Case map */ + 0x0003B9 } }, + { 0x001FAA, + 0x001FAA, + { 0x001F62, /* 1FAA; 1F62 03B9; Case map */ + 0x0003B9 } }, + { 0x001FAB, + 0x001FAB, + { 0x001F63, /* 1FAB; 1F63 03B9; Case map */ + 0x0003B9 } }, + { 0x001FAC, + 0x001FAC, + { 0x001F64, /* 1FAC; 1F64 03B9; Case map */ + 0x0003B9 } }, + { 0x001FAD, + 0x001FAD, + { 0x001F65, /* 1FAD; 1F65 03B9; Case map */ + 0x0003B9 } }, + { 0x001FAE, + 0x001FAE, + { 0x001F66, /* 1FAE; 1F66 03B9; Case map */ + 0x0003B9 } }, + { 0x001FAF, + 0x001FAF, + { 0x001F67, /* 1FAF; 1F67 03B9; Case map */ + 0x0003B9 } }, + { 0x001FB2, + 0x001FB2, + { 0x001F70, /* 1FB2; 1F70 03B9; Case map */ + 0x0003B9 } }, + { 0x001FB3, + 0x001FB3, + { 0x0003B1, /* 1FB3; 03B1 03B9; Case map */ + 0x0003B9 } }, + { 0x001FB4, + 0x001FB4, + { 0x0003AC, /* 1FB4; 03AC 03B9; Case map */ + 0x0003B9 } }, + { 0x001FB6, + 0x001FB6, + { 0x0003B1, /* 1FB6; 03B1 0342; Case map */ + 0x000342 } }, + { 0x001FB7, + 0x001FB7, + { 0x0003B1, /* 1FB7; 03B1 0342 03B9; Case map */ + 0x000342, 0x0003B9 } }, + { 0x001FB8, 0x001FB8, { 0x001FB0 } }, /* 1FB8; 1FB0; Case map */ + { 0x001FB9, 0x001FB9, { 0x001FB1 } }, /* 1FB9; 1FB1; Case map */ + { 0x001FBA, 0x001FBA, { 0x001F70 } }, /* 1FBA; 1F70; Case map */ + { 0x001FBB, 0x001FBB, { 0x001F71 } }, /* 1FBB; 1F71; Case map */ + { 0x001FBC, + 0x001FBC, + { 0x0003B1, /* 1FBC; 03B1 03B9; Case map */ + 0x0003B9 } }, + { 0x001FBE, 0x001FBE, { 0x0003B9 } }, /* 1FBE; 03B9; Case map */ + { 0x001FC2, + 0x001FC2, + { 0x001F74, /* 1FC2; 1F74 03B9; Case map */ + 0x0003B9 } }, + { 0x001FC3, + 0x001FC3, + { 0x0003B7, /* 1FC3; 03B7 03B9; Case map */ + 0x0003B9 } }, + { 0x001FC4, + 0x001FC4, + { 0x0003AE, /* 1FC4; 03AE 03B9; Case map */ + 0x0003B9 } }, + { 0x001FC6, + 0x001FC6, + { 0x0003B7, /* 1FC6; 03B7 0342; Case map */ + 0x000342 } }, + { 0x001FC7, + 0x001FC7, + { 0x0003B7, /* 1FC7; 03B7 0342 03B9; Case map */ + 0x000342, 0x0003B9 } }, + { 0x001FC8, 0x001FC8, { 0x001F72 } }, /* 1FC8; 1F72; Case map */ + { 0x001FC9, 0x001FC9, { 0x001F73 } }, /* 1FC9; 1F73; Case map */ + { 0x001FCA, 0x001FCA, { 0x001F74 } }, /* 1FCA; 1F74; Case map */ + { 0x001FCB, 0x001FCB, { 0x001F75 } }, /* 1FCB; 1F75; Case map */ + { 0x001FCC, + 0x001FCC, + { 0x0003B7, /* 1FCC; 03B7 03B9; Case map */ + 0x0003B9 } }, + { 0x001FD2, + 0x001FD2, + { 0x0003B9, /* 1FD2; 03B9 0308 0300; Case map */ + 0x000308, 0x000300 } }, + { 0x001FD3, + 0x001FD3, + { 0x0003B9, /* 1FD3; 03B9 0308 0301; Case map */ + 0x000308, 0x000301 } }, + { 0x001FD6, + 0x001FD6, + { 0x0003B9, /* 1FD6; 03B9 0342; Case map */ + 0x000342 } }, + { 0x001FD7, + 0x001FD7, + { 0x0003B9, /* 1FD7; 03B9 0308 0342; Case map */ + 0x000308, 0x000342 } }, + { 0x001FD8, 0x001FD8, { 0x001FD0 } }, /* 1FD8; 1FD0; Case map */ + { 0x001FD9, 0x001FD9, { 0x001FD1 } }, /* 1FD9; 1FD1; Case map */ + { 0x001FDA, 0x001FDA, { 0x001F76 } }, /* 1FDA; 1F76; Case map */ + { 0x001FDB, 0x001FDB, { 0x001F77 } }, /* 1FDB; 1F77; Case map */ + { 0x001FE2, + 0x001FE2, + { 0x0003C5, /* 1FE2; 03C5 0308 0300; Case map */ + 0x000308, 0x000300 } }, + { 0x001FE3, + 0x001FE3, + { 0x0003C5, /* 1FE3; 03C5 0308 0301; Case map */ + 0x000308, 0x000301 } }, + { 0x001FE4, + 0x001FE4, + { 0x0003C1, /* 1FE4; 03C1 0313; Case map */ + 0x000313 } }, + { 0x001FE6, + 0x001FE6, + { 0x0003C5, /* 1FE6; 03C5 0342; Case map */ + 0x000342 } }, + { 0x001FE7, + 0x001FE7, + { 0x0003C5, /* 1FE7; 03C5 0308 0342; Case map */ + 0x000308, 0x000342 } }, + { 0x001FE8, 0x001FE8, { 0x001FE0 } }, /* 1FE8; 1FE0; Case map */ + { 0x001FE9, 0x001FE9, { 0x001FE1 } }, /* 1FE9; 1FE1; Case map */ + { 0x001FEA, 0x001FEA, { 0x001F7A } }, /* 1FEA; 1F7A; Case map */ + { 0x001FEB, 0x001FEB, { 0x001F7B } }, /* 1FEB; 1F7B; Case map */ + { 0x001FEC, 0x001FEC, { 0x001FE5 } }, /* 1FEC; 1FE5; Case map */ + { 0x001FF2, + 0x001FF2, + { 0x001F7C, /* 1FF2; 1F7C 03B9; Case map */ + 0x0003B9 } }, + { 0x001FF3, + 0x001FF3, + { 0x0003C9, /* 1FF3; 03C9 03B9; Case map */ + 0x0003B9 } }, + { 0x001FF4, + 0x001FF4, + { 0x0003CE, /* 1FF4; 03CE 03B9; Case map */ + 0x0003B9 } }, + { 0x001FF6, + 0x001FF6, + { 0x0003C9, /* 1FF6; 03C9 0342; Case map */ + 0x000342 } }, + { 0x001FF7, + 0x001FF7, + { 0x0003C9, /* 1FF7; 03C9 0342 03B9; Case map */ + 0x000342, 0x0003B9 } }, + { 0x001FF8, 0x001FF8, { 0x001F78 } }, /* 1FF8; 1F78; Case map */ + { 0x001FF9, 0x001FF9, { 0x001F79 } }, /* 1FF9; 1F79; Case map */ + { 0x001FFA, 0x001FFA, { 0x001F7C } }, /* 1FFA; 1F7C; Case map */ + { 0x001FFB, 0x001FFB, { 0x001F7D } }, /* 1FFB; 1F7D; Case map */ + { 0x001FFC, + 0x001FFC, + { 0x0003C9, /* 1FFC; 03C9 03B9; Case map */ + 0x0003B9 } }, + { 0x0020A8, + 0x0020A8, + { 0x000072, /* 20A8; 0072 0073; Additional folding */ + 0x000073 } }, + { 0x002102, 0x002102, { 0x000063 } }, /* 2102; 0063; Additional folding */ + { 0x002103, + 0x002103, + { 0x0000B0, /* 2103; 00B0 0063; Additional folding */ + 0x000063 } }, + { 0x002107, 0x002107, { 0x00025B } }, /* 2107; 025B; Additional folding */ + { 0x002109, + 0x002109, + { 0x0000B0, /* 2109; 00B0 0066; Additional folding */ + 0x000066 } }, + { 0x00210B, 0x00210B, { 0x000068 } }, /* 210B; 0068; Additional folding */ + { 0x00210C, 0x00210C, { 0x000068 } }, /* 210C; 0068; Additional folding */ + { 0x00210D, 0x00210D, { 0x000068 } }, /* 210D; 0068; Additional folding */ + { 0x002110, 0x002110, { 0x000069 } }, /* 2110; 0069; Additional folding */ + { 0x002111, 0x002111, { 0x000069 } }, /* 2111; 0069; Additional folding */ + { 0x002112, 0x002112, { 0x00006C } }, /* 2112; 006C; Additional folding */ + { 0x002115, 0x002115, { 0x00006E } }, /* 2115; 006E; Additional folding */ + { 0x002116, + 0x002116, + { 0x00006E, /* 2116; 006E 006F; Additional folding */ + 0x00006F } }, + { 0x002119, 0x002119, { 0x000070 } }, /* 2119; 0070; Additional folding */ + { 0x00211A, 0x00211A, { 0x000071 } }, /* 211A; 0071; Additional folding */ + { 0x00211B, 0x00211B, { 0x000072 } }, /* 211B; 0072; Additional folding */ + { 0x00211C, 0x00211C, { 0x000072 } }, /* 211C; 0072; Additional folding */ + { 0x00211D, 0x00211D, { 0x000072 } }, /* 211D; 0072; Additional folding */ + { 0x002120, + 0x002120, + { 0x000073, /* 2120; 0073 006D; Additional folding */ + 0x00006D } }, + { 0x002121, + 0x002121, + { 0x000074, /* 2121; 0074 0065 006C; Additional folding */ + 0x000065, 0x00006C } }, + { 0x002122, + 0x002122, + { 0x000074, /* 2122; 0074 006D; Additional folding */ + 0x00006D } }, + { 0x002124, 0x002124, { 0x00007A } }, /* 2124; 007A; Additional folding */ + { 0x002126, 0x002126, { 0x0003C9 } }, /* 2126; 03C9; Case map */ + { 0x002128, 0x002128, { 0x00007A } }, /* 2128; 007A; Additional folding */ + { 0x00212A, 0x00212A, { 0x00006B } }, /* 212A; 006B; Case map */ + { 0x00212B, 0x00212B, { 0x0000E5 } }, /* 212B; 00E5; Case map */ + { 0x00212C, 0x00212C, { 0x000062 } }, /* 212C; 0062; Additional folding */ + { 0x00212D, 0x00212D, { 0x000063 } }, /* 212D; 0063; Additional folding */ + { 0x002130, 0x002130, { 0x000065 } }, /* 2130; 0065; Additional folding */ + { 0x002131, 0x002131, { 0x000066 } }, /* 2131; 0066; Additional folding */ + { 0x002133, 0x002133, { 0x00006D } }, /* 2133; 006D; Additional folding */ + { 0x00213E, 0x00213E, { 0x0003B3 } }, /* 213E; 03B3; Additional folding */ + { 0x00213F, 0x00213F, { 0x0003C0 } }, /* 213F; 03C0; Additional folding */ + { 0x002145, 0x002145, { 0x000064 } }, /* 2145; 0064; Additional folding */ + { 0x002160, 0x002160, { 0x002170 } }, /* 2160; 2170; Case map */ + { 0x002161, 0x002161, { 0x002171 } }, /* 2161; 2171; Case map */ + { 0x002162, 0x002162, { 0x002172 } }, /* 2162; 2172; Case map */ + { 0x002163, 0x002163, { 0x002173 } }, /* 2163; 2173; Case map */ + { 0x002164, 0x002164, { 0x002174 } }, /* 2164; 2174; Case map */ + { 0x002165, 0x002165, { 0x002175 } }, /* 2165; 2175; Case map */ + { 0x002166, 0x002166, { 0x002176 } }, /* 2166; 2176; Case map */ + { 0x002167, 0x002167, { 0x002177 } }, /* 2167; 2177; Case map */ + { 0x002168, 0x002168, { 0x002178 } }, /* 2168; 2178; Case map */ + { 0x002169, 0x002169, { 0x002179 } }, /* 2169; 2179; Case map */ + { 0x00216A, 0x00216A, { 0x00217A } }, /* 216A; 217A; Case map */ + { 0x00216B, 0x00216B, { 0x00217B } }, /* 216B; 217B; Case map */ + { 0x00216C, 0x00216C, { 0x00217C } }, /* 216C; 217C; Case map */ + { 0x00216D, 0x00216D, { 0x00217D } }, /* 216D; 217D; Case map */ + { 0x00216E, 0x00216E, { 0x00217E } }, /* 216E; 217E; Case map */ + { 0x00216F, 0x00216F, { 0x00217F } }, /* 216F; 217F; Case map */ + { 0x0024B6, 0x0024B6, { 0x0024D0 } }, /* 24B6; 24D0; Case map */ + { 0x0024B7, 0x0024B7, { 0x0024D1 } }, /* 24B7; 24D1; Case map */ + { 0x0024B8, 0x0024B8, { 0x0024D2 } }, /* 24B8; 24D2; Case map */ + { 0x0024B9, 0x0024B9, { 0x0024D3 } }, /* 24B9; 24D3; Case map */ + { 0x0024BA, 0x0024BA, { 0x0024D4 } }, /* 24BA; 24D4; Case map */ + { 0x0024BB, 0x0024BB, { 0x0024D5 } }, /* 24BB; 24D5; Case map */ + { 0x0024BC, 0x0024BC, { 0x0024D6 } }, /* 24BC; 24D6; Case map */ + { 0x0024BD, 0x0024BD, { 0x0024D7 } }, /* 24BD; 24D7; Case map */ + { 0x0024BE, 0x0024BE, { 0x0024D8 } }, /* 24BE; 24D8; Case map */ + { 0x0024BF, 0x0024BF, { 0x0024D9 } }, /* 24BF; 24D9; Case map */ + { 0x0024C0, 0x0024C0, { 0x0024DA } }, /* 24C0; 24DA; Case map */ + { 0x0024C1, 0x0024C1, { 0x0024DB } }, /* 24C1; 24DB; Case map */ + { 0x0024C2, 0x0024C2, { 0x0024DC } }, /* 24C2; 24DC; Case map */ + { 0x0024C3, 0x0024C3, { 0x0024DD } }, /* 24C3; 24DD; Case map */ + { 0x0024C4, 0x0024C4, { 0x0024DE } }, /* 24C4; 24DE; Case map */ + { 0x0024C5, 0x0024C5, { 0x0024DF } }, /* 24C5; 24DF; Case map */ + { 0x0024C6, 0x0024C6, { 0x0024E0 } }, /* 24C6; 24E0; Case map */ + { 0x0024C7, 0x0024C7, { 0x0024E1 } }, /* 24C7; 24E1; Case map */ + { 0x0024C8, 0x0024C8, { 0x0024E2 } }, /* 24C8; 24E2; Case map */ + { 0x0024C9, 0x0024C9, { 0x0024E3 } }, /* 24C9; 24E3; Case map */ + { 0x0024CA, 0x0024CA, { 0x0024E4 } }, /* 24CA; 24E4; Case map */ + { 0x0024CB, 0x0024CB, { 0x0024E5 } }, /* 24CB; 24E5; Case map */ + { 0x0024CC, 0x0024CC, { 0x0024E6 } }, /* 24CC; 24E6; Case map */ + { 0x0024CD, 0x0024CD, { 0x0024E7 } }, /* 24CD; 24E7; Case map */ + { 0x0024CE, 0x0024CE, { 0x0024E8 } }, /* 24CE; 24E8; Case map */ + { 0x0024CF, 0x0024CF, { 0x0024E9 } }, /* 24CF; 24E9; Case map */ + { 0x003371, + 0x003371, + { 0x000068, /* 3371; 0068 0070 0061; Additional folding */ + 0x000070, 0x000061 } }, + { 0x003373, + 0x003373, + { 0x000061, /* 3373; 0061 0075; Additional folding */ + 0x000075 } }, + { 0x003375, + 0x003375, + { 0x00006F, /* 3375; 006F 0076; Additional folding */ + 0x000076 } }, + { 0x003380, + 0x003380, + { 0x000070, /* 3380; 0070 0061; Additional folding */ + 0x000061 } }, + { 0x003381, + 0x003381, + { 0x00006E, /* 3381; 006E 0061; Additional folding */ + 0x000061 } }, + { 0x003382, + 0x003382, + { 0x0003BC, /* 3382; 03BC 0061; Additional folding */ + 0x000061 } }, + { 0x003383, + 0x003383, + { 0x00006D, /* 3383; 006D 0061; Additional folding */ + 0x000061 } }, + { 0x003384, + 0x003384, + { 0x00006B, /* 3384; 006B 0061; Additional folding */ + 0x000061 } }, + { 0x003385, + 0x003385, + { 0x00006B, /* 3385; 006B 0062; Additional folding */ + 0x000062 } }, + { 0x003386, + 0x003386, + { 0x00006D, /* 3386; 006D 0062; Additional folding */ + 0x000062 } }, + { 0x003387, + 0x003387, + { 0x000067, /* 3387; 0067 0062; Additional folding */ + 0x000062 } }, + { 0x00338A, + 0x00338A, + { 0x000070, /* 338A; 0070 0066; Additional folding */ + 0x000066 } }, + { 0x00338B, + 0x00338B, + { 0x00006E, /* 338B; 006E 0066; Additional folding */ + 0x000066 } }, + { 0x00338C, + 0x00338C, + { 0x0003BC, /* 338C; 03BC 0066; Additional folding */ + 0x000066 } }, + { 0x003390, + 0x003390, + { 0x000068, /* 3390; 0068 007A; Additional folding */ + 0x00007A } }, + { 0x003391, + 0x003391, + { 0x00006B, /* 3391; 006B 0068 007A; Additional folding */ + 0x000068, 0x00007A } }, + { 0x003392, + 0x003392, + { 0x00006D, /* 3392; 006D 0068 007A; Additional folding */ + 0x000068, 0x00007A } }, + { 0x003393, + 0x003393, + { 0x000067, /* 3393; 0067 0068 007A; Additional folding */ + 0x000068, 0x00007A } }, + { 0x003394, + 0x003394, + { 0x000074, /* 3394; 0074 0068 007A; Additional folding */ + 0x000068, 0x00007A } }, + { 0x0033A9, + 0x0033A9, + { 0x000070, /* 33A9; 0070 0061; Additional folding */ + 0x000061 } }, + { 0x0033AA, + 0x0033AA, + { 0x00006B, /* 33AA; 006B 0070 0061; Additional folding */ + 0x000070, 0x000061 } }, + { 0x0033AB, + 0x0033AB, + { 0x00006D, /* 33AB; 006D 0070 0061; Additional folding */ + 0x000070, 0x000061 } }, + { 0x0033AC, + 0x0033AC, + { 0x000067, /* 33AC; 0067 0070 0061; Additional folding */ + 0x000070, 0x000061 } }, + { 0x0033B4, + 0x0033B4, + { 0x000070, /* 33B4; 0070 0076; Additional folding */ + 0x000076 } }, + { 0x0033B5, + 0x0033B5, + { 0x00006E, /* 33B5; 006E 0076; Additional folding */ + 0x000076 } }, + { 0x0033B6, + 0x0033B6, + { 0x0003BC, /* 33B6; 03BC 0076; Additional folding */ + 0x000076 } }, + { 0x0033B7, + 0x0033B7, + { 0x00006D, /* 33B7; 006D 0076; Additional folding */ + 0x000076 } }, + { 0x0033B8, + 0x0033B8, + { 0x00006B, /* 33B8; 006B 0076; Additional folding */ + 0x000076 } }, + { 0x0033B9, + 0x0033B9, + { 0x00006D, /* 33B9; 006D 0076; Additional folding */ + 0x000076 } }, + { 0x0033BA, + 0x0033BA, + { 0x000070, /* 33BA; 0070 0077; Additional folding */ + 0x000077 } }, + { 0x0033BB, + 0x0033BB, + { 0x00006E, /* 33BB; 006E 0077; Additional folding */ + 0x000077 } }, + { 0x0033BC, + 0x0033BC, + { 0x0003BC, /* 33BC; 03BC 0077; Additional folding */ + 0x000077 } }, + { 0x0033BD, + 0x0033BD, + { 0x00006D, /* 33BD; 006D 0077; Additional folding */ + 0x000077 } }, + { 0x0033BE, + 0x0033BE, + { 0x00006B, /* 33BE; 006B 0077; Additional folding */ + 0x000077 } }, + { 0x0033BF, + 0x0033BF, + { 0x00006D, /* 33BF; 006D 0077; Additional folding */ + 0x000077 } }, + { 0x0033C0, + 0x0033C0, + { 0x00006B, /* 33C0; 006B 03C9; Additional folding */ + 0x0003C9 } }, + { 0x0033C1, + 0x0033C1, + { 0x00006D, /* 33C1; 006D 03C9; Additional folding */ + 0x0003C9 } }, + { 0x0033C3, + 0x0033C3, + { 0x000062, /* 33C3; 0062 0071; Additional folding */ + 0x000071 } }, + { 0x0033C6, + 0x0033C6, + { 0x000063, /* 33C6; 0063 2215 006B 0067; Additional folding */ + 0x002215, 0x00006B, 0x000067 } }, + { 0x0033C7, + 0x0033C7, + { 0x000063, /* 33C7; 0063 006F 002E; Additional folding */ + 0x00006F, 0x00002E } }, + { 0x0033C8, + 0x0033C8, + { 0x000064, /* 33C8; 0064 0062; Additional folding */ + 0x000062 } }, + { 0x0033C9, + 0x0033C9, + { 0x000067, /* 33C9; 0067 0079; Additional folding */ + 0x000079 } }, + { 0x0033CB, + 0x0033CB, + { 0x000068, /* 33CB; 0068 0070; Additional folding */ + 0x000070 } }, + { 0x0033CD, + 0x0033CD, + { 0x00006B, /* 33CD; 006B 006B; Additional folding */ + 0x00006B } }, + { 0x0033CE, + 0x0033CE, + { 0x00006B, /* 33CE; 006B 006D; Additional folding */ + 0x00006D } }, + { 0x0033D7, + 0x0033D7, + { 0x000070, /* 33D7; 0070 0068; Additional folding */ + 0x000068 } }, + { 0x0033D9, + 0x0033D9, + { 0x000070, /* 33D9; 0070 0070 006D; Additional folding */ + 0x000070, 0x00006D } }, + { 0x0033DA, + 0x0033DA, + { 0x000070, /* 33DA; 0070 0072; Additional folding */ + 0x000072 } }, + { 0x0033DC, + 0x0033DC, + { 0x000073, /* 33DC; 0073 0076; Additional folding */ + 0x000076 } }, + { 0x0033DD, + 0x0033DD, + { 0x000077, /* 33DD; 0077 0062; Additional folding */ + 0x000062 } }, + { 0x00FB00, + 0x00FB00, + { 0x000066, /* FB00; 0066 0066; Case map */ + 0x000066 } }, + { 0x00FB01, + 0x00FB01, + { 0x000066, /* FB01; 0066 0069; Case map */ + 0x000069 } }, + { 0x00FB02, + 0x00FB02, + { 0x000066, /* FB02; 0066 006C; Case map */ + 0x00006C } }, + { 0x00FB03, + 0x00FB03, + { 0x000066, /* FB03; 0066 0066 0069; Case map */ + 0x000066, 0x000069 } }, + { 0x00FB04, + 0x00FB04, + { 0x000066, /* FB04; 0066 0066 006C; Case map */ + 0x000066, 0x00006C } }, + { 0x00FB05, + 0x00FB05, + { 0x000073, /* FB05; 0073 0074; Case map */ + 0x000074 } }, + { 0x00FB06, + 0x00FB06, + { 0x000073, /* FB06; 0073 0074; Case map */ + 0x000074 } }, + { 0x00FB13, + 0x00FB13, + { 0x000574, /* FB13; 0574 0576; Case map */ + 0x000576 } }, + { 0x00FB14, + 0x00FB14, + { 0x000574, /* FB14; 0574 0565; Case map */ + 0x000565 } }, + { 0x00FB15, + 0x00FB15, + { 0x000574, /* FB15; 0574 056B; Case map */ + 0x00056B } }, + { 0x00FB16, + 0x00FB16, + { 0x00057E, /* FB16; 057E 0576; Case map */ + 0x000576 } }, + { 0x00FB17, + 0x00FB17, + { 0x000574, /* FB17; 0574 056D; Case map */ + 0x00056D } }, + { 0x00FF21, 0x00FF21, { 0x00FF41 } }, /* FF21; FF41; Case map */ + { 0x00FF22, 0x00FF22, { 0x00FF42 } }, /* FF22; FF42; Case map */ + { 0x00FF23, 0x00FF23, { 0x00FF43 } }, /* FF23; FF43; Case map */ + { 0x00FF24, 0x00FF24, { 0x00FF44 } }, /* FF24; FF44; Case map */ + { 0x00FF25, 0x00FF25, { 0x00FF45 } }, /* FF25; FF45; Case map */ + { 0x00FF26, 0x00FF26, { 0x00FF46 } }, /* FF26; FF46; Case map */ + { 0x00FF27, 0x00FF27, { 0x00FF47 } }, /* FF27; FF47; Case map */ + { 0x00FF28, 0x00FF28, { 0x00FF48 } }, /* FF28; FF48; Case map */ + { 0x00FF29, 0x00FF29, { 0x00FF49 } }, /* FF29; FF49; Case map */ + { 0x00FF2A, 0x00FF2A, { 0x00FF4A } }, /* FF2A; FF4A; Case map */ + { 0x00FF2B, 0x00FF2B, { 0x00FF4B } }, /* FF2B; FF4B; Case map */ + { 0x00FF2C, 0x00FF2C, { 0x00FF4C } }, /* FF2C; FF4C; Case map */ + { 0x00FF2D, 0x00FF2D, { 0x00FF4D } }, /* FF2D; FF4D; Case map */ + { 0x00FF2E, 0x00FF2E, { 0x00FF4E } }, /* FF2E; FF4E; Case map */ + { 0x00FF2F, 0x00FF2F, { 0x00FF4F } }, /* FF2F; FF4F; Case map */ + { 0x00FF30, 0x00FF30, { 0x00FF50 } }, /* FF30; FF50; Case map */ + { 0x00FF31, 0x00FF31, { 0x00FF51 } }, /* FF31; FF51; Case map */ + { 0x00FF32, 0x00FF32, { 0x00FF52 } }, /* FF32; FF52; Case map */ + { 0x00FF33, 0x00FF33, { 0x00FF53 } }, /* FF33; FF53; Case map */ + { 0x00FF34, 0x00FF34, { 0x00FF54 } }, /* FF34; FF54; Case map */ + { 0x00FF35, 0x00FF35, { 0x00FF55 } }, /* FF35; FF55; Case map */ + { 0x00FF36, 0x00FF36, { 0x00FF56 } }, /* FF36; FF56; Case map */ + { 0x00FF37, 0x00FF37, { 0x00FF57 } }, /* FF37; FF57; Case map */ + { 0x00FF38, 0x00FF38, { 0x00FF58 } }, /* FF38; FF58; Case map */ + { 0x00FF39, 0x00FF39, { 0x00FF59 } }, /* FF39; FF59; Case map */ + { 0x00FF3A, 0x00FF3A, { 0x00FF5A } }, /* FF3A; FF5A; Case map */ + { 0x010400, 0x010400, { 0x010428 } }, /* 10400; 10428; Case map */ + { 0x010401, 0x010401, { 0x010429 } }, /* 10401; 10429; Case map */ + { 0x010402, 0x010402, { 0x01042A } }, /* 10402; 1042A; Case map */ + { 0x010403, 0x010403, { 0x01042B } }, /* 10403; 1042B; Case map */ + { 0x010404, 0x010404, { 0x01042C } }, /* 10404; 1042C; Case map */ + { 0x010405, 0x010405, { 0x01042D } }, /* 10405; 1042D; Case map */ + { 0x010406, 0x010406, { 0x01042E } }, /* 10406; 1042E; Case map */ + { 0x010407, 0x010407, { 0x01042F } }, /* 10407; 1042F; Case map */ + { 0x010408, 0x010408, { 0x010430 } }, /* 10408; 10430; Case map */ + { 0x010409, 0x010409, { 0x010431 } }, /* 10409; 10431; Case map */ + { 0x01040A, 0x01040A, { 0x010432 } }, /* 1040A; 10432; Case map */ + { 0x01040B, 0x01040B, { 0x010433 } }, /* 1040B; 10433; Case map */ + { 0x01040C, 0x01040C, { 0x010434 } }, /* 1040C; 10434; Case map */ + { 0x01040D, 0x01040D, { 0x010435 } }, /* 1040D; 10435; Case map */ + { 0x01040E, 0x01040E, { 0x010436 } }, /* 1040E; 10436; Case map */ + { 0x01040F, 0x01040F, { 0x010437 } }, /* 1040F; 10437; Case map */ + { 0x010410, 0x010410, { 0x010438 } }, /* 10410; 10438; Case map */ + { 0x010411, 0x010411, { 0x010439 } }, /* 10411; 10439; Case map */ + { 0x010412, 0x010412, { 0x01043A } }, /* 10412; 1043A; Case map */ + { 0x010413, 0x010413, { 0x01043B } }, /* 10413; 1043B; Case map */ + { 0x010414, 0x010414, { 0x01043C } }, /* 10414; 1043C; Case map */ + { 0x010415, 0x010415, { 0x01043D } }, /* 10415; 1043D; Case map */ + { 0x010416, 0x010416, { 0x01043E } }, /* 10416; 1043E; Case map */ + { 0x010417, 0x010417, { 0x01043F } }, /* 10417; 1043F; Case map */ + { 0x010418, 0x010418, { 0x010440 } }, /* 10418; 10440; Case map */ + { 0x010419, 0x010419, { 0x010441 } }, /* 10419; 10441; Case map */ + { 0x01041A, 0x01041A, { 0x010442 } }, /* 1041A; 10442; Case map */ + { 0x01041B, 0x01041B, { 0x010443 } }, /* 1041B; 10443; Case map */ + { 0x01041C, 0x01041C, { 0x010444 } }, /* 1041C; 10444; Case map */ + { 0x01041D, 0x01041D, { 0x010445 } }, /* 1041D; 10445; Case map */ + { 0x01041E, 0x01041E, { 0x010446 } }, /* 1041E; 10446; Case map */ + { 0x01041F, 0x01041F, { 0x010447 } }, /* 1041F; 10447; Case map */ + { 0x010420, 0x010420, { 0x010448 } }, /* 10420; 10448; Case map */ + { 0x010421, 0x010421, { 0x010449 } }, /* 10421; 10449; Case map */ + { 0x010422, 0x010422, { 0x01044A } }, /* 10422; 1044A; Case map */ + { 0x010423, 0x010423, { 0x01044B } }, /* 10423; 1044B; Case map */ + { 0x010424, 0x010424, { 0x01044C } }, /* 10424; 1044C; Case map */ + { 0x010425, 0x010425, { 0x01044D } }, /* 10425; 1044D; Case map */ + { 0x01D400, 0x01D400, { 0x000061 } }, /* 1D400; 0061; Additional folding */ + { 0x01D401, 0x01D401, { 0x000062 } }, /* 1D401; 0062; Additional folding */ + { 0x01D402, 0x01D402, { 0x000063 } }, /* 1D402; 0063; Additional folding */ + { 0x01D403, 0x01D403, { 0x000064 } }, /* 1D403; 0064; Additional folding */ + { 0x01D404, 0x01D404, { 0x000065 } }, /* 1D404; 0065; Additional folding */ + { 0x01D405, 0x01D405, { 0x000066 } }, /* 1D405; 0066; Additional folding */ + { 0x01D406, 0x01D406, { 0x000067 } }, /* 1D406; 0067; Additional folding */ + { 0x01D407, 0x01D407, { 0x000068 } }, /* 1D407; 0068; Additional folding */ + { 0x01D408, 0x01D408, { 0x000069 } }, /* 1D408; 0069; Additional folding */ + { 0x01D409, 0x01D409, { 0x00006A } }, /* 1D409; 006A; Additional folding */ + { 0x01D40A, 0x01D40A, { 0x00006B } }, /* 1D40A; 006B; Additional folding */ + { 0x01D40B, 0x01D40B, { 0x00006C } }, /* 1D40B; 006C; Additional folding */ + { 0x01D40C, 0x01D40C, { 0x00006D } }, /* 1D40C; 006D; Additional folding */ + { 0x01D40D, 0x01D40D, { 0x00006E } }, /* 1D40D; 006E; Additional folding */ + { 0x01D40E, 0x01D40E, { 0x00006F } }, /* 1D40E; 006F; Additional folding */ + { 0x01D40F, 0x01D40F, { 0x000070 } }, /* 1D40F; 0070; Additional folding */ + { 0x01D410, 0x01D410, { 0x000071 } }, /* 1D410; 0071; Additional folding */ + { 0x01D411, 0x01D411, { 0x000072 } }, /* 1D411; 0072; Additional folding */ + { 0x01D412, 0x01D412, { 0x000073 } }, /* 1D412; 0073; Additional folding */ + { 0x01D413, 0x01D413, { 0x000074 } }, /* 1D413; 0074; Additional folding */ + { 0x01D414, 0x01D414, { 0x000075 } }, /* 1D414; 0075; Additional folding */ + { 0x01D415, 0x01D415, { 0x000076 } }, /* 1D415; 0076; Additional folding */ + { 0x01D416, 0x01D416, { 0x000077 } }, /* 1D416; 0077; Additional folding */ + { 0x01D417, 0x01D417, { 0x000078 } }, /* 1D417; 0078; Additional folding */ + { 0x01D418, 0x01D418, { 0x000079 } }, /* 1D418; 0079; Additional folding */ + { 0x01D419, 0x01D419, { 0x00007A } }, /* 1D419; 007A; Additional folding */ + { 0x01D434, 0x01D434, { 0x000061 } }, /* 1D434; 0061; Additional folding */ + { 0x01D435, 0x01D435, { 0x000062 } }, /* 1D435; 0062; Additional folding */ + { 0x01D436, 0x01D436, { 0x000063 } }, /* 1D436; 0063; Additional folding */ + { 0x01D437, 0x01D437, { 0x000064 } }, /* 1D437; 0064; Additional folding */ + { 0x01D438, 0x01D438, { 0x000065 } }, /* 1D438; 0065; Additional folding */ + { 0x01D439, 0x01D439, { 0x000066 } }, /* 1D439; 0066; Additional folding */ + { 0x01D43A, 0x01D43A, { 0x000067 } }, /* 1D43A; 0067; Additional folding */ + { 0x01D43B, 0x01D43B, { 0x000068 } }, /* 1D43B; 0068; Additional folding */ + { 0x01D43C, 0x01D43C, { 0x000069 } }, /* 1D43C; 0069; Additional folding */ + { 0x01D43D, 0x01D43D, { 0x00006A } }, /* 1D43D; 006A; Additional folding */ + { 0x01D43E, 0x01D43E, { 0x00006B } }, /* 1D43E; 006B; Additional folding */ + { 0x01D43F, 0x01D43F, { 0x00006C } }, /* 1D43F; 006C; Additional folding */ + { 0x01D440, 0x01D440, { 0x00006D } }, /* 1D440; 006D; Additional folding */ + { 0x01D441, 0x01D441, { 0x00006E } }, /* 1D441; 006E; Additional folding */ + { 0x01D442, 0x01D442, { 0x00006F } }, /* 1D442; 006F; Additional folding */ + { 0x01D443, 0x01D443, { 0x000070 } }, /* 1D443; 0070; Additional folding */ + { 0x01D444, 0x01D444, { 0x000071 } }, /* 1D444; 0071; Additional folding */ + { 0x01D445, 0x01D445, { 0x000072 } }, /* 1D445; 0072; Additional folding */ + { 0x01D446, 0x01D446, { 0x000073 } }, /* 1D446; 0073; Additional folding */ + { 0x01D447, 0x01D447, { 0x000074 } }, /* 1D447; 0074; Additional folding */ + { 0x01D448, 0x01D448, { 0x000075 } }, /* 1D448; 0075; Additional folding */ + { 0x01D449, 0x01D449, { 0x000076 } }, /* 1D449; 0076; Additional folding */ + { 0x01D44A, 0x01D44A, { 0x000077 } }, /* 1D44A; 0077; Additional folding */ + { 0x01D44B, 0x01D44B, { 0x000078 } }, /* 1D44B; 0078; Additional folding */ + { 0x01D44C, 0x01D44C, { 0x000079 } }, /* 1D44C; 0079; Additional folding */ + { 0x01D44D, 0x01D44D, { 0x00007A } }, /* 1D44D; 007A; Additional folding */ + { 0x01D468, 0x01D468, { 0x000061 } }, /* 1D468; 0061; Additional folding */ + { 0x01D469, 0x01D469, { 0x000062 } }, /* 1D469; 0062; Additional folding */ + { 0x01D46A, 0x01D46A, { 0x000063 } }, /* 1D46A; 0063; Additional folding */ + { 0x01D46B, 0x01D46B, { 0x000064 } }, /* 1D46B; 0064; Additional folding */ + { 0x01D46C, 0x01D46C, { 0x000065 } }, /* 1D46C; 0065; Additional folding */ + { 0x01D46D, 0x01D46D, { 0x000066 } }, /* 1D46D; 0066; Additional folding */ + { 0x01D46E, 0x01D46E, { 0x000067 } }, /* 1D46E; 0067; Additional folding */ + { 0x01D46F, 0x01D46F, { 0x000068 } }, /* 1D46F; 0068; Additional folding */ + { 0x01D470, 0x01D470, { 0x000069 } }, /* 1D470; 0069; Additional folding */ + { 0x01D471, 0x01D471, { 0x00006A } }, /* 1D471; 006A; Additional folding */ + { 0x01D472, 0x01D472, { 0x00006B } }, /* 1D472; 006B; Additional folding */ + { 0x01D473, 0x01D473, { 0x00006C } }, /* 1D473; 006C; Additional folding */ + { 0x01D474, 0x01D474, { 0x00006D } }, /* 1D474; 006D; Additional folding */ + { 0x01D475, 0x01D475, { 0x00006E } }, /* 1D475; 006E; Additional folding */ + { 0x01D476, 0x01D476, { 0x00006F } }, /* 1D476; 006F; Additional folding */ + { 0x01D477, 0x01D477, { 0x000070 } }, /* 1D477; 0070; Additional folding */ + { 0x01D478, 0x01D478, { 0x000071 } }, /* 1D478; 0071; Additional folding */ + { 0x01D479, 0x01D479, { 0x000072 } }, /* 1D479; 0072; Additional folding */ + { 0x01D47A, 0x01D47A, { 0x000073 } }, /* 1D47A; 0073; Additional folding */ + { 0x01D47B, 0x01D47B, { 0x000074 } }, /* 1D47B; 0074; Additional folding */ + { 0x01D47C, 0x01D47C, { 0x000075 } }, /* 1D47C; 0075; Additional folding */ + { 0x01D47D, 0x01D47D, { 0x000076 } }, /* 1D47D; 0076; Additional folding */ + { 0x01D47E, 0x01D47E, { 0x000077 } }, /* 1D47E; 0077; Additional folding */ + { 0x01D47F, 0x01D47F, { 0x000078 } }, /* 1D47F; 0078; Additional folding */ + { 0x01D480, 0x01D480, { 0x000079 } }, /* 1D480; 0079; Additional folding */ + { 0x01D481, 0x01D481, { 0x00007A } }, /* 1D481; 007A; Additional folding */ + { 0x01D49C, 0x01D49C, { 0x000061 } }, /* 1D49C; 0061; Additional folding */ + { 0x01D49E, 0x01D49E, { 0x000063 } }, /* 1D49E; 0063; Additional folding */ + { 0x01D49F, 0x01D49F, { 0x000064 } }, /* 1D49F; 0064; Additional folding */ + { 0x01D4A2, 0x01D4A2, { 0x000067 } }, /* 1D4A2; 0067; Additional folding */ + { 0x01D4A5, 0x01D4A5, { 0x00006A } }, /* 1D4A5; 006A; Additional folding */ + { 0x01D4A6, 0x01D4A6, { 0x00006B } }, /* 1D4A6; 006B; Additional folding */ + { 0x01D4A9, 0x01D4A9, { 0x00006E } }, /* 1D4A9; 006E; Additional folding */ + { 0x01D4AA, 0x01D4AA, { 0x00006F } }, /* 1D4AA; 006F; Additional folding */ + { 0x01D4AB, 0x01D4AB, { 0x000070 } }, /* 1D4AB; 0070; Additional folding */ + { 0x01D4AC, 0x01D4AC, { 0x000071 } }, /* 1D4AC; 0071; Additional folding */ + { 0x01D4AE, 0x01D4AE, { 0x000073 } }, /* 1D4AE; 0073; Additional folding */ + { 0x01D4AF, 0x01D4AF, { 0x000074 } }, /* 1D4AF; 0074; Additional folding */ + { 0x01D4B0, 0x01D4B0, { 0x000075 } }, /* 1D4B0; 0075; Additional folding */ + { 0x01D4B1, 0x01D4B1, { 0x000076 } }, /* 1D4B1; 0076; Additional folding */ + { 0x01D4B2, 0x01D4B2, { 0x000077 } }, /* 1D4B2; 0077; Additional folding */ + { 0x01D4B3, 0x01D4B3, { 0x000078 } }, /* 1D4B3; 0078; Additional folding */ + { 0x01D4B4, 0x01D4B4, { 0x000079 } }, /* 1D4B4; 0079; Additional folding */ + { 0x01D4B5, 0x01D4B5, { 0x00007A } }, /* 1D4B5; 007A; Additional folding */ + { 0x01D4D0, 0x01D4D0, { 0x000061 } }, /* 1D4D0; 0061; Additional folding */ + { 0x01D4D1, 0x01D4D1, { 0x000062 } }, /* 1D4D1; 0062; Additional folding */ + { 0x01D4D2, 0x01D4D2, { 0x000063 } }, /* 1D4D2; 0063; Additional folding */ + { 0x01D4D3, 0x01D4D3, { 0x000064 } }, /* 1D4D3; 0064; Additional folding */ + { 0x01D4D4, 0x01D4D4, { 0x000065 } }, /* 1D4D4; 0065; Additional folding */ + { 0x01D4D5, 0x01D4D5, { 0x000066 } }, /* 1D4D5; 0066; Additional folding */ + { 0x01D4D6, 0x01D4D6, { 0x000067 } }, /* 1D4D6; 0067; Additional folding */ + { 0x01D4D7, 0x01D4D7, { 0x000068 } }, /* 1D4D7; 0068; Additional folding */ + { 0x01D4D8, 0x01D4D8, { 0x000069 } }, /* 1D4D8; 0069; Additional folding */ + { 0x01D4D9, 0x01D4D9, { 0x00006A } }, /* 1D4D9; 006A; Additional folding */ + { 0x01D4DA, 0x01D4DA, { 0x00006B } }, /* 1D4DA; 006B; Additional folding */ + { 0x01D4DB, 0x01D4DB, { 0x00006C } }, /* 1D4DB; 006C; Additional folding */ + { 0x01D4DC, 0x01D4DC, { 0x00006D } }, /* 1D4DC; 006D; Additional folding */ + { 0x01D4DD, 0x01D4DD, { 0x00006E } }, /* 1D4DD; 006E; Additional folding */ + { 0x01D4DE, 0x01D4DE, { 0x00006F } }, /* 1D4DE; 006F; Additional folding */ + { 0x01D4DF, 0x01D4DF, { 0x000070 } }, /* 1D4DF; 0070; Additional folding */ + { 0x01D4E0, 0x01D4E0, { 0x000071 } }, /* 1D4E0; 0071; Additional folding */ + { 0x01D4E1, 0x01D4E1, { 0x000072 } }, /* 1D4E1; 0072; Additional folding */ + { 0x01D4E2, 0x01D4E2, { 0x000073 } }, /* 1D4E2; 0073; Additional folding */ + { 0x01D4E3, 0x01D4E3, { 0x000074 } }, /* 1D4E3; 0074; Additional folding */ + { 0x01D4E4, 0x01D4E4, { 0x000075 } }, /* 1D4E4; 0075; Additional folding */ + { 0x01D4E5, 0x01D4E5, { 0x000076 } }, /* 1D4E5; 0076; Additional folding */ + { 0x01D4E6, 0x01D4E6, { 0x000077 } }, /* 1D4E6; 0077; Additional folding */ + { 0x01D4E7, 0x01D4E7, { 0x000078 } }, /* 1D4E7; 0078; Additional folding */ + { 0x01D4E8, 0x01D4E8, { 0x000079 } }, /* 1D4E8; 0079; Additional folding */ + { 0x01D4E9, 0x01D4E9, { 0x00007A } }, /* 1D4E9; 007A; Additional folding */ + { 0x01D504, 0x01D504, { 0x000061 } }, /* 1D504; 0061; Additional folding */ + { 0x01D505, 0x01D505, { 0x000062 } }, /* 1D505; 0062; Additional folding */ + { 0x01D507, 0x01D507, { 0x000064 } }, /* 1D507; 0064; Additional folding */ + { 0x01D508, 0x01D508, { 0x000065 } }, /* 1D508; 0065; Additional folding */ + { 0x01D509, 0x01D509, { 0x000066 } }, /* 1D509; 0066; Additional folding */ + { 0x01D50A, 0x01D50A, { 0x000067 } }, /* 1D50A; 0067; Additional folding */ + { 0x01D50D, 0x01D50D, { 0x00006A } }, /* 1D50D; 006A; Additional folding */ + { 0x01D50E, 0x01D50E, { 0x00006B } }, /* 1D50E; 006B; Additional folding */ + { 0x01D50F, 0x01D50F, { 0x00006C } }, /* 1D50F; 006C; Additional folding */ + { 0x01D510, 0x01D510, { 0x00006D } }, /* 1D510; 006D; Additional folding */ + { 0x01D511, 0x01D511, { 0x00006E } }, /* 1D511; 006E; Additional folding */ + { 0x01D512, 0x01D512, { 0x00006F } }, /* 1D512; 006F; Additional folding */ + { 0x01D513, 0x01D513, { 0x000070 } }, /* 1D513; 0070; Additional folding */ + { 0x01D514, 0x01D514, { 0x000071 } }, /* 1D514; 0071; Additional folding */ + { 0x01D516, 0x01D516, { 0x000073 } }, /* 1D516; 0073; Additional folding */ + { 0x01D517, 0x01D517, { 0x000074 } }, /* 1D517; 0074; Additional folding */ + { 0x01D518, 0x01D518, { 0x000075 } }, /* 1D518; 0075; Additional folding */ + { 0x01D519, 0x01D519, { 0x000076 } }, /* 1D519; 0076; Additional folding */ + { 0x01D51A, 0x01D51A, { 0x000077 } }, /* 1D51A; 0077; Additional folding */ + { 0x01D51B, 0x01D51B, { 0x000078 } }, /* 1D51B; 0078; Additional folding */ + { 0x01D51C, 0x01D51C, { 0x000079 } }, /* 1D51C; 0079; Additional folding */ + { 0x01D538, 0x01D538, { 0x000061 } }, /* 1D538; 0061; Additional folding */ + { 0x01D539, 0x01D539, { 0x000062 } }, /* 1D539; 0062; Additional folding */ + { 0x01D53B, 0x01D53B, { 0x000064 } }, /* 1D53B; 0064; Additional folding */ + { 0x01D53C, 0x01D53C, { 0x000065 } }, /* 1D53C; 0065; Additional folding */ + { 0x01D53D, 0x01D53D, { 0x000066 } }, /* 1D53D; 0066; Additional folding */ + { 0x01D53E, 0x01D53E, { 0x000067 } }, /* 1D53E; 0067; Additional folding */ + { 0x01D540, 0x01D540, { 0x000069 } }, /* 1D540; 0069; Additional folding */ + { 0x01D541, 0x01D541, { 0x00006A } }, /* 1D541; 006A; Additional folding */ + { 0x01D542, 0x01D542, { 0x00006B } }, /* 1D542; 006B; Additional folding */ + { 0x01D543, 0x01D543, { 0x00006C } }, /* 1D543; 006C; Additional folding */ + { 0x01D544, 0x01D544, { 0x00006D } }, /* 1D544; 006D; Additional folding */ + { 0x01D546, 0x01D546, { 0x00006F } }, /* 1D546; 006F; Additional folding */ + { 0x01D54A, 0x01D54A, { 0x000073 } }, /* 1D54A; 0073; Additional folding */ + { 0x01D54B, 0x01D54B, { 0x000074 } }, /* 1D54B; 0074; Additional folding */ + { 0x01D54C, 0x01D54C, { 0x000075 } }, /* 1D54C; 0075; Additional folding */ + { 0x01D54D, 0x01D54D, { 0x000076 } }, /* 1D54D; 0076; Additional folding */ + { 0x01D54E, 0x01D54E, { 0x000077 } }, /* 1D54E; 0077; Additional folding */ + { 0x01D54F, 0x01D54F, { 0x000078 } }, /* 1D54F; 0078; Additional folding */ + { 0x01D550, 0x01D550, { 0x000079 } }, /* 1D550; 0079; Additional folding */ + { 0x01D56C, 0x01D56C, { 0x000061 } }, /* 1D56C; 0061; Additional folding */ + { 0x01D56D, 0x01D56D, { 0x000062 } }, /* 1D56D; 0062; Additional folding */ + { 0x01D56E, 0x01D56E, { 0x000063 } }, /* 1D56E; 0063; Additional folding */ + { 0x01D56F, 0x01D56F, { 0x000064 } }, /* 1D56F; 0064; Additional folding */ + { 0x01D570, 0x01D570, { 0x000065 } }, /* 1D570; 0065; Additional folding */ + { 0x01D571, 0x01D571, { 0x000066 } }, /* 1D571; 0066; Additional folding */ + { 0x01D572, 0x01D572, { 0x000067 } }, /* 1D572; 0067; Additional folding */ + { 0x01D573, 0x01D573, { 0x000068 } }, /* 1D573; 0068; Additional folding */ + { 0x01D574, 0x01D574, { 0x000069 } }, /* 1D574; 0069; Additional folding */ + { 0x01D575, 0x01D575, { 0x00006A } }, /* 1D575; 006A; Additional folding */ + { 0x01D576, 0x01D576, { 0x00006B } }, /* 1D576; 006B; Additional folding */ + { 0x01D577, 0x01D577, { 0x00006C } }, /* 1D577; 006C; Additional folding */ + { 0x01D578, 0x01D578, { 0x00006D } }, /* 1D578; 006D; Additional folding */ + { 0x01D579, 0x01D579, { 0x00006E } }, /* 1D579; 006E; Additional folding */ + { 0x01D57A, 0x01D57A, { 0x00006F } }, /* 1D57A; 006F; Additional folding */ + { 0x01D57B, 0x01D57B, { 0x000070 } }, /* 1D57B; 0070; Additional folding */ + { 0x01D57C, 0x01D57C, { 0x000071 } }, /* 1D57C; 0071; Additional folding */ + { 0x01D57D, 0x01D57D, { 0x000072 } }, /* 1D57D; 0072; Additional folding */ + { 0x01D57E, 0x01D57E, { 0x000073 } }, /* 1D57E; 0073; Additional folding */ + { 0x01D57F, 0x01D57F, { 0x000074 } }, /* 1D57F; 0074; Additional folding */ + { 0x01D580, 0x01D580, { 0x000075 } }, /* 1D580; 0075; Additional folding */ + { 0x01D581, 0x01D581, { 0x000076 } }, /* 1D581; 0076; Additional folding */ + { 0x01D582, 0x01D582, { 0x000077 } }, /* 1D582; 0077; Additional folding */ + { 0x01D583, 0x01D583, { 0x000078 } }, /* 1D583; 0078; Additional folding */ + { 0x01D584, 0x01D584, { 0x000079 } }, /* 1D584; 0079; Additional folding */ + { 0x01D585, 0x01D585, { 0x00007A } }, /* 1D585; 007A; Additional folding */ + { 0x01D5A0, 0x01D5A0, { 0x000061 } }, /* 1D5A0; 0061; Additional folding */ + { 0x01D5A1, 0x01D5A1, { 0x000062 } }, /* 1D5A1; 0062; Additional folding */ + { 0x01D5A2, 0x01D5A2, { 0x000063 } }, /* 1D5A2; 0063; Additional folding */ + { 0x01D5A3, 0x01D5A3, { 0x000064 } }, /* 1D5A3; 0064; Additional folding */ + { 0x01D5A4, 0x01D5A4, { 0x000065 } }, /* 1D5A4; 0065; Additional folding */ + { 0x01D5A5, 0x01D5A5, { 0x000066 } }, /* 1D5A5; 0066; Additional folding */ + { 0x01D5A6, 0x01D5A6, { 0x000067 } }, /* 1D5A6; 0067; Additional folding */ + { 0x01D5A7, 0x01D5A7, { 0x000068 } }, /* 1D5A7; 0068; Additional folding */ + { 0x01D5A8, 0x01D5A8, { 0x000069 } }, /* 1D5A8; 0069; Additional folding */ + { 0x01D5A9, 0x01D5A9, { 0x00006A } }, /* 1D5A9; 006A; Additional folding */ + { 0x01D5AA, 0x01D5AA, { 0x00006B } }, /* 1D5AA; 006B; Additional folding */ + { 0x01D5AB, 0x01D5AB, { 0x00006C } }, /* 1D5AB; 006C; Additional folding */ + { 0x01D5AC, 0x01D5AC, { 0x00006D } }, /* 1D5AC; 006D; Additional folding */ + { 0x01D5AD, 0x01D5AD, { 0x00006E } }, /* 1D5AD; 006E; Additional folding */ + { 0x01D5AE, 0x01D5AE, { 0x00006F } }, /* 1D5AE; 006F; Additional folding */ + { 0x01D5AF, 0x01D5AF, { 0x000070 } }, /* 1D5AF; 0070; Additional folding */ + { 0x01D5B0, 0x01D5B0, { 0x000071 } }, /* 1D5B0; 0071; Additional folding */ + { 0x01D5B1, 0x01D5B1, { 0x000072 } }, /* 1D5B1; 0072; Additional folding */ + { 0x01D5B2, 0x01D5B2, { 0x000073 } }, /* 1D5B2; 0073; Additional folding */ + { 0x01D5B3, 0x01D5B3, { 0x000074 } }, /* 1D5B3; 0074; Additional folding */ + { 0x01D5B4, 0x01D5B4, { 0x000075 } }, /* 1D5B4; 0075; Additional folding */ + { 0x01D5B5, 0x01D5B5, { 0x000076 } }, /* 1D5B5; 0076; Additional folding */ + { 0x01D5B6, 0x01D5B6, { 0x000077 } }, /* 1D5B6; 0077; Additional folding */ + { 0x01D5B7, 0x01D5B7, { 0x000078 } }, /* 1D5B7; 0078; Additional folding */ + { 0x01D5B8, 0x01D5B8, { 0x000079 } }, /* 1D5B8; 0079; Additional folding */ + { 0x01D5B9, 0x01D5B9, { 0x00007A } }, /* 1D5B9; 007A; Additional folding */ + { 0x01D5D4, 0x01D5D4, { 0x000061 } }, /* 1D5D4; 0061; Additional folding */ + { 0x01D5D5, 0x01D5D5, { 0x000062 } }, /* 1D5D5; 0062; Additional folding */ + { 0x01D5D6, 0x01D5D6, { 0x000063 } }, /* 1D5D6; 0063; Additional folding */ + { 0x01D5D7, 0x01D5D7, { 0x000064 } }, /* 1D5D7; 0064; Additional folding */ + { 0x01D5D8, 0x01D5D8, { 0x000065 } }, /* 1D5D8; 0065; Additional folding */ + { 0x01D5D9, 0x01D5D9, { 0x000066 } }, /* 1D5D9; 0066; Additional folding */ + { 0x01D5DA, 0x01D5DA, { 0x000067 } }, /* 1D5DA; 0067; Additional folding */ + { 0x01D5DB, 0x01D5DB, { 0x000068 } }, /* 1D5DB; 0068; Additional folding */ + { 0x01D5DC, 0x01D5DC, { 0x000069 } }, /* 1D5DC; 0069; Additional folding */ + { 0x01D5DD, 0x01D5DD, { 0x00006A } }, /* 1D5DD; 006A; Additional folding */ + { 0x01D5DE, 0x01D5DE, { 0x00006B } }, /* 1D5DE; 006B; Additional folding */ + { 0x01D5DF, 0x01D5DF, { 0x00006C } }, /* 1D5DF; 006C; Additional folding */ + { 0x01D5E0, 0x01D5E0, { 0x00006D } }, /* 1D5E0; 006D; Additional folding */ + { 0x01D5E1, 0x01D5E1, { 0x00006E } }, /* 1D5E1; 006E; Additional folding */ + { 0x01D5E2, 0x01D5E2, { 0x00006F } }, /* 1D5E2; 006F; Additional folding */ + { 0x01D5E3, 0x01D5E3, { 0x000070 } }, /* 1D5E3; 0070; Additional folding */ + { 0x01D5E4, 0x01D5E4, { 0x000071 } }, /* 1D5E4; 0071; Additional folding */ + { 0x01D5E5, 0x01D5E5, { 0x000072 } }, /* 1D5E5; 0072; Additional folding */ + { 0x01D5E6, 0x01D5E6, { 0x000073 } }, /* 1D5E6; 0073; Additional folding */ + { 0x01D5E7, 0x01D5E7, { 0x000074 } }, /* 1D5E7; 0074; Additional folding */ + { 0x01D5E8, 0x01D5E8, { 0x000075 } }, /* 1D5E8; 0075; Additional folding */ + { 0x01D5E9, 0x01D5E9, { 0x000076 } }, /* 1D5E9; 0076; Additional folding */ + { 0x01D5EA, 0x01D5EA, { 0x000077 } }, /* 1D5EA; 0077; Additional folding */ + { 0x01D5EB, 0x01D5EB, { 0x000078 } }, /* 1D5EB; 0078; Additional folding */ + { 0x01D5EC, 0x01D5EC, { 0x000079 } }, /* 1D5EC; 0079; Additional folding */ + { 0x01D5ED, 0x01D5ED, { 0x00007A } }, /* 1D5ED; 007A; Additional folding */ + { 0x01D608, 0x01D608, { 0x000061 } }, /* 1D608; 0061; Additional folding */ + { 0x01D609, 0x01D609, { 0x000062 } }, /* 1D609; 0062; Additional folding */ + { 0x01D60A, 0x01D60A, { 0x000063 } }, /* 1D60A; 0063; Additional folding */ + { 0x01D60B, 0x01D60B, { 0x000064 } }, /* 1D60B; 0064; Additional folding */ + { 0x01D60C, 0x01D60C, { 0x000065 } }, /* 1D60C; 0065; Additional folding */ + { 0x01D60D, 0x01D60D, { 0x000066 } }, /* 1D60D; 0066; Additional folding */ + { 0x01D60E, 0x01D60E, { 0x000067 } }, /* 1D60E; 0067; Additional folding */ + { 0x01D60F, 0x01D60F, { 0x000068 } }, /* 1D60F; 0068; Additional folding */ + { 0x01D610, 0x01D610, { 0x000069 } }, /* 1D610; 0069; Additional folding */ + { 0x01D611, 0x01D611, { 0x00006A } }, /* 1D611; 006A; Additional folding */ + { 0x01D612, 0x01D612, { 0x00006B } }, /* 1D612; 006B; Additional folding */ + { 0x01D613, 0x01D613, { 0x00006C } }, /* 1D613; 006C; Additional folding */ + { 0x01D614, 0x01D614, { 0x00006D } }, /* 1D614; 006D; Additional folding */ + { 0x01D615, 0x01D615, { 0x00006E } }, /* 1D615; 006E; Additional folding */ + { 0x01D616, 0x01D616, { 0x00006F } }, /* 1D616; 006F; Additional folding */ + { 0x01D617, 0x01D617, { 0x000070 } }, /* 1D617; 0070; Additional folding */ + { 0x01D618, 0x01D618, { 0x000071 } }, /* 1D618; 0071; Additional folding */ + { 0x01D619, 0x01D619, { 0x000072 } }, /* 1D619; 0072; Additional folding */ + { 0x01D61A, 0x01D61A, { 0x000073 } }, /* 1D61A; 0073; Additional folding */ + { 0x01D61B, 0x01D61B, { 0x000074 } }, /* 1D61B; 0074; Additional folding */ + { 0x01D61C, 0x01D61C, { 0x000075 } }, /* 1D61C; 0075; Additional folding */ + { 0x01D61D, 0x01D61D, { 0x000076 } }, /* 1D61D; 0076; Additional folding */ + { 0x01D61E, 0x01D61E, { 0x000077 } }, /* 1D61E; 0077; Additional folding */ + { 0x01D61F, 0x01D61F, { 0x000078 } }, /* 1D61F; 0078; Additional folding */ + { 0x01D620, 0x01D620, { 0x000079 } }, /* 1D620; 0079; Additional folding */ + { 0x01D621, 0x01D621, { 0x00007A } }, /* 1D621; 007A; Additional folding */ + { 0x01D63C, 0x01D63C, { 0x000061 } }, /* 1D63C; 0061; Additional folding */ + { 0x01D63D, 0x01D63D, { 0x000062 } }, /* 1D63D; 0062; Additional folding */ + { 0x01D63E, 0x01D63E, { 0x000063 } }, /* 1D63E; 0063; Additional folding */ + { 0x01D63F, 0x01D63F, { 0x000064 } }, /* 1D63F; 0064; Additional folding */ + { 0x01D640, 0x01D640, { 0x000065 } }, /* 1D640; 0065; Additional folding */ + { 0x01D641, 0x01D641, { 0x000066 } }, /* 1D641; 0066; Additional folding */ + { 0x01D642, 0x01D642, { 0x000067 } }, /* 1D642; 0067; Additional folding */ + { 0x01D643, 0x01D643, { 0x000068 } }, /* 1D643; 0068; Additional folding */ + { 0x01D644, 0x01D644, { 0x000069 } }, /* 1D644; 0069; Additional folding */ + { 0x01D645, 0x01D645, { 0x00006A } }, /* 1D645; 006A; Additional folding */ + { 0x01D646, 0x01D646, { 0x00006B } }, /* 1D646; 006B; Additional folding */ + { 0x01D647, 0x01D647, { 0x00006C } }, /* 1D647; 006C; Additional folding */ + { 0x01D648, 0x01D648, { 0x00006D } }, /* 1D648; 006D; Additional folding */ + { 0x01D649, 0x01D649, { 0x00006E } }, /* 1D649; 006E; Additional folding */ + { 0x01D64A, 0x01D64A, { 0x00006F } }, /* 1D64A; 006F; Additional folding */ + { 0x01D64B, 0x01D64B, { 0x000070 } }, /* 1D64B; 0070; Additional folding */ + { 0x01D64C, 0x01D64C, { 0x000071 } }, /* 1D64C; 0071; Additional folding */ + { 0x01D64D, 0x01D64D, { 0x000072 } }, /* 1D64D; 0072; Additional folding */ + { 0x01D64E, 0x01D64E, { 0x000073 } }, /* 1D64E; 0073; Additional folding */ + { 0x01D64F, 0x01D64F, { 0x000074 } }, /* 1D64F; 0074; Additional folding */ + { 0x01D650, 0x01D650, { 0x000075 } }, /* 1D650; 0075; Additional folding */ + { 0x01D651, 0x01D651, { 0x000076 } }, /* 1D651; 0076; Additional folding */ + { 0x01D652, 0x01D652, { 0x000077 } }, /* 1D652; 0077; Additional folding */ + { 0x01D653, 0x01D653, { 0x000078 } }, /* 1D653; 0078; Additional folding */ + { 0x01D654, 0x01D654, { 0x000079 } }, /* 1D654; 0079; Additional folding */ + { 0x01D655, 0x01D655, { 0x00007A } }, /* 1D655; 007A; Additional folding */ + { 0x01D670, 0x01D670, { 0x000061 } }, /* 1D670; 0061; Additional folding */ + { 0x01D671, 0x01D671, { 0x000062 } }, /* 1D671; 0062; Additional folding */ + { 0x01D672, 0x01D672, { 0x000063 } }, /* 1D672; 0063; Additional folding */ + { 0x01D673, 0x01D673, { 0x000064 } }, /* 1D673; 0064; Additional folding */ + { 0x01D674, 0x01D674, { 0x000065 } }, /* 1D674; 0065; Additional folding */ + { 0x01D675, 0x01D675, { 0x000066 } }, /* 1D675; 0066; Additional folding */ + { 0x01D676, 0x01D676, { 0x000067 } }, /* 1D676; 0067; Additional folding */ + { 0x01D677, 0x01D677, { 0x000068 } }, /* 1D677; 0068; Additional folding */ + { 0x01D678, 0x01D678, { 0x000069 } }, /* 1D678; 0069; Additional folding */ + { 0x01D679, 0x01D679, { 0x00006A } }, /* 1D679; 006A; Additional folding */ + { 0x01D67A, 0x01D67A, { 0x00006B } }, /* 1D67A; 006B; Additional folding */ + { 0x01D67B, 0x01D67B, { 0x00006C } }, /* 1D67B; 006C; Additional folding */ + { 0x01D67C, 0x01D67C, { 0x00006D } }, /* 1D67C; 006D; Additional folding */ + { 0x01D67D, 0x01D67D, { 0x00006E } }, /* 1D67D; 006E; Additional folding */ + { 0x01D67E, 0x01D67E, { 0x00006F } }, /* 1D67E; 006F; Additional folding */ + { 0x01D67F, 0x01D67F, { 0x000070 } }, /* 1D67F; 0070; Additional folding */ + { 0x01D680, 0x01D680, { 0x000071 } }, /* 1D680; 0071; Additional folding */ + { 0x01D681, 0x01D681, { 0x000072 } }, /* 1D681; 0072; Additional folding */ + { 0x01D682, 0x01D682, { 0x000073 } }, /* 1D682; 0073; Additional folding */ + { 0x01D683, 0x01D683, { 0x000074 } }, /* 1D683; 0074; Additional folding */ + { 0x01D684, 0x01D684, { 0x000075 } }, /* 1D684; 0075; Additional folding */ + { 0x01D685, 0x01D685, { 0x000076 } }, /* 1D685; 0076; Additional folding */ + { 0x01D686, 0x01D686, { 0x000077 } }, /* 1D686; 0077; Additional folding */ + { 0x01D687, 0x01D687, { 0x000078 } }, /* 1D687; 0078; Additional folding */ + { 0x01D688, 0x01D688, { 0x000079 } }, /* 1D688; 0079; Additional folding */ + { 0x01D689, 0x01D689, { 0x00007A } }, /* 1D689; 007A; Additional folding */ + { 0x01D6A8, 0x01D6A8, { 0x0003B1 } }, /* 1D6A8; 03B1; Additional folding */ + { 0x01D6A9, 0x01D6A9, { 0x0003B2 } }, /* 1D6A9; 03B2; Additional folding */ + { 0x01D6AA, 0x01D6AA, { 0x0003B3 } }, /* 1D6AA; 03B3; Additional folding */ + { 0x01D6AB, 0x01D6AB, { 0x0003B4 } }, /* 1D6AB; 03B4; Additional folding */ + { 0x01D6AC, 0x01D6AC, { 0x0003B5 } }, /* 1D6AC; 03B5; Additional folding */ + { 0x01D6AD, 0x01D6AD, { 0x0003B6 } }, /* 1D6AD; 03B6; Additional folding */ + { 0x01D6AE, 0x01D6AE, { 0x0003B7 } }, /* 1D6AE; 03B7; Additional folding */ + { 0x01D6AF, 0x01D6AF, { 0x0003B8 } }, /* 1D6AF; 03B8; Additional folding */ + { 0x01D6B0, 0x01D6B0, { 0x0003B9 } }, /* 1D6B0; 03B9; Additional folding */ + { 0x01D6B1, 0x01D6B1, { 0x0003BA } }, /* 1D6B1; 03BA; Additional folding */ + { 0x01D6B2, 0x01D6B2, { 0x0003BB } }, /* 1D6B2; 03BB; Additional folding */ + { 0x01D6B3, 0x01D6B3, { 0x0003BC } }, /* 1D6B3; 03BC; Additional folding */ + { 0x01D6B4, 0x01D6B4, { 0x0003BD } }, /* 1D6B4; 03BD; Additional folding */ + { 0x01D6B5, 0x01D6B5, { 0x0003BE } }, /* 1D6B5; 03BE; Additional folding */ + { 0x01D6B6, 0x01D6B6, { 0x0003BF } }, /* 1D6B6; 03BF; Additional folding */ + { 0x01D6B7, 0x01D6B7, { 0x0003C0 } }, /* 1D6B7; 03C0; Additional folding */ + { 0x01D6B8, 0x01D6B8, { 0x0003C1 } }, /* 1D6B8; 03C1; Additional folding */ + { 0x01D6B9, 0x01D6B9, { 0x0003B8 } }, /* 1D6B9; 03B8; Additional folding */ + { 0x01D6BA, 0x01D6BA, { 0x0003C3 } }, /* 1D6BA; 03C3; Additional folding */ + { 0x01D6BB, 0x01D6BB, { 0x0003C4 } }, /* 1D6BB; 03C4; Additional folding */ + { 0x01D6BC, 0x01D6BC, { 0x0003C5 } }, /* 1D6BC; 03C5; Additional folding */ + { 0x01D6BD, 0x01D6BD, { 0x0003C6 } }, /* 1D6BD; 03C6; Additional folding */ + { 0x01D6BE, 0x01D6BE, { 0x0003C7 } }, /* 1D6BE; 03C7; Additional folding */ + { 0x01D6BF, 0x01D6BF, { 0x0003C8 } }, /* 1D6BF; 03C8; Additional folding */ + { 0x01D6C0, 0x01D6C0, { 0x0003C9 } }, /* 1D6C0; 03C9; Additional folding */ + { 0x01D6D3, 0x01D6D3, { 0x0003C3 } }, /* 1D6D3; 03C3; Additional folding */ + { 0x01D6E2, 0x01D6E2, { 0x0003B1 } }, /* 1D6E2; 03B1; Additional folding */ + { 0x01D6E3, 0x01D6E3, { 0x0003B2 } }, /* 1D6E3; 03B2; Additional folding */ + { 0x01D6E4, 0x01D6E4, { 0x0003B3 } }, /* 1D6E4; 03B3; Additional folding */ + { 0x01D6E5, 0x01D6E5, { 0x0003B4 } }, /* 1D6E5; 03B4; Additional folding */ + { 0x01D6E6, 0x01D6E6, { 0x0003B5 } }, /* 1D6E6; 03B5; Additional folding */ + { 0x01D6E7, 0x01D6E7, { 0x0003B6 } }, /* 1D6E7; 03B6; Additional folding */ + { 0x01D6E8, 0x01D6E8, { 0x0003B7 } }, /* 1D6E8; 03B7; Additional folding */ + { 0x01D6E9, 0x01D6E9, { 0x0003B8 } }, /* 1D6E9; 03B8; Additional folding */ + { 0x01D6EA, 0x01D6EA, { 0x0003B9 } }, /* 1D6EA; 03B9; Additional folding */ + { 0x01D6EB, 0x01D6EB, { 0x0003BA } }, /* 1D6EB; 03BA; Additional folding */ + { 0x01D6EC, 0x01D6EC, { 0x0003BB } }, /* 1D6EC; 03BB; Additional folding */ + { 0x01D6ED, 0x01D6ED, { 0x0003BC } }, /* 1D6ED; 03BC; Additional folding */ + { 0x01D6EE, 0x01D6EE, { 0x0003BD } }, /* 1D6EE; 03BD; Additional folding */ + { 0x01D6EF, 0x01D6EF, { 0x0003BE } }, /* 1D6EF; 03BE; Additional folding */ + { 0x01D6F0, 0x01D6F0, { 0x0003BF } }, /* 1D6F0; 03BF; Additional folding */ + { 0x01D6F1, 0x01D6F1, { 0x0003C0 } }, /* 1D6F1; 03C0; Additional folding */ + { 0x01D6F2, 0x01D6F2, { 0x0003C1 } }, /* 1D6F2; 03C1; Additional folding */ + { 0x01D6F3, 0x01D6F3, { 0x0003B8 } }, /* 1D6F3; 03B8; Additional folding */ + { 0x01D6F4, 0x01D6F4, { 0x0003C3 } }, /* 1D6F4; 03C3; Additional folding */ + { 0x01D6F5, 0x01D6F5, { 0x0003C4 } }, /* 1D6F5; 03C4; Additional folding */ + { 0x01D6F6, 0x01D6F6, { 0x0003C5 } }, /* 1D6F6; 03C5; Additional folding */ + { 0x01D6F7, 0x01D6F7, { 0x0003C6 } }, /* 1D6F7; 03C6; Additional folding */ + { 0x01D6F8, 0x01D6F8, { 0x0003C7 } }, /* 1D6F8; 03C7; Additional folding */ + { 0x01D6F9, 0x01D6F9, { 0x0003C8 } }, /* 1D6F9; 03C8; Additional folding */ + { 0x01D6FA, 0x01D6FA, { 0x0003C9 } }, /* 1D6FA; 03C9; Additional folding */ + { 0x01D70D, 0x01D70D, { 0x0003C3 } }, /* 1D70D; 03C3; Additional folding */ + { 0x01D71C, 0x01D71C, { 0x0003B1 } }, /* 1D71C; 03B1; Additional folding */ + { 0x01D71D, 0x01D71D, { 0x0003B2 } }, /* 1D71D; 03B2; Additional folding */ + { 0x01D71E, 0x01D71E, { 0x0003B3 } }, /* 1D71E; 03B3; Additional folding */ + { 0x01D71F, 0x01D71F, { 0x0003B4 } }, /* 1D71F; 03B4; Additional folding */ + { 0x01D720, 0x01D720, { 0x0003B5 } }, /* 1D720; 03B5; Additional folding */ + { 0x01D721, 0x01D721, { 0x0003B6 } }, /* 1D721; 03B6; Additional folding */ + { 0x01D722, 0x01D722, { 0x0003B7 } }, /* 1D722; 03B7; Additional folding */ + { 0x01D723, 0x01D723, { 0x0003B8 } }, /* 1D723; 03B8; Additional folding */ + { 0x01D724, 0x01D724, { 0x0003B9 } }, /* 1D724; 03B9; Additional folding */ + { 0x01D725, 0x01D725, { 0x0003BA } }, /* 1D725; 03BA; Additional folding */ + { 0x01D726, 0x01D726, { 0x0003BB } }, /* 1D726; 03BB; Additional folding */ + { 0x01D727, 0x01D727, { 0x0003BC } }, /* 1D727; 03BC; Additional folding */ + { 0x01D728, 0x01D728, { 0x0003BD } }, /* 1D728; 03BD; Additional folding */ + { 0x01D729, 0x01D729, { 0x0003BE } }, /* 1D729; 03BE; Additional folding */ + { 0x01D72A, 0x01D72A, { 0x0003BF } }, /* 1D72A; 03BF; Additional folding */ + { 0x01D72B, 0x01D72B, { 0x0003C0 } }, /* 1D72B; 03C0; Additional folding */ + { 0x01D72C, 0x01D72C, { 0x0003C1 } }, /* 1D72C; 03C1; Additional folding */ + { 0x01D72D, 0x01D72D, { 0x0003B8 } }, /* 1D72D; 03B8; Additional folding */ + { 0x01D72E, 0x01D72E, { 0x0003C3 } }, /* 1D72E; 03C3; Additional folding */ + { 0x01D72F, 0x01D72F, { 0x0003C4 } }, /* 1D72F; 03C4; Additional folding */ + { 0x01D730, 0x01D730, { 0x0003C5 } }, /* 1D730; 03C5; Additional folding */ + { 0x01D731, 0x01D731, { 0x0003C6 } }, /* 1D731; 03C6; Additional folding */ + { 0x01D732, 0x01D732, { 0x0003C7 } }, /* 1D732; 03C7; Additional folding */ + { 0x01D733, 0x01D733, { 0x0003C8 } }, /* 1D733; 03C8; Additional folding */ + { 0x01D734, 0x01D734, { 0x0003C9 } }, /* 1D734; 03C9; Additional folding */ + { 0x01D747, 0x01D747, { 0x0003C3 } }, /* 1D747; 03C3; Additional folding */ + { 0x01D756, 0x01D756, { 0x0003B1 } }, /* 1D756; 03B1; Additional folding */ + { 0x01D757, 0x01D757, { 0x0003B2 } }, /* 1D757; 03B2; Additional folding */ + { 0x01D758, 0x01D758, { 0x0003B3 } }, /* 1D758; 03B3; Additional folding */ + { 0x01D759, 0x01D759, { 0x0003B4 } }, /* 1D759; 03B4; Additional folding */ + { 0x01D75A, 0x01D75A, { 0x0003B5 } }, /* 1D75A; 03B5; Additional folding */ + { 0x01D75B, 0x01D75B, { 0x0003B6 } }, /* 1D75B; 03B6; Additional folding */ + { 0x01D75C, 0x01D75C, { 0x0003B7 } }, /* 1D75C; 03B7; Additional folding */ + { 0x01D75D, 0x01D75D, { 0x0003B8 } }, /* 1D75D; 03B8; Additional folding */ + { 0x01D75E, 0x01D75E, { 0x0003B9 } }, /* 1D75E; 03B9; Additional folding */ + { 0x01D75F, 0x01D75F, { 0x0003BA } }, /* 1D75F; 03BA; Additional folding */ + { 0x01D760, 0x01D760, { 0x0003BB } }, /* 1D760; 03BB; Additional folding */ + { 0x01D761, 0x01D761, { 0x0003BC } }, /* 1D761; 03BC; Additional folding */ + { 0x01D762, 0x01D762, { 0x0003BD } }, /* 1D762; 03BD; Additional folding */ + { 0x01D763, 0x01D763, { 0x0003BE } }, /* 1D763; 03BE; Additional folding */ + { 0x01D764, 0x01D764, { 0x0003BF } }, /* 1D764; 03BF; Additional folding */ + { 0x01D765, 0x01D765, { 0x0003C0 } }, /* 1D765; 03C0; Additional folding */ + { 0x01D766, 0x01D766, { 0x0003C1 } }, /* 1D766; 03C1; Additional folding */ + { 0x01D767, 0x01D767, { 0x0003B8 } }, /* 1D767; 03B8; Additional folding */ + { 0x01D768, 0x01D768, { 0x0003C3 } }, /* 1D768; 03C3; Additional folding */ + { 0x01D769, 0x01D769, { 0x0003C4 } }, /* 1D769; 03C4; Additional folding */ + { 0x01D76A, 0x01D76A, { 0x0003C5 } }, /* 1D76A; 03C5; Additional folding */ + { 0x01D76B, 0x01D76B, { 0x0003C6 } }, /* 1D76B; 03C6; Additional folding */ + { 0x01D76C, 0x01D76C, { 0x0003C7 } }, /* 1D76C; 03C7; Additional folding */ + { 0x01D76D, 0x01D76D, { 0x0003C8 } }, /* 1D76D; 03C8; Additional folding */ + { 0x01D76E, 0x01D76E, { 0x0003C9 } }, /* 1D76E; 03C9; Additional folding */ + { 0x01D781, 0x01D781, { 0x0003C3 } }, /* 1D781; 03C3; Additional folding */ + { 0x01D790, 0x01D790, { 0x0003B1 } }, /* 1D790; 03B1; Additional folding */ + { 0x01D791, 0x01D791, { 0x0003B2 } }, /* 1D791; 03B2; Additional folding */ + { 0x01D792, 0x01D792, { 0x0003B3 } }, /* 1D792; 03B3; Additional folding */ + { 0x01D793, 0x01D793, { 0x0003B4 } }, /* 1D793; 03B4; Additional folding */ + { 0x01D794, 0x01D794, { 0x0003B5 } }, /* 1D794; 03B5; Additional folding */ + { 0x01D795, 0x01D795, { 0x0003B6 } }, /* 1D795; 03B6; Additional folding */ + { 0x01D796, 0x01D796, { 0x0003B7 } }, /* 1D796; 03B7; Additional folding */ + { 0x01D797, 0x01D797, { 0x0003B8 } }, /* 1D797; 03B8; Additional folding */ + { 0x01D798, 0x01D798, { 0x0003B9 } }, /* 1D798; 03B9; Additional folding */ + { 0x01D799, 0x01D799, { 0x0003BA } }, /* 1D799; 03BA; Additional folding */ + { 0x01D79A, 0x01D79A, { 0x0003BB } }, /* 1D79A; 03BB; Additional folding */ + { 0x01D79B, 0x01D79B, { 0x0003BC } }, /* 1D79B; 03BC; Additional folding */ + { 0x01D79C, 0x01D79C, { 0x0003BD } }, /* 1D79C; 03BD; Additional folding */ + { 0x01D79D, 0x01D79D, { 0x0003BE } }, /* 1D79D; 03BE; Additional folding */ + { 0x01D79E, 0x01D79E, { 0x0003BF } }, /* 1D79E; 03BF; Additional folding */ + { 0x01D79F, 0x01D79F, { 0x0003C0 } }, /* 1D79F; 03C0; Additional folding */ + { 0x01D7A0, 0x01D7A0, { 0x0003C1 } }, /* 1D7A0; 03C1; Additional folding */ + { 0x01D7A1, 0x01D7A1, { 0x0003B8 } }, /* 1D7A1; 03B8; Additional folding */ + { 0x01D7A2, 0x01D7A2, { 0x0003C3 } }, /* 1D7A2; 03C3; Additional folding */ + { 0x01D7A3, 0x01D7A3, { 0x0003C4 } }, /* 1D7A3; 03C4; Additional folding */ + { 0x01D7A4, 0x01D7A4, { 0x0003C5 } }, /* 1D7A4; 03C5; Additional folding */ + { 0x01D7A5, 0x01D7A5, { 0x0003C6 } }, /* 1D7A5; 03C6; Additional folding */ + { 0x01D7A6, 0x01D7A6, { 0x0003C7 } }, /* 1D7A6; 03C7; Additional folding */ + { 0x01D7A7, 0x01D7A7, { 0x0003C8 } }, /* 1D7A7; 03C8; Additional folding */ + { 0x01D7A8, 0x01D7A8, { 0x0003C9 } }, /* 1D7A8; 03C9; Additional folding */ + { 0x01D7BB, 0x01D7BB, { 0x0003C3 } }, /* 1D7BB; 03C3; Additional folding */ + { 0 }, +}; + +/* + * FF3A; FF5A; Case map + * 10400; 10428; Case map +10401; 10429; Case map +10402; 1042A; Case map +10403; 1042B; Case map +10404; 1042C; Case map +10405; 1042D; Case map +10406; 1042E; Case map +10407; 1042F; Case map +10408; 10430; Case map +10409; 10431; Case map +1040A; 10432; Case map +1040B; 10433; Case map +1040C; 10434; Case map +1040D; 10435; Case map +1040E; 10436; Case map +1040F; 10437; Case map +10410; 10438; Case map +10411; 10439; Case map +10412; 1043A; Case map +10413; 1043B; Case map +10414; 1043C; Case map +10415; 1043D; Case map +10416; 1043E; Case map +10417; 1043F; Case map +10418; 10440; Case map +10419; 10441; Case map +1041A; 10442; Case map +1041B; 10443; Case map +1041C; 10444; Case map +1041D; 10445; Case map +1041E; 10446; Case map +1041F; 10447; Case map +10420; 10448; Case map +10421; 10449; Case map +10422; 1044A; Case map +10423; 1044B; Case map +10424; 1044C; Case map +10425; 1044D; Case map +1D400; 0061; Additional folding +1D401; 0062; Additional folding +1D402; 0063; Additional folding +1D403; 0064; Additional folding +1D404; 0065; Additional folding +1D405; 0066; Additional folding +1D406; 0067; Additional folding +1D407; 0068; Additional folding +1D408; 0069; Additional folding +1D409; 006A; Additional folding +1D40A; 006B; Additional folding +1D40B; 006C; Additional folding +1D40C; 006D; Additional folding +1D40D; 006E; Additional folding +1D40E; 006F; Additional folding +1D40F; 0070; Additional folding +1D410; 0071; Additional folding +1D411; 0072; Additional folding +1D412; 0073; Additional folding +1D413; 0074; Additional folding +1D414; 0075; Additional folding +1D415; 0076; Additional folding +1D416; 0077; Additional folding +1D417; 0078; Additional folding +1D418; 0079; Additional folding +1D419; 007A; Additional folding +1D434; 0061; Additional folding +1D435; 0062; Additional folding +1D436; 0063; Additional folding +1D437; 0064; Additional folding +1D438; 0065; Additional folding +1D439; 0066; Additional folding +1D43A; 0067; Additional folding +1D43B; 0068; Additional folding +1D43C; 0069; Additional folding +1D43D; 006A; Additional folding +1D43E; 006B; Additional folding +1D43F; 006C; Additional folding +1D440; 006D; Additional folding +1D441; 006E; Additional folding +1D442; 006F; Additional folding +1D443; 0070; Additional folding +1D444; 0071; Additional folding +1D445; 0072; Additional folding +1D446; 0073; Additional folding +1D447; 0074; Additional folding +1D448; 0075; Additional folding +1D449; 0076; Additional folding +1D44A; 0077; Additional folding +1D44B; 0078; Additional folding +1D44C; 0079; Additional folding +1D44D; 007A; Additional folding +1D468; 0061; Additional folding +1D469; 0062; Additional folding +1D46A; 0063; Additional folding +1D46B; 0064; Additional folding +1D46C; 0065; Additional folding +1D46D; 0066; Additional folding +1D46E; 0067; Additional folding +1D46F; 0068; Additional folding +1D470; 0069; Additional folding +1D471; 006A; Additional folding +1D472; 006B; Additional folding +1D473; 006C; Additional folding +1D474; 006D; Additional folding +1D475; 006E; Additional folding +1D476; 006F; Additional folding +1D477; 0070; Additional folding +1D478; 0071; Additional folding +1D479; 0072; Additional folding +1D47A; 0073; Additional folding +1D47B; 0074; Additional folding +1D47C; 0075; Additional folding +1D47D; 0076; Additional folding +1D47E; 0077; Additional folding +1D47F; 0078; Additional folding +1D480; 0079; Additional folding +1D481; 007A; Additional folding +1D49C; 0061; Additional folding +1D49E; 0063; Additional folding +1D49F; 0064; Additional folding +1D4A2; 0067; Additional folding +1D4A5; 006A; Additional folding +1D4A6; 006B; Additional folding +1D4A9; 006E; Additional folding +1D4AA; 006F; Additional folding +1D4AB; 0070; Additional folding +1D4AC; 0071; Additional folding +1D4AE; 0073; Additional folding +1D4AF; 0074; Additional folding +1D4B0; 0075; Additional folding +1D4B1; 0076; Additional folding +1D4B2; 0077; Additional folding +1D4B3; 0078; Additional folding +1D4B4; 0079; Additional folding +1D4B5; 007A; Additional folding +1D4D0; 0061; Additional folding +1D4D1; 0062; Additional folding +1D4D2; 0063; Additional folding +1D4D3; 0064; Additional folding +1D4D4; 0065; Additional folding +1D4D5; 0066; Additional folding +1D4D6; 0067; Additional folding +1D4D7; 0068; Additional folding +1D4D8; 0069; Additional folding +1D4D9; 006A; Additional folding +1D4DA; 006B; Additional folding +1D4DB; 006C; Additional folding +1D4DC; 006D; Additional folding +1D4DD; 006E; Additional folding +1D4DE; 006F; Additional folding +1D4DF; 0070; Additional folding +1D4E0; 0071; Additional folding +1D4E1; 0072; Additional folding +1D4E2; 0073; Additional folding +1D4E3; 0074; Additional folding +1D4E4; 0075; Additional folding +1D4E5; 0076; Additional folding +1D4E6; 0077; Additional folding +1D4E7; 0078; Additional folding +1D4E8; 0079; Additional folding +1D4E9; 007A; Additional folding +1D504; 0061; Additional folding +1D505; 0062; Additional folding +1D507; 0064; Additional folding +1D508; 0065; Additional folding +1D509; 0066; Additional folding +1D50A; 0067; Additional folding +1D50D; 006A; Additional folding +1D50E; 006B; Additional folding +1D50F; 006C; Additional folding +1D510; 006D; Additional folding +1D511; 006E; Additional folding +1D512; 006F; Additional folding +1D513; 0070; Additional folding +1D514; 0071; Additional folding +1D516; 0073; Additional folding +1D517; 0074; Additional folding +1D518; 0075; Additional folding +1D519; 0076; Additional folding +1D51A; 0077; Additional folding +1D51B; 0078; Additional folding +1D51C; 0079; Additional folding +1D538; 0061; Additional folding +1D539; 0062; Additional folding +1D53B; 0064; Additional folding +1D53C; 0065; Additional folding +1D53D; 0066; Additional folding +1D53E; 0067; Additional folding +1D540; 0069; Additional folding +1D541; 006A; Additional folding +1D542; 006B; Additional folding +1D543; 006C; Additional folding +1D544; 006D; Additional folding +1D546; 006F; Additional folding +1D54A; 0073; Additional folding +1D54B; 0074; Additional folding +1D54C; 0075; Additional folding +1D54D; 0076; Additional folding +1D54E; 0077; Additional folding +1D54F; 0078; Additional folding +1D550; 0079; Additional folding +1D56C; 0061; Additional folding +1D56D; 0062; Additional folding +1D56E; 0063; Additional folding +1D56F; 0064; Additional folding +1D570; 0065; Additional folding +1D571; 0066; Additional folding +1D572; 0067; Additional folding +1D573; 0068; Additional folding +1D574; 0069; Additional folding +1D575; 006A; Additional folding +1D576; 006B; Additional folding +1D577; 006C; Additional folding +1D578; 006D; Additional folding +1D579; 006E; Additional folding +1D57A; 006F; Additional folding +1D57B; 0070; Additional folding +1D57C; 0071; Additional folding +1D57D; 0072; Additional folding +1D57E; 0073; Additional folding +1D57F; 0074; Additional folding +1D580; 0075; Additional folding +1D581; 0076; Additional folding +1D582; 0077; Additional folding +1D583; 0078; Additional folding +1D584; 0079; Additional folding +1D585; 007A; Additional folding +1D5A0; 0061; Additional folding +1D5A1; 0062; Additional folding +1D5A2; 0063; Additional folding +1D5A3; 0064; Additional folding +1D5A4; 0065; Additional folding +1D5A5; 0066; Additional folding +1D5A6; 0067; Additional folding +1D5A7; 0068; Additional folding +1D5A8; 0069; Additional folding +1D5A9; 006A; Additional folding +1D5AA; 006B; Additional folding +1D5AB; 006C; Additional folding +1D5AC; 006D; Additional folding +1D5AD; 006E; Additional folding +1D5AE; 006F; Additional folding +1D5AF; 0070; Additional folding +1D5B0; 0071; Additional folding +1D5B1; 0072; Additional folding +1D5B2; 0073; Additional folding +1D5B3; 0074; Additional folding +1D5B4; 0075; Additional folding +1D5B5; 0076; Additional folding +1D5B6; 0077; Additional folding +1D5B7; 0078; Additional folding +1D5B8; 0079; Additional folding +1D5B9; 007A; Additional folding +1D5D4; 0061; Additional folding +1D5D5; 0062; Additional folding +1D5D6; 0063; Additional folding +1D5D7; 0064; Additional folding +1D5D8; 0065; Additional folding +1D5D9; 0066; Additional folding +1D5DA; 0067; Additional folding +1D5DB; 0068; Additional folding +1D5DC; 0069; Additional folding +1D5DD; 006A; Additional folding +1D5DE; 006B; Additional folding +1D5DF; 006C; Additional folding +1D5E0; 006D; Additional folding +1D5E1; 006E; Additional folding +1D5E2; 006F; Additional folding +1D5E3; 0070; Additional folding +1D5E4; 0071; Additional folding +1D5E5; 0072; Additional folding +1D5E6; 0073; Additional folding +1D5E7; 0074; Additional folding +1D5E8; 0075; Additional folding +1D5E9; 0076; Additional folding +1D5EA; 0077; Additional folding +1D5EB; 0078; Additional folding +1D5EC; 0079; Additional folding +1D5ED; 007A; Additional folding +1D608; 0061; Additional folding +1D609; 0062; Additional folding +1D60A; 0063; Additional folding +1D60B; 0064; Additional folding +1D60C; 0065; Additional folding +1D60D; 0066; Additional folding +1D60E; 0067; Additional folding +1D60F; 0068; Additional folding +1D610; 0069; Additional folding +1D611; 006A; Additional folding +1D612; 006B; Additional folding +1D613; 006C; Additional folding +1D614; 006D; Additional folding +1D615; 006E; Additional folding +1D616; 006F; Additional folding +1D617; 0070; Additional folding +1D618; 0071; Additional folding +1D619; 0072; Additional folding +1D61A; 0073; Additional folding +1D61B; 0074; Additional folding +1D61C; 0075; Additional folding +1D61D; 0076; Additional folding +1D61E; 0077; Additional folding +1D61F; 0078; Additional folding +1D620; 0079; Additional folding +1D621; 007A; Additional folding +1D63C; 0061; Additional folding +1D63D; 0062; Additional folding +1D63E; 0063; Additional folding +1D63F; 0064; Additional folding +1D640; 0065; Additional folding +1D641; 0066; Additional folding +1D642; 0067; Additional folding +1D643; 0068; Additional folding +1D644; 0069; Additional folding +1D645; 006A; Additional folding +1D646; 006B; Additional folding +1D647; 006C; Additional folding +1D648; 006D; Additional folding +1D649; 006E; Additional folding +1D64A; 006F; Additional folding +1D64B; 0070; Additional folding +1D64C; 0071; Additional folding +1D64D; 0072; Additional folding +1D64E; 0073; Additional folding +1D64F; 0074; Additional folding +1D650; 0075; Additional folding +1D651; 0076; Additional folding +1D652; 0077; Additional folding +1D653; 0078; Additional folding +1D654; 0079; Additional folding +1D655; 007A; Additional folding +1D670; 0061; Additional folding +1D671; 0062; Additional folding +1D672; 0063; Additional folding +1D673; 0064; Additional folding +1D674; 0065; Additional folding +1D675; 0066; Additional folding +1D676; 0067; Additional folding +1D677; 0068; Additional folding +1D678; 0069; Additional folding +1D679; 006A; Additional folding +1D67A; 006B; Additional folding +1D67B; 006C; Additional folding +1D67C; 006D; Additional folding +1D67D; 006E; Additional folding +1D67E; 006F; Additional folding +1D67F; 0070; Additional folding +1D680; 0071; Additional folding +1D681; 0072; Additional folding +1D682; 0073; Additional folding +1D683; 0074; Additional folding +1D684; 0075; Additional folding +1D685; 0076; Additional folding +1D686; 0077; Additional folding +1D687; 0078; Additional folding +1D688; 0079; Additional folding +1D689; 007A; Additional folding +1D6A8; 03B1; Additional folding +1D6A9; 03B2; Additional folding +1D6AA; 03B3; Additional folding +1D6AB; 03B4; Additional folding +1D6AC; 03B5; Additional folding +1D6AD; 03B6; Additional folding +1D6AE; 03B7; Additional folding +1D6AF; 03B8; Additional folding +1D6B0; 03B9; Additional folding +1D6B1; 03BA; Additional folding +1D6B2; 03BB; Additional folding +1D6B3; 03BC; Additional folding +1D6B4; 03BD; Additional folding +1D6B5; 03BE; Additional folding +1D6B6; 03BF; Additional folding +1D6B7; 03C0; Additional folding +1D6B8; 03C1; Additional folding +1D6B9; 03B8; Additional folding +1D6BA; 03C3; Additional folding +1D6BB; 03C4; Additional folding +1D6BC; 03C5; Additional folding +1D6BD; 03C6; Additional folding +1D6BE; 03C7; Additional folding +1D6BF; 03C8; Additional folding +1D6C0; 03C9; Additional folding +1D6D3; 03C3; Additional folding +1D6E2; 03B1; Additional folding +1D6E3; 03B2; Additional folding +1D6E4; 03B3; Additional folding +1D6E5; 03B4; Additional folding +1D6E6; 03B5; Additional folding +1D6E7; 03B6; Additional folding +1D6E8; 03B7; Additional folding +1D6E9; 03B8; Additional folding +1D6EA; 03B9; Additional folding +1D6EB; 03BA; Additional folding +1D6EC; 03BB; Additional folding +1D6ED; 03BC; Additional folding +1D6EE; 03BD; Additional folding +1D6EF; 03BE; Additional folding +1D6F0; 03BF; Additional folding +1D6F1; 03C0; Additional folding +1D6F2; 03C1; Additional folding +1D6F3; 03B8; Additional folding +1D6F4; 03C3; Additional folding +1D6F5; 03C4; Additional folding +1D6F6; 03C5; Additional folding +1D6F7; 03C6; Additional folding +1D6F8; 03C7; Additional folding +1D6F9; 03C8; Additional folding +1D6FA; 03C9; Additional folding +1D70D; 03C3; Additional folding +1D71C; 03B1; Additional folding +1D71D; 03B2; Additional folding +1D71E; 03B3; Additional folding +1D71F; 03B4; Additional folding +1D720; 03B5; Additional folding +1D721; 03B6; Additional folding +1D722; 03B7; Additional folding +1D723; 03B8; Additional folding +1D724; 03B9; Additional folding +1D725; 03BA; Additional folding +1D726; 03BB; Additional folding +1D727; 03BC; Additional folding +1D728; 03BD; Additional folding +1D729; 03BE; Additional folding +1D72A; 03BF; Additional folding +1D72B; 03C0; Additional folding +1D72C; 03C1; Additional folding +1D72D; 03B8; Additional folding +1D72E; 03C3; Additional folding +1D72F; 03C4; Additional folding +1D730; 03C5; Additional folding +1D731; 03C6; Additional folding +1D732; 03C7; Additional folding +1D733; 03C8; Additional folding +1D734; 03C9; Additional folding +1D747; 03C3; Additional folding +1D756; 03B1; Additional folding +1D757; 03B2; Additional folding +1D758; 03B3; Additional folding +1D759; 03B4; Additional folding +1D75A; 03B5; Additional folding +1D75B; 03B6; Additional folding +1D75C; 03B7; Additional folding +1D75D; 03B8; Additional folding +1D75E; 03B9; Additional folding +1D75F; 03BA; Additional folding +1D760; 03BB; Additional folding +1D761; 03BC; Additional folding +1D762; 03BD; Additional folding +1D763; 03BE; Additional folding +1D764; 03BF; Additional folding +1D765; 03C0; Additional folding +1D766; 03C1; Additional folding +1D767; 03B8; Additional folding +1D768; 03C3; Additional folding +1D769; 03C4; Additional folding +1D76A; 03C5; Additional folding +1D76B; 03C6; Additional folding +1D76C; 03C7; Additional folding +1D76D; 03C8; Additional folding +1D76E; 03C9; Additional folding +1D781; 03C3; Additional folding +1D790; 03B1; Additional folding +1D791; 03B2; Additional folding +1D792; 03B3; Additional folding +1D793; 03B4; Additional folding +1D794; 03B5; Additional folding +1D795; 03B6; Additional folding +1D796; 03B7; Additional folding +1D797; 03B8; Additional folding +1D798; 03B9; Additional folding +1D799; 03BA; Additional folding +1D79A; 03BB; Additional folding +1D79B; 03BC; Additional folding +1D79C; 03BD; Additional folding +1D79D; 03BE; Additional folding +1D79E; 03BF; Additional folding +1D79F; 03C0; Additional folding +1D7A0; 03C1; Additional folding +1D7A1; 03B8; Additional folding +1D7A2; 03C3; Additional folding +1D7A3; 03C4; Additional folding +1D7A4; 03C5; Additional folding +1D7A5; 03C6; Additional folding +1D7A6; 03C7; Additional folding +1D7A7; 03C8; Additional folding +1D7A8; 03C9; Additional folding +1D7BB; 03C3; Additional folding + + */ + +const Stringprep_table_element stringprep_rfc3454_B_3[] = { + { 0x000041, 0x000041, { 0x000061 } }, /* 0041; 0061; Case map */ + { 0x000042, 0x000042, { 0x000062 } }, /* 0042; 0062; Case map */ + { 0x000043, 0x000043, { 0x000063 } }, /* 0043; 0063; Case map */ + { 0x000044, 0x000044, { 0x000064 } }, /* 0044; 0064; Case map */ + { 0x000045, 0x000045, { 0x000065 } }, /* 0045; 0065; Case map */ + { 0x000046, 0x000046, { 0x000066 } }, /* 0046; 0066; Case map */ + { 0x000047, 0x000047, { 0x000067 } }, /* 0047; 0067; Case map */ + { 0x000048, 0x000048, { 0x000068 } }, /* 0048; 0068; Case map */ + { 0x000049, 0x000049, { 0x000069 } }, /* 0049; 0069; Case map */ + { 0x00004A, 0x00004A, { 0x00006A } }, /* 004A; 006A; Case map */ + { 0x00004B, 0x00004B, { 0x00006B } }, /* 004B; 006B; Case map */ + { 0x00004C, 0x00004C, { 0x00006C } }, /* 004C; 006C; Case map */ + { 0x00004D, 0x00004D, { 0x00006D } }, /* 004D; 006D; Case map */ + { 0x00004E, 0x00004E, { 0x00006E } }, /* 004E; 006E; Case map */ + { 0x00004F, 0x00004F, { 0x00006F } }, /* 004F; 006F; Case map */ + { 0x000050, 0x000050, { 0x000070 } }, /* 0050; 0070; Case map */ + { 0x000051, 0x000051, { 0x000071 } }, /* 0051; 0071; Case map */ + { 0x000052, 0x000052, { 0x000072 } }, /* 0052; 0072; Case map */ + { 0x000053, 0x000053, { 0x000073 } }, /* 0053; 0073; Case map */ + { 0x000054, 0x000054, { 0x000074 } }, /* 0054; 0074; Case map */ + { 0x000055, 0x000055, { 0x000075 } }, /* 0055; 0075; Case map */ + { 0x000056, 0x000056, { 0x000076 } }, /* 0056; 0076; Case map */ + { 0x000057, 0x000057, { 0x000077 } }, /* 0057; 0077; Case map */ + { 0x000058, 0x000058, { 0x000078 } }, /* 0058; 0078; Case map */ + { 0x000059, 0x000059, { 0x000079 } }, /* 0059; 0079; Case map */ + { 0x00005A, 0x00005A, { 0x00007A } }, /* 005A; 007A; Case map */ + { 0x0000B5, 0x0000B5, { 0x0003BC } }, /* 00B5; 03BC; Case map */ + { 0x0000C0, 0x0000C0, { 0x0000E0 } }, /* 00C0; 00E0; Case map */ + { 0x0000C1, 0x0000C1, { 0x0000E1 } }, /* 00C1; 00E1; Case map */ + { 0x0000C2, 0x0000C2, { 0x0000E2 } }, /* 00C2; 00E2; Case map */ + { 0x0000C3, 0x0000C3, { 0x0000E3 } }, /* 00C3; 00E3; Case map */ + { 0x0000C4, 0x0000C4, { 0x0000E4 } }, /* 00C4; 00E4; Case map */ + { 0x0000C5, 0x0000C5, { 0x0000E5 } }, /* 00C5; 00E5; Case map */ + { 0x0000C6, 0x0000C6, { 0x0000E6 } }, /* 00C6; 00E6; Case map */ + { 0x0000C7, 0x0000C7, { 0x0000E7 } }, /* 00C7; 00E7; Case map */ + { 0x0000C8, 0x0000C8, { 0x0000E8 } }, /* 00C8; 00E8; Case map */ + { 0x0000C9, 0x0000C9, { 0x0000E9 } }, /* 00C9; 00E9; Case map */ + { 0x0000CA, 0x0000CA, { 0x0000EA } }, /* 00CA; 00EA; Case map */ + { 0x0000CB, 0x0000CB, { 0x0000EB } }, /* 00CB; 00EB; Case map */ + { 0x0000CC, 0x0000CC, { 0x0000EC } }, /* 00CC; 00EC; Case map */ + { 0x0000CD, 0x0000CD, { 0x0000ED } }, /* 00CD; 00ED; Case map */ + { 0x0000CE, 0x0000CE, { 0x0000EE } }, /* 00CE; 00EE; Case map */ + { 0x0000CF, 0x0000CF, { 0x0000EF } }, /* 00CF; 00EF; Case map */ + { 0x0000D0, 0x0000D0, { 0x0000F0 } }, /* 00D0; 00F0; Case map */ + { 0x0000D1, 0x0000D1, { 0x0000F1 } }, /* 00D1; 00F1; Case map */ + { 0x0000D2, 0x0000D2, { 0x0000F2 } }, /* 00D2; 00F2; Case map */ + { 0x0000D3, 0x0000D3, { 0x0000F3 } }, /* 00D3; 00F3; Case map */ + { 0x0000D4, 0x0000D4, { 0x0000F4 } }, /* 00D4; 00F4; Case map */ + { 0x0000D5, 0x0000D5, { 0x0000F5 } }, /* 00D5; 00F5; Case map */ + { 0x0000D6, 0x0000D6, { 0x0000F6 } }, /* 00D6; 00F6; Case map */ + { 0x0000D8, 0x0000D8, { 0x0000F8 } }, /* 00D8; 00F8; Case map */ + { 0x0000D9, 0x0000D9, { 0x0000F9 } }, /* 00D9; 00F9; Case map */ + { 0x0000DA, 0x0000DA, { 0x0000FA } }, /* 00DA; 00FA; Case map */ + { 0x0000DB, 0x0000DB, { 0x0000FB } }, /* 00DB; 00FB; Case map */ + { 0x0000DC, 0x0000DC, { 0x0000FC } }, /* 00DC; 00FC; Case map */ + { 0x0000DD, 0x0000DD, { 0x0000FD } }, /* 00DD; 00FD; Case map */ + { 0x0000DE, 0x0000DE, { 0x0000FE } }, /* 00DE; 00FE; Case map */ + { 0x0000DF, + 0x0000DF, + { 0x000073, /* 00DF; 0073 0073; Case map */ + 0x000073 } }, + { 0x000100, 0x000100, { 0x000101 } }, /* 0100; 0101; Case map */ + { 0x000102, 0x000102, { 0x000103 } }, /* 0102; 0103; Case map */ + { 0x000104, 0x000104, { 0x000105 } }, /* 0104; 0105; Case map */ + { 0x000106, 0x000106, { 0x000107 } }, /* 0106; 0107; Case map */ + { 0x000108, 0x000108, { 0x000109 } }, /* 0108; 0109; Case map */ + { 0x00010A, 0x00010A, { 0x00010B } }, /* 010A; 010B; Case map */ + { 0x00010C, 0x00010C, { 0x00010D } }, /* 010C; 010D; Case map */ + { 0x00010E, 0x00010E, { 0x00010F } }, /* 010E; 010F; Case map */ + { 0x000110, 0x000110, { 0x000111 } }, /* 0110; 0111; Case map */ + { 0x000112, 0x000112, { 0x000113 } }, /* 0112; 0113; Case map */ + { 0x000114, 0x000114, { 0x000115 } }, /* 0114; 0115; Case map */ + { 0x000116, 0x000116, { 0x000117 } }, /* 0116; 0117; Case map */ + { 0x000118, 0x000118, { 0x000119 } }, /* 0118; 0119; Case map */ + { 0x00011A, 0x00011A, { 0x00011B } }, /* 011A; 011B; Case map */ + { 0x00011C, 0x00011C, { 0x00011D } }, /* 011C; 011D; Case map */ + { 0x00011E, 0x00011E, { 0x00011F } }, /* 011E; 011F; Case map */ + { 0x000120, 0x000120, { 0x000121 } }, /* 0120; 0121; Case map */ + { 0x000122, 0x000122, { 0x000123 } }, /* 0122; 0123; Case map */ + { 0x000124, 0x000124, { 0x000125 } }, /* 0124; 0125; Case map */ + { 0x000126, 0x000126, { 0x000127 } }, /* 0126; 0127; Case map */ + { 0x000128, 0x000128, { 0x000129 } }, /* 0128; 0129; Case map */ + { 0x00012A, 0x00012A, { 0x00012B } }, /* 012A; 012B; Case map */ + { 0x00012C, 0x00012C, { 0x00012D } }, /* 012C; 012D; Case map */ + { 0x00012E, 0x00012E, { 0x00012F } }, /* 012E; 012F; Case map */ + { 0x000130, + 0x000130, + { 0x000069, /* 0130; 0069 0307; Case map */ + 0x000307 } }, + { 0x000132, 0x000132, { 0x000133 } }, /* 0132; 0133; Case map */ + { 0x000134, 0x000134, { 0x000135 } }, /* 0134; 0135; Case map */ + { 0x000136, 0x000136, { 0x000137 } }, /* 0136; 0137; Case map */ + { 0x000139, 0x000139, { 0x00013A } }, /* 0139; 013A; Case map */ + { 0x00013B, 0x00013B, { 0x00013C } }, /* 013B; 013C; Case map */ + { 0x00013D, 0x00013D, { 0x00013E } }, /* 013D; 013E; Case map */ + { 0x00013F, 0x00013F, { 0x000140 } }, /* 013F; 0140; Case map */ + { 0x000141, 0x000141, { 0x000142 } }, /* 0141; 0142; Case map */ + { 0x000143, 0x000143, { 0x000144 } }, /* 0143; 0144; Case map */ + { 0x000145, 0x000145, { 0x000146 } }, /* 0145; 0146; Case map */ + { 0x000147, 0x000147, { 0x000148 } }, /* 0147; 0148; Case map */ + { 0x000149, + 0x000149, + { 0x0002BC, /* 0149; 02BC 006E; Case map */ + 0x00006E } }, + { 0x00014A, 0x00014A, { 0x00014B } }, /* 014A; 014B; Case map */ + { 0x00014C, 0x00014C, { 0x00014D } }, /* 014C; 014D; Case map */ + { 0x00014E, 0x00014E, { 0x00014F } }, /* 014E; 014F; Case map */ + { 0x000150, 0x000150, { 0x000151 } }, /* 0150; 0151; Case map */ + { 0x000152, 0x000152, { 0x000153 } }, /* 0152; 0153; Case map */ + { 0x000154, 0x000154, { 0x000155 } }, /* 0154; 0155; Case map */ + { 0x000156, 0x000156, { 0x000157 } }, /* 0156; 0157; Case map */ + { 0x000158, 0x000158, { 0x000159 } }, /* 0158; 0159; Case map */ + { 0x00015A, 0x00015A, { 0x00015B } }, /* 015A; 015B; Case map */ + { 0x00015C, 0x00015C, { 0x00015D } }, /* 015C; 015D; Case map */ + { 0x00015E, 0x00015E, { 0x00015F } }, /* 015E; 015F; Case map */ + { 0x000160, 0x000160, { 0x000161 } }, /* 0160; 0161; Case map */ + { 0x000162, 0x000162, { 0x000163 } }, /* 0162; 0163; Case map */ + { 0x000164, 0x000164, { 0x000165 } }, /* 0164; 0165; Case map */ + { 0x000166, 0x000166, { 0x000167 } }, /* 0166; 0167; Case map */ + { 0x000168, 0x000168, { 0x000169 } }, /* 0168; 0169; Case map */ + { 0x00016A, 0x00016A, { 0x00016B } }, /* 016A; 016B; Case map */ + { 0x00016C, 0x00016C, { 0x00016D } }, /* 016C; 016D; Case map */ + { 0x00016E, 0x00016E, { 0x00016F } }, /* 016E; 016F; Case map */ + { 0x000170, 0x000170, { 0x000171 } }, /* 0170; 0171; Case map */ + { 0x000172, 0x000172, { 0x000173 } }, /* 0172; 0173; Case map */ + { 0x000174, 0x000174, { 0x000175 } }, /* 0174; 0175; Case map */ + { 0x000176, 0x000176, { 0x000177 } }, /* 0176; 0177; Case map */ + { 0x000178, 0x000178, { 0x0000FF } }, /* 0178; 00FF; Case map */ + { 0x000179, 0x000179, { 0x00017A } }, /* 0179; 017A; Case map */ + { 0x00017B, 0x00017B, { 0x00017C } }, /* 017B; 017C; Case map */ + { 0x00017D, 0x00017D, { 0x00017E } }, /* 017D; 017E; Case map */ + { 0x00017F, 0x00017F, { 0x000073 } }, /* 017F; 0073; Case map */ + { 0x000181, 0x000181, { 0x000253 } }, /* 0181; 0253; Case map */ + { 0x000182, 0x000182, { 0x000183 } }, /* 0182; 0183; Case map */ + { 0x000184, 0x000184, { 0x000185 } }, /* 0184; 0185; Case map */ + { 0x000186, 0x000186, { 0x000254 } }, /* 0186; 0254; Case map */ + { 0x000187, 0x000187, { 0x000188 } }, /* 0187; 0188; Case map */ + { 0x000189, 0x000189, { 0x000256 } }, /* 0189; 0256; Case map */ + { 0x00018A, 0x00018A, { 0x000257 } }, /* 018A; 0257; Case map */ + { 0x00018B, 0x00018B, { 0x00018C } }, /* 018B; 018C; Case map */ + { 0x00018E, 0x00018E, { 0x0001DD } }, /* 018E; 01DD; Case map */ + { 0x00018F, 0x00018F, { 0x000259 } }, /* 018F; 0259; Case map */ + { 0x000190, 0x000190, { 0x00025B } }, /* 0190; 025B; Case map */ + { 0x000191, 0x000191, { 0x000192 } }, /* 0191; 0192; Case map */ + { 0x000193, 0x000193, { 0x000260 } }, /* 0193; 0260; Case map */ + { 0x000194, 0x000194, { 0x000263 } }, /* 0194; 0263; Case map */ + { 0x000196, 0x000196, { 0x000269 } }, /* 0196; 0269; Case map */ + { 0x000197, 0x000197, { 0x000268 } }, /* 0197; 0268; Case map */ + { 0x000198, 0x000198, { 0x000199 } }, /* 0198; 0199; Case map */ + { 0x00019C, 0x00019C, { 0x00026F } }, /* 019C; 026F; Case map */ + { 0x00019D, 0x00019D, { 0x000272 } }, /* 019D; 0272; Case map */ + { 0x00019F, 0x00019F, { 0x000275 } }, /* 019F; 0275; Case map */ + { 0x0001A0, 0x0001A0, { 0x0001A1 } }, /* 01A0; 01A1; Case map */ + { 0x0001A2, 0x0001A2, { 0x0001A3 } }, /* 01A2; 01A3; Case map */ + { 0x0001A4, 0x0001A4, { 0x0001A5 } }, /* 01A4; 01A5; Case map */ + { 0x0001A6, 0x0001A6, { 0x000280 } }, /* 01A6; 0280; Case map */ + { 0x0001A7, 0x0001A7, { 0x0001A8 } }, /* 01A7; 01A8; Case map */ + { 0x0001A9, 0x0001A9, { 0x000283 } }, /* 01A9; 0283; Case map */ + { 0x0001AC, 0x0001AC, { 0x0001AD } }, /* 01AC; 01AD; Case map */ + { 0x0001AE, 0x0001AE, { 0x000288 } }, /* 01AE; 0288; Case map */ + { 0x0001AF, 0x0001AF, { 0x0001B0 } }, /* 01AF; 01B0; Case map */ + { 0x0001B1, 0x0001B1, { 0x00028A } }, /* 01B1; 028A; Case map */ + { 0x0001B2, 0x0001B2, { 0x00028B } }, /* 01B2; 028B; Case map */ + { 0x0001B3, 0x0001B3, { 0x0001B4 } }, /* 01B3; 01B4; Case map */ + { 0x0001B5, 0x0001B5, { 0x0001B6 } }, /* 01B5; 01B6; Case map */ + { 0x0001B7, 0x0001B7, { 0x000292 } }, /* 01B7; 0292; Case map */ + { 0x0001B8, 0x0001B8, { 0x0001B9 } }, /* 01B8; 01B9; Case map */ + { 0x0001BC, 0x0001BC, { 0x0001BD } }, /* 01BC; 01BD; Case map */ + { 0x0001C4, 0x0001C4, { 0x0001C6 } }, /* 01C4; 01C6; Case map */ + { 0x0001C5, 0x0001C5, { 0x0001C6 } }, /* 01C5; 01C6; Case map */ + { 0x0001C7, 0x0001C7, { 0x0001C9 } }, /* 01C7; 01C9; Case map */ + { 0x0001C8, 0x0001C8, { 0x0001C9 } }, /* 01C8; 01C9; Case map */ + { 0x0001CA, 0x0001CA, { 0x0001CC } }, /* 01CA; 01CC; Case map */ + { 0x0001CB, 0x0001CB, { 0x0001CC } }, /* 01CB; 01CC; Case map */ + { 0x0001CD, 0x0001CD, { 0x0001CE } }, /* 01CD; 01CE; Case map */ + { 0x0001CF, 0x0001CF, { 0x0001D0 } }, /* 01CF; 01D0; Case map */ + { 0x0001D1, 0x0001D1, { 0x0001D2 } }, /* 01D1; 01D2; Case map */ + { 0x0001D3, 0x0001D3, { 0x0001D4 } }, /* 01D3; 01D4; Case map */ + { 0x0001D5, 0x0001D5, { 0x0001D6 } }, /* 01D5; 01D6; Case map */ + { 0x0001D7, 0x0001D7, { 0x0001D8 } }, /* 01D7; 01D8; Case map */ + { 0x0001D9, 0x0001D9, { 0x0001DA } }, /* 01D9; 01DA; Case map */ + { 0x0001DB, 0x0001DB, { 0x0001DC } }, /* 01DB; 01DC; Case map */ + { 0x0001DE, 0x0001DE, { 0x0001DF } }, /* 01DE; 01DF; Case map */ + { 0x0001E0, 0x0001E0, { 0x0001E1 } }, /* 01E0; 01E1; Case map */ + { 0x0001E2, 0x0001E2, { 0x0001E3 } }, /* 01E2; 01E3; Case map */ + { 0x0001E4, 0x0001E4, { 0x0001E5 } }, /* 01E4; 01E5; Case map */ + { 0x0001E6, 0x0001E6, { 0x0001E7 } }, /* 01E6; 01E7; Case map */ + { 0x0001E8, 0x0001E8, { 0x0001E9 } }, /* 01E8; 01E9; Case map */ + { 0x0001EA, 0x0001EA, { 0x0001EB } }, /* 01EA; 01EB; Case map */ + { 0x0001EC, 0x0001EC, { 0x0001ED } }, /* 01EC; 01ED; Case map */ + { 0x0001EE, 0x0001EE, { 0x0001EF } }, /* 01EE; 01EF; Case map */ + { 0x0001F0, + 0x0001F0, + { 0x00006A, /* 01F0; 006A 030C; Case map */ + 0x00030C } }, + { 0x0001F1, 0x0001F1, { 0x0001F3 } }, /* 01F1; 01F3; Case map */ + { 0x0001F2, 0x0001F2, { 0x0001F3 } }, /* 01F2; 01F3; Case map */ + { 0x0001F4, 0x0001F4, { 0x0001F5 } }, /* 01F4; 01F5; Case map */ + { 0x0001F6, 0x0001F6, { 0x000195 } }, /* 01F6; 0195; Case map */ + { 0x0001F7, 0x0001F7, { 0x0001BF } }, /* 01F7; 01BF; Case map */ + { 0x0001F8, 0x0001F8, { 0x0001F9 } }, /* 01F8; 01F9; Case map */ + { 0x0001FA, 0x0001FA, { 0x0001FB } }, /* 01FA; 01FB; Case map */ + { 0x0001FC, 0x0001FC, { 0x0001FD } }, /* 01FC; 01FD; Case map */ + { 0x0001FE, 0x0001FE, { 0x0001FF } }, /* 01FE; 01FF; Case map */ + { 0x000200, 0x000200, { 0x000201 } }, /* 0200; 0201; Case map */ + { 0x000202, 0x000202, { 0x000203 } }, /* 0202; 0203; Case map */ + { 0x000204, 0x000204, { 0x000205 } }, /* 0204; 0205; Case map */ + { 0x000206, 0x000206, { 0x000207 } }, /* 0206; 0207; Case map */ + { 0x000208, 0x000208, { 0x000209 } }, /* 0208; 0209; Case map */ + { 0x00020A, 0x00020A, { 0x00020B } }, /* 020A; 020B; Case map */ + { 0x00020C, 0x00020C, { 0x00020D } }, /* 020C; 020D; Case map */ + { 0x00020E, 0x00020E, { 0x00020F } }, /* 020E; 020F; Case map */ + { 0x000210, 0x000210, { 0x000211 } }, /* 0210; 0211; Case map */ + { 0x000212, 0x000212, { 0x000213 } }, /* 0212; 0213; Case map */ + { 0x000214, 0x000214, { 0x000215 } }, /* 0214; 0215; Case map */ + { 0x000216, 0x000216, { 0x000217 } }, /* 0216; 0217; Case map */ + { 0x000218, 0x000218, { 0x000219 } }, /* 0218; 0219; Case map */ + { 0x00021A, 0x00021A, { 0x00021B } }, /* 021A; 021B; Case map */ + { 0x00021C, 0x00021C, { 0x00021D } }, /* 021C; 021D; Case map */ + { 0x00021E, 0x00021E, { 0x00021F } }, /* 021E; 021F; Case map */ + { 0x000220, 0x000220, { 0x00019E } }, /* 0220; 019E; Case map */ + { 0x000222, 0x000222, { 0x000223 } }, /* 0222; 0223; Case map */ + { 0x000224, 0x000224, { 0x000225 } }, /* 0224; 0225; Case map */ + { 0x000226, 0x000226, { 0x000227 } }, /* 0226; 0227; Case map */ + { 0x000228, 0x000228, { 0x000229 } }, /* 0228; 0229; Case map */ + { 0x00022A, 0x00022A, { 0x00022B } }, /* 022A; 022B; Case map */ + { 0x00022C, 0x00022C, { 0x00022D } }, /* 022C; 022D; Case map */ + { 0x00022E, 0x00022E, { 0x00022F } }, /* 022E; 022F; Case map */ + { 0x000230, 0x000230, { 0x000231 } }, /* 0230; 0231; Case map */ + { 0x000232, 0x000232, { 0x000233 } }, /* 0232; 0233; Case map */ + { 0x000345, 0x000345, { 0x0003B9 } }, /* 0345; 03B9; Case map */ + { 0x000386, 0x000386, { 0x0003AC } }, /* 0386; 03AC; Case map */ + { 0x000388, 0x000388, { 0x0003AD } }, /* 0388; 03AD; Case map */ + { 0x000389, 0x000389, { 0x0003AE } }, /* 0389; 03AE; Case map */ + { 0x00038A, 0x00038A, { 0x0003AF } }, /* 038A; 03AF; Case map */ + { 0x00038C, 0x00038C, { 0x0003CC } }, /* 038C; 03CC; Case map */ + { 0x00038E, 0x00038E, { 0x0003CD } }, /* 038E; 03CD; Case map */ + { 0x00038F, 0x00038F, { 0x0003CE } }, /* 038F; 03CE; Case map */ + { 0x000390, + 0x000390, + { 0x0003B9, /* 0390; 03B9 0308 0301; Case map */ + 0x000308, 0x000301 } }, + { 0x000391, 0x000391, { 0x0003B1 } }, /* 0391; 03B1; Case map */ + { 0x000392, 0x000392, { 0x0003B2 } }, /* 0392; 03B2; Case map */ + { 0x000393, 0x000393, { 0x0003B3 } }, /* 0393; 03B3; Case map */ + { 0x000394, 0x000394, { 0x0003B4 } }, /* 0394; 03B4; Case map */ + { 0x000395, 0x000395, { 0x0003B5 } }, /* 0395; 03B5; Case map */ + { 0x000396, 0x000396, { 0x0003B6 } }, /* 0396; 03B6; Case map */ + { 0x000397, 0x000397, { 0x0003B7 } }, /* 0397; 03B7; Case map */ + { 0x000398, 0x000398, { 0x0003B8 } }, /* 0398; 03B8; Case map */ + { 0x000399, 0x000399, { 0x0003B9 } }, /* 0399; 03B9; Case map */ + { 0x00039A, 0x00039A, { 0x0003BA } }, /* 039A; 03BA; Case map */ + { 0x00039B, 0x00039B, { 0x0003BB } }, /* 039B; 03BB; Case map */ + { 0x00039C, 0x00039C, { 0x0003BC } }, /* 039C; 03BC; Case map */ + { 0x00039D, 0x00039D, { 0x0003BD } }, /* 039D; 03BD; Case map */ + { 0x00039E, 0x00039E, { 0x0003BE } }, /* 039E; 03BE; Case map */ + { 0x00039F, 0x00039F, { 0x0003BF } }, /* 039F; 03BF; Case map */ + { 0x0003A0, 0x0003A0, { 0x0003C0 } }, /* 03A0; 03C0; Case map */ + { 0x0003A1, 0x0003A1, { 0x0003C1 } }, /* 03A1; 03C1; Case map */ + { 0x0003A3, 0x0003A3, { 0x0003C3 } }, /* 03A3; 03C3; Case map */ + { 0x0003A4, 0x0003A4, { 0x0003C4 } }, /* 03A4; 03C4; Case map */ + { 0x0003A5, 0x0003A5, { 0x0003C5 } }, /* 03A5; 03C5; Case map */ + { 0x0003A6, 0x0003A6, { 0x0003C6 } }, /* 03A6; 03C6; Case map */ + { 0x0003A7, 0x0003A7, { 0x0003C7 } }, /* 03A7; 03C7; Case map */ + { 0x0003A8, 0x0003A8, { 0x0003C8 } }, /* 03A8; 03C8; Case map */ + { 0x0003A9, 0x0003A9, { 0x0003C9 } }, /* 03A9; 03C9; Case map */ + { 0x0003AA, 0x0003AA, { 0x0003CA } }, /* 03AA; 03CA; Case map */ + { 0x0003AB, 0x0003AB, { 0x0003CB } }, /* 03AB; 03CB; Case map */ + { 0x0003B0, + 0x0003B0, + { 0x0003C5, /* 03B0; 03C5 0308 0301; Case map */ + 0x000308, 0x000301 } }, + { 0x0003C2, 0x0003C2, { 0x0003C3 } }, /* 03C2; 03C3; Case map */ + { 0x0003D0, 0x0003D0, { 0x0003B2 } }, /* 03D0; 03B2; Case map */ + { 0x0003D1, 0x0003D1, { 0x0003B8 } }, /* 03D1; 03B8; Case map */ + { 0x0003D5, 0x0003D5, { 0x0003C6 } }, /* 03D5; 03C6; Case map */ + { 0x0003D6, 0x0003D6, { 0x0003C0 } }, /* 03D6; 03C0; Case map */ + { 0x0003D8, 0x0003D8, { 0x0003D9 } }, /* 03D8; 03D9; Case map */ + { 0x0003DA, 0x0003DA, { 0x0003DB } }, /* 03DA; 03DB; Case map */ + { 0x0003DC, 0x0003DC, { 0x0003DD } }, /* 03DC; 03DD; Case map */ + { 0x0003DE, 0x0003DE, { 0x0003DF } }, /* 03DE; 03DF; Case map */ + { 0x0003E0, 0x0003E0, { 0x0003E1 } }, /* 03E0; 03E1; Case map */ + { 0x0003E2, 0x0003E2, { 0x0003E3 } }, /* 03E2; 03E3; Case map */ + { 0x0003E4, 0x0003E4, { 0x0003E5 } }, /* 03E4; 03E5; Case map */ + { 0x0003E6, 0x0003E6, { 0x0003E7 } }, /* 03E6; 03E7; Case map */ + { 0x0003E8, 0x0003E8, { 0x0003E9 } }, /* 03E8; 03E9; Case map */ + { 0x0003EA, 0x0003EA, { 0x0003EB } }, /* 03EA; 03EB; Case map */ + { 0x0003EC, 0x0003EC, { 0x0003ED } }, /* 03EC; 03ED; Case map */ + { 0x0003EE, 0x0003EE, { 0x0003EF } }, /* 03EE; 03EF; Case map */ + { 0x0003F0, 0x0003F0, { 0x0003BA } }, /* 03F0; 03BA; Case map */ + { 0x0003F1, 0x0003F1, { 0x0003C1 } }, /* 03F1; 03C1; Case map */ + { 0x0003F2, 0x0003F2, { 0x0003C3 } }, /* 03F2; 03C3; Case map */ + { 0x0003F4, 0x0003F4, { 0x0003B8 } }, /* 03F4; 03B8; Case map */ + { 0x0003F5, 0x0003F5, { 0x0003B5 } }, /* 03F5; 03B5; Case map */ + { 0x000400, 0x000400, { 0x000450 } }, /* 0400; 0450; Case map */ + { 0x000401, 0x000401, { 0x000451 } }, /* 0401; 0451; Case map */ + { 0x000402, 0x000402, { 0x000452 } }, /* 0402; 0452; Case map */ + { 0x000403, 0x000403, { 0x000453 } }, /* 0403; 0453; Case map */ + { 0x000404, 0x000404, { 0x000454 } }, /* 0404; 0454; Case map */ + { 0x000405, 0x000405, { 0x000455 } }, /* 0405; 0455; Case map */ + { 0x000406, 0x000406, { 0x000456 } }, /* 0406; 0456; Case map */ + { 0x000407, 0x000407, { 0x000457 } }, /* 0407; 0457; Case map */ + { 0x000408, 0x000408, { 0x000458 } }, /* 0408; 0458; Case map */ + { 0x000409, 0x000409, { 0x000459 } }, /* 0409; 0459; Case map */ + { 0x00040A, 0x00040A, { 0x00045A } }, /* 040A; 045A; Case map */ + { 0x00040B, 0x00040B, { 0x00045B } }, /* 040B; 045B; Case map */ + { 0x00040C, 0x00040C, { 0x00045C } }, /* 040C; 045C; Case map */ + { 0x00040D, 0x00040D, { 0x00045D } }, /* 040D; 045D; Case map */ + { 0x00040E, 0x00040E, { 0x00045E } }, /* 040E; 045E; Case map */ + { 0x00040F, 0x00040F, { 0x00045F } }, /* 040F; 045F; Case map */ + { 0x000410, 0x000410, { 0x000430 } }, /* 0410; 0430; Case map */ + { 0x000411, 0x000411, { 0x000431 } }, /* 0411; 0431; Case map */ + { 0x000412, 0x000412, { 0x000432 } }, /* 0412; 0432; Case map */ + { 0x000413, 0x000413, { 0x000433 } }, /* 0413; 0433; Case map */ + { 0x000414, 0x000414, { 0x000434 } }, /* 0414; 0434; Case map */ + { 0x000415, 0x000415, { 0x000435 } }, /* 0415; 0435; Case map */ + { 0x000416, 0x000416, { 0x000436 } }, /* 0416; 0436; Case map */ + { 0x000417, 0x000417, { 0x000437 } }, /* 0417; 0437; Case map */ + { 0x000418, 0x000418, { 0x000438 } }, /* 0418; 0438; Case map */ + { 0x000419, 0x000419, { 0x000439 } }, /* 0419; 0439; Case map */ + { 0x00041A, 0x00041A, { 0x00043A } }, /* 041A; 043A; Case map */ + { 0x00041B, 0x00041B, { 0x00043B } }, /* 041B; 043B; Case map */ + { 0x00041C, 0x00041C, { 0x00043C } }, /* 041C; 043C; Case map */ + { 0x00041D, 0x00041D, { 0x00043D } }, /* 041D; 043D; Case map */ + { 0x00041E, 0x00041E, { 0x00043E } }, /* 041E; 043E; Case map */ + { 0x00041F, 0x00041F, { 0x00043F } }, /* 041F; 043F; Case map */ + { 0x000420, 0x000420, { 0x000440 } }, /* 0420; 0440; Case map */ + { 0x000421, 0x000421, { 0x000441 } }, /* 0421; 0441; Case map */ + { 0x000422, 0x000422, { 0x000442 } }, /* 0422; 0442; Case map */ + { 0x000423, 0x000423, { 0x000443 } }, /* 0423; 0443; Case map */ + { 0x000424, 0x000424, { 0x000444 } }, /* 0424; 0444; Case map */ + { 0x000425, 0x000425, { 0x000445 } }, /* 0425; 0445; Case map */ + { 0x000426, 0x000426, { 0x000446 } }, /* 0426; 0446; Case map */ + { 0x000427, 0x000427, { 0x000447 } }, /* 0427; 0447; Case map */ + { 0x000428, 0x000428, { 0x000448 } }, /* 0428; 0448; Case map */ + { 0x000429, 0x000429, { 0x000449 } }, /* 0429; 0449; Case map */ + { 0x00042A, 0x00042A, { 0x00044A } }, /* 042A; 044A; Case map */ + { 0x00042B, 0x00042B, { 0x00044B } }, /* 042B; 044B; Case map */ + { 0x00042C, 0x00042C, { 0x00044C } }, /* 042C; 044C; Case map */ + { 0x00042D, 0x00042D, { 0x00044D } }, /* 042D; 044D; Case map */ + { 0x00042E, 0x00042E, { 0x00044E } }, /* 042E; 044E; Case map */ + { 0x00042F, 0x00042F, { 0x00044F } }, /* 042F; 044F; Case map */ + { 0x000460, 0x000460, { 0x000461 } }, /* 0460; 0461; Case map */ + { 0x000462, 0x000462, { 0x000463 } }, /* 0462; 0463; Case map */ + { 0x000464, 0x000464, { 0x000465 } }, /* 0464; 0465; Case map */ + { 0x000466, 0x000466, { 0x000467 } }, /* 0466; 0467; Case map */ + { 0x000468, 0x000468, { 0x000469 } }, /* 0468; 0469; Case map */ + { 0x00046A, 0x00046A, { 0x00046B } }, /* 046A; 046B; Case map */ + { 0x00046C, 0x00046C, { 0x00046D } }, /* 046C; 046D; Case map */ + { 0x00046E, 0x00046E, { 0x00046F } }, /* 046E; 046F; Case map */ + { 0x000470, 0x000470, { 0x000471 } }, /* 0470; 0471; Case map */ + { 0x000472, 0x000472, { 0x000473 } }, /* 0472; 0473; Case map */ + { 0x000474, 0x000474, { 0x000475 } }, /* 0474; 0475; Case map */ + { 0x000476, 0x000476, { 0x000477 } }, /* 0476; 0477; Case map */ + { 0x000478, 0x000478, { 0x000479 } }, /* 0478; 0479; Case map */ + { 0x00047A, 0x00047A, { 0x00047B } }, /* 047A; 047B; Case map */ + { 0x00047C, 0x00047C, { 0x00047D } }, /* 047C; 047D; Case map */ + { 0x00047E, 0x00047E, { 0x00047F } }, /* 047E; 047F; Case map */ + { 0x000480, 0x000480, { 0x000481 } }, /* 0480; 0481; Case map */ + { 0x00048A, 0x00048A, { 0x00048B } }, /* 048A; 048B; Case map */ + { 0x00048C, 0x00048C, { 0x00048D } }, /* 048C; 048D; Case map */ + { 0x00048E, 0x00048E, { 0x00048F } }, /* 048E; 048F; Case map */ + { 0x000490, 0x000490, { 0x000491 } }, /* 0490; 0491; Case map */ + { 0x000492, 0x000492, { 0x000493 } }, /* 0492; 0493; Case map */ + { 0x000494, 0x000494, { 0x000495 } }, /* 0494; 0495; Case map */ + { 0x000496, 0x000496, { 0x000497 } }, /* 0496; 0497; Case map */ + { 0x000498, 0x000498, { 0x000499 } }, /* 0498; 0499; Case map */ + { 0x00049A, 0x00049A, { 0x00049B } }, /* 049A; 049B; Case map */ + { 0x00049C, 0x00049C, { 0x00049D } }, /* 049C; 049D; Case map */ + { 0x00049E, 0x00049E, { 0x00049F } }, /* 049E; 049F; Case map */ + { 0x0004A0, 0x0004A0, { 0x0004A1 } }, /* 04A0; 04A1; Case map */ + { 0x0004A2, 0x0004A2, { 0x0004A3 } }, /* 04A2; 04A3; Case map */ + { 0x0004A4, 0x0004A4, { 0x0004A5 } }, /* 04A4; 04A5; Case map */ + { 0x0004A6, 0x0004A6, { 0x0004A7 } }, /* 04A6; 04A7; Case map */ + { 0x0004A8, 0x0004A8, { 0x0004A9 } }, /* 04A8; 04A9; Case map */ + { 0x0004AA, 0x0004AA, { 0x0004AB } }, /* 04AA; 04AB; Case map */ + { 0x0004AC, 0x0004AC, { 0x0004AD } }, /* 04AC; 04AD; Case map */ + { 0x0004AE, 0x0004AE, { 0x0004AF } }, /* 04AE; 04AF; Case map */ + { 0x0004B0, 0x0004B0, { 0x0004B1 } }, /* 04B0; 04B1; Case map */ + { 0x0004B2, 0x0004B2, { 0x0004B3 } }, /* 04B2; 04B3; Case map */ + { 0x0004B4, 0x0004B4, { 0x0004B5 } }, /* 04B4; 04B5; Case map */ + { 0x0004B6, 0x0004B6, { 0x0004B7 } }, /* 04B6; 04B7; Case map */ + { 0x0004B8, 0x0004B8, { 0x0004B9 } }, /* 04B8; 04B9; Case map */ + { 0x0004BA, 0x0004BA, { 0x0004BB } }, /* 04BA; 04BB; Case map */ + { 0x0004BC, 0x0004BC, { 0x0004BD } }, /* 04BC; 04BD; Case map */ + { 0x0004BE, 0x0004BE, { 0x0004BF } }, /* 04BE; 04BF; Case map */ + { 0x0004C1, 0x0004C1, { 0x0004C2 } }, /* 04C1; 04C2; Case map */ + { 0x0004C3, 0x0004C3, { 0x0004C4 } }, /* 04C3; 04C4; Case map */ + { 0x0004C5, 0x0004C5, { 0x0004C6 } }, /* 04C5; 04C6; Case map */ + { 0x0004C7, 0x0004C7, { 0x0004C8 } }, /* 04C7; 04C8; Case map */ + { 0x0004C9, 0x0004C9, { 0x0004CA } }, /* 04C9; 04CA; Case map */ + { 0x0004CB, 0x0004CB, { 0x0004CC } }, /* 04CB; 04CC; Case map */ + { 0x0004CD, 0x0004CD, { 0x0004CE } }, /* 04CD; 04CE; Case map */ + { 0x0004D0, 0x0004D0, { 0x0004D1 } }, /* 04D0; 04D1; Case map */ + { 0x0004D2, 0x0004D2, { 0x0004D3 } }, /* 04D2; 04D3; Case map */ + { 0x0004D4, 0x0004D4, { 0x0004D5 } }, /* 04D4; 04D5; Case map */ + { 0x0004D6, 0x0004D6, { 0x0004D7 } }, /* 04D6; 04D7; Case map */ + { 0x0004D8, 0x0004D8, { 0x0004D9 } }, /* 04D8; 04D9; Case map */ + { 0x0004DA, 0x0004DA, { 0x0004DB } }, /* 04DA; 04DB; Case map */ + { 0x0004DC, 0x0004DC, { 0x0004DD } }, /* 04DC; 04DD; Case map */ + { 0x0004DE, 0x0004DE, { 0x0004DF } }, /* 04DE; 04DF; Case map */ + { 0x0004E0, 0x0004E0, { 0x0004E1 } }, /* 04E0; 04E1; Case map */ + { 0x0004E2, 0x0004E2, { 0x0004E3 } }, /* 04E2; 04E3; Case map */ + { 0x0004E4, 0x0004E4, { 0x0004E5 } }, /* 04E4; 04E5; Case map */ + { 0x0004E6, 0x0004E6, { 0x0004E7 } }, /* 04E6; 04E7; Case map */ + { 0x0004E8, 0x0004E8, { 0x0004E9 } }, /* 04E8; 04E9; Case map */ + { 0x0004EA, 0x0004EA, { 0x0004EB } }, /* 04EA; 04EB; Case map */ + { 0x0004EC, 0x0004EC, { 0x0004ED } }, /* 04EC; 04ED; Case map */ + { 0x0004EE, 0x0004EE, { 0x0004EF } }, /* 04EE; 04EF; Case map */ + { 0x0004F0, 0x0004F0, { 0x0004F1 } }, /* 04F0; 04F1; Case map */ + { 0x0004F2, 0x0004F2, { 0x0004F3 } }, /* 04F2; 04F3; Case map */ + { 0x0004F4, 0x0004F4, { 0x0004F5 } }, /* 04F4; 04F5; Case map */ + { 0x0004F8, 0x0004F8, { 0x0004F9 } }, /* 04F8; 04F9; Case map */ + { 0x000500, 0x000500, { 0x000501 } }, /* 0500; 0501; Case map */ + { 0x000502, 0x000502, { 0x000503 } }, /* 0502; 0503; Case map */ + { 0x000504, 0x000504, { 0x000505 } }, /* 0504; 0505; Case map */ + { 0x000506, 0x000506, { 0x000507 } }, /* 0506; 0507; Case map */ + { 0x000508, 0x000508, { 0x000509 } }, /* 0508; 0509; Case map */ + { 0x00050A, 0x00050A, { 0x00050B } }, /* 050A; 050B; Case map */ + { 0x00050C, 0x00050C, { 0x00050D } }, /* 050C; 050D; Case map */ + { 0x00050E, 0x00050E, { 0x00050F } }, /* 050E; 050F; Case map */ + { 0x000531, 0x000531, { 0x000561 } }, /* 0531; 0561; Case map */ + { 0x000532, 0x000532, { 0x000562 } }, /* 0532; 0562; Case map */ + { 0x000533, 0x000533, { 0x000563 } }, /* 0533; 0563; Case map */ + { 0x000534, 0x000534, { 0x000564 } }, /* 0534; 0564; Case map */ + { 0x000535, 0x000535, { 0x000565 } }, /* 0535; 0565; Case map */ + { 0x000536, 0x000536, { 0x000566 } }, /* 0536; 0566; Case map */ + { 0x000537, 0x000537, { 0x000567 } }, /* 0537; 0567; Case map */ + { 0x000538, 0x000538, { 0x000568 } }, /* 0538; 0568; Case map */ + { 0x000539, 0x000539, { 0x000569 } }, /* 0539; 0569; Case map */ + { 0x00053A, 0x00053A, { 0x00056A } }, /* 053A; 056A; Case map */ + { 0x00053B, 0x00053B, { 0x00056B } }, /* 053B; 056B; Case map */ + { 0x00053C, 0x00053C, { 0x00056C } }, /* 053C; 056C; Case map */ + { 0x00053D, 0x00053D, { 0x00056D } }, /* 053D; 056D; Case map */ + { 0x00053E, 0x00053E, { 0x00056E } }, /* 053E; 056E; Case map */ + { 0x00053F, 0x00053F, { 0x00056F } }, /* 053F; 056F; Case map */ + { 0x000540, 0x000540, { 0x000570 } }, /* 0540; 0570; Case map */ + { 0x000541, 0x000541, { 0x000571 } }, /* 0541; 0571; Case map */ + { 0x000542, 0x000542, { 0x000572 } }, /* 0542; 0572; Case map */ + { 0x000543, 0x000543, { 0x000573 } }, /* 0543; 0573; Case map */ + { 0x000544, 0x000544, { 0x000574 } }, /* 0544; 0574; Case map */ + { 0x000545, 0x000545, { 0x000575 } }, /* 0545; 0575; Case map */ + { 0x000546, 0x000546, { 0x000576 } }, /* 0546; 0576; Case map */ + { 0x000547, 0x000547, { 0x000577 } }, /* 0547; 0577; Case map */ + { 0x000548, 0x000548, { 0x000578 } }, /* 0548; 0578; Case map */ + { 0x000549, 0x000549, { 0x000579 } }, /* 0549; 0579; Case map */ + { 0x00054A, 0x00054A, { 0x00057A } }, /* 054A; 057A; Case map */ + { 0x00054B, 0x00054B, { 0x00057B } }, /* 054B; 057B; Case map */ + { 0x00054C, 0x00054C, { 0x00057C } }, /* 054C; 057C; Case map */ + { 0x00054D, 0x00054D, { 0x00057D } }, /* 054D; 057D; Case map */ + { 0x00054E, 0x00054E, { 0x00057E } }, /* 054E; 057E; Case map */ + { 0x00054F, 0x00054F, { 0x00057F } }, /* 054F; 057F; Case map */ + { 0x000550, 0x000550, { 0x000580 } }, /* 0550; 0580; Case map */ + { 0x000551, 0x000551, { 0x000581 } }, /* 0551; 0581; Case map */ + { 0x000552, 0x000552, { 0x000582 } }, /* 0552; 0582; Case map */ + { 0x000553, 0x000553, { 0x000583 } }, /* 0553; 0583; Case map */ + { 0x000554, 0x000554, { 0x000584 } }, /* 0554; 0584; Case map */ + { 0x000555, 0x000555, { 0x000585 } }, /* 0555; 0585; Case map */ + { 0x000556, 0x000556, { 0x000586 } }, /* 0556; 0586; Case map */ + { 0x000587, + 0x000587, + { 0x000565, /* 0587; 0565 0582; Case map */ + 0x000582 } }, + { 0x001E00, 0x001E00, { 0x001E01 } }, /* 1E00; 1E01; Case map */ + { 0x001E02, 0x001E02, { 0x001E03 } }, /* 1E02; 1E03; Case map */ + { 0x001E04, 0x001E04, { 0x001E05 } }, /* 1E04; 1E05; Case map */ + { 0x001E06, 0x001E06, { 0x001E07 } }, /* 1E06; 1E07; Case map */ + { 0x001E08, 0x001E08, { 0x001E09 } }, /* 1E08; 1E09; Case map */ + { 0x001E0A, 0x001E0A, { 0x001E0B } }, /* 1E0A; 1E0B; Case map */ + { 0x001E0C, 0x001E0C, { 0x001E0D } }, /* 1E0C; 1E0D; Case map */ + { 0x001E0E, 0x001E0E, { 0x001E0F } }, /* 1E0E; 1E0F; Case map */ + { 0x001E10, 0x001E10, { 0x001E11 } }, /* 1E10; 1E11; Case map */ + { 0x001E12, 0x001E12, { 0x001E13 } }, /* 1E12; 1E13; Case map */ + { 0x001E14, 0x001E14, { 0x001E15 } }, /* 1E14; 1E15; Case map */ + { 0x001E16, 0x001E16, { 0x001E17 } }, /* 1E16; 1E17; Case map */ + { 0x001E18, 0x001E18, { 0x001E19 } }, /* 1E18; 1E19; Case map */ + { 0x001E1A, 0x001E1A, { 0x001E1B } }, /* 1E1A; 1E1B; Case map */ + { 0x001E1C, 0x001E1C, { 0x001E1D } }, /* 1E1C; 1E1D; Case map */ + { 0x001E1E, 0x001E1E, { 0x001E1F } }, /* 1E1E; 1E1F; Case map */ + { 0x001E20, 0x001E20, { 0x001E21 } }, /* 1E20; 1E21; Case map */ + { 0x001E22, 0x001E22, { 0x001E23 } }, /* 1E22; 1E23; Case map */ + { 0x001E24, 0x001E24, { 0x001E25 } }, /* 1E24; 1E25; Case map */ + { 0x001E26, 0x001E26, { 0x001E27 } }, /* 1E26; 1E27; Case map */ + { 0x001E28, 0x001E28, { 0x001E29 } }, /* 1E28; 1E29; Case map */ + { 0x001E2A, 0x001E2A, { 0x001E2B } }, /* 1E2A; 1E2B; Case map */ + { 0x001E2C, 0x001E2C, { 0x001E2D } }, /* 1E2C; 1E2D; Case map */ + { 0x001E2E, 0x001E2E, { 0x001E2F } }, /* 1E2E; 1E2F; Case map */ + { 0x001E30, 0x001E30, { 0x001E31 } }, /* 1E30; 1E31; Case map */ + { 0x001E32, 0x001E32, { 0x001E33 } }, /* 1E32; 1E33; Case map */ + { 0x001E34, 0x001E34, { 0x001E35 } }, /* 1E34; 1E35; Case map */ + { 0x001E36, 0x001E36, { 0x001E37 } }, /* 1E36; 1E37; Case map */ + { 0x001E38, 0x001E38, { 0x001E39 } }, /* 1E38; 1E39; Case map */ + { 0x001E3A, 0x001E3A, { 0x001E3B } }, /* 1E3A; 1E3B; Case map */ + { 0x001E3C, 0x001E3C, { 0x001E3D } }, /* 1E3C; 1E3D; Case map */ + { 0x001E3E, 0x001E3E, { 0x001E3F } }, /* 1E3E; 1E3F; Case map */ + { 0x001E40, 0x001E40, { 0x001E41 } }, /* 1E40; 1E41; Case map */ + { 0x001E42, 0x001E42, { 0x001E43 } }, /* 1E42; 1E43; Case map */ + { 0x001E44, 0x001E44, { 0x001E45 } }, /* 1E44; 1E45; Case map */ + { 0x001E46, 0x001E46, { 0x001E47 } }, /* 1E46; 1E47; Case map */ + { 0x001E48, 0x001E48, { 0x001E49 } }, /* 1E48; 1E49; Case map */ + { 0x001E4A, 0x001E4A, { 0x001E4B } }, /* 1E4A; 1E4B; Case map */ + { 0x001E4C, 0x001E4C, { 0x001E4D } }, /* 1E4C; 1E4D; Case map */ + { 0x001E4E, 0x001E4E, { 0x001E4F } }, /* 1E4E; 1E4F; Case map */ + { 0x001E50, 0x001E50, { 0x001E51 } }, /* 1E50; 1E51; Case map */ + { 0x001E52, 0x001E52, { 0x001E53 } }, /* 1E52; 1E53; Case map */ + { 0x001E54, 0x001E54, { 0x001E55 } }, /* 1E54; 1E55; Case map */ + { 0x001E56, 0x001E56, { 0x001E57 } }, /* 1E56; 1E57; Case map */ + { 0x001E58, 0x001E58, { 0x001E59 } }, /* 1E58; 1E59; Case map */ + { 0x001E5A, 0x001E5A, { 0x001E5B } }, /* 1E5A; 1E5B; Case map */ + { 0x001E5C, 0x001E5C, { 0x001E5D } }, /* 1E5C; 1E5D; Case map */ + { 0x001E5E, 0x001E5E, { 0x001E5F } }, /* 1E5E; 1E5F; Case map */ + { 0x001E60, 0x001E60, { 0x001E61 } }, /* 1E60; 1E61; Case map */ + { 0x001E62, 0x001E62, { 0x001E63 } }, /* 1E62; 1E63; Case map */ + { 0x001E64, 0x001E64, { 0x001E65 } }, /* 1E64; 1E65; Case map */ + { 0x001E66, 0x001E66, { 0x001E67 } }, /* 1E66; 1E67; Case map */ + { 0x001E68, 0x001E68, { 0x001E69 } }, /* 1E68; 1E69; Case map */ + { 0x001E6A, 0x001E6A, { 0x001E6B } }, /* 1E6A; 1E6B; Case map */ + { 0x001E6C, 0x001E6C, { 0x001E6D } }, /* 1E6C; 1E6D; Case map */ + { 0x001E6E, 0x001E6E, { 0x001E6F } }, /* 1E6E; 1E6F; Case map */ + { 0x001E70, 0x001E70, { 0x001E71 } }, /* 1E70; 1E71; Case map */ + { 0x001E72, 0x001E72, { 0x001E73 } }, /* 1E72; 1E73; Case map */ + { 0x001E74, 0x001E74, { 0x001E75 } }, /* 1E74; 1E75; Case map */ + { 0x001E76, 0x001E76, { 0x001E77 } }, /* 1E76; 1E77; Case map */ + { 0x001E78, 0x001E78, { 0x001E79 } }, /* 1E78; 1E79; Case map */ + { 0x001E7A, 0x001E7A, { 0x001E7B } }, /* 1E7A; 1E7B; Case map */ + { 0x001E7C, 0x001E7C, { 0x001E7D } }, /* 1E7C; 1E7D; Case map */ + { 0x001E7E, 0x001E7E, { 0x001E7F } }, /* 1E7E; 1E7F; Case map */ + { 0x001E80, 0x001E80, { 0x001E81 } }, /* 1E80; 1E81; Case map */ + { 0x001E82, 0x001E82, { 0x001E83 } }, /* 1E82; 1E83; Case map */ + { 0x001E84, 0x001E84, { 0x001E85 } }, /* 1E84; 1E85; Case map */ + { 0x001E86, 0x001E86, { 0x001E87 } }, /* 1E86; 1E87; Case map */ + { 0x001E88, 0x001E88, { 0x001E89 } }, /* 1E88; 1E89; Case map */ + { 0x001E8A, 0x001E8A, { 0x001E8B } }, /* 1E8A; 1E8B; Case map */ + { 0x001E8C, 0x001E8C, { 0x001E8D } }, /* 1E8C; 1E8D; Case map */ + { 0x001E8E, 0x001E8E, { 0x001E8F } }, /* 1E8E; 1E8F; Case map */ + { 0x001E90, 0x001E90, { 0x001E91 } }, /* 1E90; 1E91; Case map */ + { 0x001E92, 0x001E92, { 0x001E93 } }, /* 1E92; 1E93; Case map */ + { 0x001E94, 0x001E94, { 0x001E95 } }, /* 1E94; 1E95; Case map */ + { 0x001E96, + 0x001E96, + { 0x000068, /* 1E96; 0068 0331; Case map */ + 0x000331 } }, + { 0x001E97, + 0x001E97, + { 0x000074, /* 1E97; 0074 0308; Case map */ + 0x000308 } }, + { 0x001E98, + 0x001E98, + { 0x000077, /* 1E98; 0077 030A; Case map */ + 0x00030A } }, + { 0x001E99, + 0x001E99, + { 0x000079, /* 1E99; 0079 030A; Case map */ + 0x00030A } }, + { 0x001E9A, + 0x001E9A, + { 0x000061, /* 1E9A; 0061 02BE; Case map */ + 0x0002BE } }, + { 0x001E9B, 0x001E9B, { 0x001E61 } }, /* 1E9B; 1E61; Case map */ + { 0x001EA0, 0x001EA0, { 0x001EA1 } }, /* 1EA0; 1EA1; Case map */ + { 0x001EA2, 0x001EA2, { 0x001EA3 } }, /* 1EA2; 1EA3; Case map */ + { 0x001EA4, 0x001EA4, { 0x001EA5 } }, /* 1EA4; 1EA5; Case map */ + { 0x001EA6, 0x001EA6, { 0x001EA7 } }, /* 1EA6; 1EA7; Case map */ + { 0x001EA8, 0x001EA8, { 0x001EA9 } }, /* 1EA8; 1EA9; Case map */ + { 0x001EAA, 0x001EAA, { 0x001EAB } }, /* 1EAA; 1EAB; Case map */ + { 0x001EAC, 0x001EAC, { 0x001EAD } }, /* 1EAC; 1EAD; Case map */ + { 0x001EAE, 0x001EAE, { 0x001EAF } }, /* 1EAE; 1EAF; Case map */ + { 0x001EB0, 0x001EB0, { 0x001EB1 } }, /* 1EB0; 1EB1; Case map */ + { 0x001EB2, 0x001EB2, { 0x001EB3 } }, /* 1EB2; 1EB3; Case map */ + { 0x001EB4, 0x001EB4, { 0x001EB5 } }, /* 1EB4; 1EB5; Case map */ + { 0x001EB6, 0x001EB6, { 0x001EB7 } }, /* 1EB6; 1EB7; Case map */ + { 0x001EB8, 0x001EB8, { 0x001EB9 } }, /* 1EB8; 1EB9; Case map */ + { 0x001EBA, 0x001EBA, { 0x001EBB } }, /* 1EBA; 1EBB; Case map */ + { 0x001EBC, 0x001EBC, { 0x001EBD } }, /* 1EBC; 1EBD; Case map */ + { 0x001EBE, 0x001EBE, { 0x001EBF } }, /* 1EBE; 1EBF; Case map */ + { 0x001EC0, 0x001EC0, { 0x001EC1 } }, /* 1EC0; 1EC1; Case map */ + { 0x001EC2, 0x001EC2, { 0x001EC3 } }, /* 1EC2; 1EC3; Case map */ + { 0x001EC4, 0x001EC4, { 0x001EC5 } }, /* 1EC4; 1EC5; Case map */ + { 0x001EC6, 0x001EC6, { 0x001EC7 } }, /* 1EC6; 1EC7; Case map */ + { 0x001EC8, 0x001EC8, { 0x001EC9 } }, /* 1EC8; 1EC9; Case map */ + { 0x001ECA, 0x001ECA, { 0x001ECB } }, /* 1ECA; 1ECB; Case map */ + { 0x001ECC, 0x001ECC, { 0x001ECD } }, /* 1ECC; 1ECD; Case map */ + { 0x001ECE, 0x001ECE, { 0x001ECF } }, /* 1ECE; 1ECF; Case map */ + { 0x001ED0, 0x001ED0, { 0x001ED1 } }, /* 1ED0; 1ED1; Case map */ + { 0x001ED2, 0x001ED2, { 0x001ED3 } }, /* 1ED2; 1ED3; Case map */ + { 0x001ED4, 0x001ED4, { 0x001ED5 } }, /* 1ED4; 1ED5; Case map */ + { 0x001ED6, 0x001ED6, { 0x001ED7 } }, /* 1ED6; 1ED7; Case map */ + { 0x001ED8, 0x001ED8, { 0x001ED9 } }, /* 1ED8; 1ED9; Case map */ + { 0x001EDA, 0x001EDA, { 0x001EDB } }, /* 1EDA; 1EDB; Case map */ + { 0x001EDC, 0x001EDC, { 0x001EDD } }, /* 1EDC; 1EDD; Case map */ + { 0x001EDE, 0x001EDE, { 0x001EDF } }, /* 1EDE; 1EDF; Case map */ + { 0x001EE0, 0x001EE0, { 0x001EE1 } }, /* 1EE0; 1EE1; Case map */ + { 0x001EE2, 0x001EE2, { 0x001EE3 } }, /* 1EE2; 1EE3; Case map */ + { 0x001EE4, 0x001EE4, { 0x001EE5 } }, /* 1EE4; 1EE5; Case map */ + { 0x001EE6, 0x001EE6, { 0x001EE7 } }, /* 1EE6; 1EE7; Case map */ + { 0x001EE8, 0x001EE8, { 0x001EE9 } }, /* 1EE8; 1EE9; Case map */ + { 0x001EEA, 0x001EEA, { 0x001EEB } }, /* 1EEA; 1EEB; Case map */ + { 0x001EEC, 0x001EEC, { 0x001EED } }, /* 1EEC; 1EED; Case map */ + { 0x001EEE, 0x001EEE, { 0x001EEF } }, /* 1EEE; 1EEF; Case map */ + { 0x001EF0, 0x001EF0, { 0x001EF1 } }, /* 1EF0; 1EF1; Case map */ + { 0x001EF2, 0x001EF2, { 0x001EF3 } }, /* 1EF2; 1EF3; Case map */ + { 0x001EF4, 0x001EF4, { 0x001EF5 } }, /* 1EF4; 1EF5; Case map */ + { 0x001EF6, 0x001EF6, { 0x001EF7 } }, /* 1EF6; 1EF7; Case map */ + { 0x001EF8, 0x001EF8, { 0x001EF9 } }, /* 1EF8; 1EF9; Case map */ + { 0x001F08, 0x001F08, { 0x001F00 } }, /* 1F08; 1F00; Case map */ + { 0x001F09, 0x001F09, { 0x001F01 } }, /* 1F09; 1F01; Case map */ + { 0x001F0A, 0x001F0A, { 0x001F02 } }, /* 1F0A; 1F02; Case map */ + { 0x001F0B, 0x001F0B, { 0x001F03 } }, /* 1F0B; 1F03; Case map */ + { 0x001F0C, 0x001F0C, { 0x001F04 } }, /* 1F0C; 1F04; Case map */ + { 0x001F0D, 0x001F0D, { 0x001F05 } }, /* 1F0D; 1F05; Case map */ + { 0x001F0E, 0x001F0E, { 0x001F06 } }, /* 1F0E; 1F06; Case map */ + { 0x001F0F, 0x001F0F, { 0x001F07 } }, /* 1F0F; 1F07; Case map */ + { 0x001F18, 0x001F18, { 0x001F10 } }, /* 1F18; 1F10; Case map */ + { 0x001F19, 0x001F19, { 0x001F11 } }, /* 1F19; 1F11; Case map */ + { 0x001F1A, 0x001F1A, { 0x001F12 } }, /* 1F1A; 1F12; Case map */ + { 0x001F1B, 0x001F1B, { 0x001F13 } }, /* 1F1B; 1F13; Case map */ + { 0x001F1C, 0x001F1C, { 0x001F14 } }, /* 1F1C; 1F14; Case map */ + { 0x001F1D, 0x001F1D, { 0x001F15 } }, /* 1F1D; 1F15; Case map */ + { 0x001F28, 0x001F28, { 0x001F20 } }, /* 1F28; 1F20; Case map */ + { 0x001F29, 0x001F29, { 0x001F21 } }, /* 1F29; 1F21; Case map */ + { 0x001F2A, 0x001F2A, { 0x001F22 } }, /* 1F2A; 1F22; Case map */ + { 0x001F2B, 0x001F2B, { 0x001F23 } }, /* 1F2B; 1F23; Case map */ + { 0x001F2C, 0x001F2C, { 0x001F24 } }, /* 1F2C; 1F24; Case map */ + { 0x001F2D, 0x001F2D, { 0x001F25 } }, /* 1F2D; 1F25; Case map */ + { 0x001F2E, 0x001F2E, { 0x001F26 } }, /* 1F2E; 1F26; Case map */ + { 0x001F2F, 0x001F2F, { 0x001F27 } }, /* 1F2F; 1F27; Case map */ + { 0x001F38, 0x001F38, { 0x001F30 } }, /* 1F38; 1F30; Case map */ + { 0x001F39, 0x001F39, { 0x001F31 } }, /* 1F39; 1F31; Case map */ + { 0x001F3A, 0x001F3A, { 0x001F32 } }, /* 1F3A; 1F32; Case map */ + { 0x001F3B, 0x001F3B, { 0x001F33 } }, /* 1F3B; 1F33; Case map */ + { 0x001F3C, 0x001F3C, { 0x001F34 } }, /* 1F3C; 1F34; Case map */ + { 0x001F3D, 0x001F3D, { 0x001F35 } }, /* 1F3D; 1F35; Case map */ + { 0x001F3E, 0x001F3E, { 0x001F36 } }, /* 1F3E; 1F36; Case map */ + { 0x001F3F, 0x001F3F, { 0x001F37 } }, /* 1F3F; 1F37; Case map */ + { 0x001F48, 0x001F48, { 0x001F40 } }, /* 1F48; 1F40; Case map */ + { 0x001F49, 0x001F49, { 0x001F41 } }, /* 1F49; 1F41; Case map */ + { 0x001F4A, 0x001F4A, { 0x001F42 } }, /* 1F4A; 1F42; Case map */ + { 0x001F4B, 0x001F4B, { 0x001F43 } }, /* 1F4B; 1F43; Case map */ + { 0x001F4C, 0x001F4C, { 0x001F44 } }, /* 1F4C; 1F44; Case map */ + { 0x001F4D, 0x001F4D, { 0x001F45 } }, /* 1F4D; 1F45; Case map */ + { 0x001F50, + 0x001F50, + { 0x0003C5, /* 1F50; 03C5 0313; Case map */ + 0x000313 } }, + { 0x001F52, + 0x001F52, + { 0x0003C5, /* 1F52; 03C5 0313 0300; Case map */ + 0x000313, 0x000300 } }, + { 0x001F54, + 0x001F54, + { 0x0003C5, /* 1F54; 03C5 0313 0301; Case map */ + 0x000313, 0x000301 } }, + { 0x001F56, + 0x001F56, + { 0x0003C5, /* 1F56; 03C5 0313 0342; Case map */ + 0x000313, 0x000342 } }, + { 0x001F59, 0x001F59, { 0x001F51 } }, /* 1F59; 1F51; Case map */ + { 0x001F5B, 0x001F5B, { 0x001F53 } }, /* 1F5B; 1F53; Case map */ + { 0x001F5D, 0x001F5D, { 0x001F55 } }, /* 1F5D; 1F55; Case map */ + { 0x001F5F, 0x001F5F, { 0x001F57 } }, /* 1F5F; 1F57; Case map */ + { 0x001F68, 0x001F68, { 0x001F60 } }, /* 1F68; 1F60; Case map */ + { 0x001F69, 0x001F69, { 0x001F61 } }, /* 1F69; 1F61; Case map */ + { 0x001F6A, 0x001F6A, { 0x001F62 } }, /* 1F6A; 1F62; Case map */ + { 0x001F6B, 0x001F6B, { 0x001F63 } }, /* 1F6B; 1F63; Case map */ + { 0x001F6C, 0x001F6C, { 0x001F64 } }, /* 1F6C; 1F64; Case map */ + { 0x001F6D, 0x001F6D, { 0x001F65 } }, /* 1F6D; 1F65; Case map */ + { 0x001F6E, 0x001F6E, { 0x001F66 } }, /* 1F6E; 1F66; Case map */ + { 0x001F6F, 0x001F6F, { 0x001F67 } }, /* 1F6F; 1F67; Case map */ + { 0x001F80, + 0x001F80, + { 0x001F00, /* 1F80; 1F00 03B9; Case map */ + 0x0003B9 } }, + { 0x001F81, + 0x001F81, + { 0x001F01, /* 1F81; 1F01 03B9; Case map */ + 0x0003B9 } }, + { 0x001F82, + 0x001F82, + { 0x001F02, /* 1F82; 1F02 03B9; Case map */ + 0x0003B9 } }, + { 0x001F83, + 0x001F83, + { 0x001F03, /* 1F83; 1F03 03B9; Case map */ + 0x0003B9 } }, + { 0x001F84, + 0x001F84, + { 0x001F04, /* 1F84; 1F04 03B9; Case map */ + 0x0003B9 } }, + { 0x001F85, + 0x001F85, + { 0x001F05, /* 1F85; 1F05 03B9; Case map */ + 0x0003B9 } }, + { 0x001F86, + 0x001F86, + { 0x001F06, /* 1F86; 1F06 03B9; Case map */ + 0x0003B9 } }, + { 0x001F87, + 0x001F87, + { 0x001F07, /* 1F87; 1F07 03B9; Case map */ + 0x0003B9 } }, + { 0x001F88, + 0x001F88, + { 0x001F00, /* 1F88; 1F00 03B9; Case map */ + 0x0003B9 } }, + { 0x001F89, + 0x001F89, + { 0x001F01, /* 1F89; 1F01 03B9; Case map */ + 0x0003B9 } }, + { 0x001F8A, + 0x001F8A, + { 0x001F02, /* 1F8A; 1F02 03B9; Case map */ + 0x0003B9 } }, + { 0x001F8B, + 0x001F8B, + { 0x001F03, /* 1F8B; 1F03 03B9; Case map */ + 0x0003B9 } }, + { 0x001F8C, + 0x001F8C, + { 0x001F04, /* 1F8C; 1F04 03B9; Case map */ + 0x0003B9 } }, + { 0x001F8D, + 0x001F8D, + { 0x001F05, /* 1F8D; 1F05 03B9; Case map */ + 0x0003B9 } }, + { 0x001F8E, + 0x001F8E, + { 0x001F06, /* 1F8E; 1F06 03B9; Case map */ + 0x0003B9 } }, + { 0x001F8F, + 0x001F8F, + { 0x001F07, /* 1F8F; 1F07 03B9; Case map */ + 0x0003B9 } }, + { 0x001F90, + 0x001F90, + { 0x001F20, /* 1F90; 1F20 03B9; Case map */ + 0x0003B9 } }, + { 0x001F91, + 0x001F91, + { 0x001F21, /* 1F91; 1F21 03B9; Case map */ + 0x0003B9 } }, + { 0x001F92, + 0x001F92, + { 0x001F22, /* 1F92; 1F22 03B9; Case map */ + 0x0003B9 } }, + { 0x001F93, + 0x001F93, + { 0x001F23, /* 1F93; 1F23 03B9; Case map */ + 0x0003B9 } }, + { 0x001F94, + 0x001F94, + { 0x001F24, /* 1F94; 1F24 03B9; Case map */ + 0x0003B9 } }, + { 0x001F95, + 0x001F95, + { 0x001F25, /* 1F95; 1F25 03B9; Case map */ + 0x0003B9 } }, + { 0x001F96, + 0x001F96, + { 0x001F26, /* 1F96; 1F26 03B9; Case map */ + 0x0003B9 } }, + { 0x001F97, + 0x001F97, + { 0x001F27, /* 1F97; 1F27 03B9; Case map */ + 0x0003B9 } }, + { 0x001F98, + 0x001F98, + { 0x001F20, /* 1F98; 1F20 03B9; Case map */ + 0x0003B9 } }, + { 0x001F99, + 0x001F99, + { 0x001F21, /* 1F99; 1F21 03B9; Case map */ + 0x0003B9 } }, + { 0x001F9A, + 0x001F9A, + { 0x001F22, /* 1F9A; 1F22 03B9; Case map */ + 0x0003B9 } }, + { 0x001F9B, + 0x001F9B, + { 0x001F23, /* 1F9B; 1F23 03B9; Case map */ + 0x0003B9 } }, + { 0x001F9C, + 0x001F9C, + { 0x001F24, /* 1F9C; 1F24 03B9; Case map */ + 0x0003B9 } }, + { 0x001F9D, + 0x001F9D, + { 0x001F25, /* 1F9D; 1F25 03B9; Case map */ + 0x0003B9 } }, + { 0x001F9E, + 0x001F9E, + { 0x001F26, /* 1F9E; 1F26 03B9; Case map */ + 0x0003B9 } }, + { 0x001F9F, + 0x001F9F, + { 0x001F27, /* 1F9F; 1F27 03B9; Case map */ + 0x0003B9 } }, + { 0x001FA0, + 0x001FA0, + { 0x001F60, /* 1FA0; 1F60 03B9; Case map */ + 0x0003B9 } }, + { 0x001FA1, + 0x001FA1, + { 0x001F61, /* 1FA1; 1F61 03B9; Case map */ + 0x0003B9 } }, + { 0x001FA2, + 0x001FA2, + { 0x001F62, /* 1FA2; 1F62 03B9; Case map */ + 0x0003B9 } }, + { 0x001FA3, + 0x001FA3, + { 0x001F63, /* 1FA3; 1F63 03B9; Case map */ + 0x0003B9 } }, + { 0x001FA4, + 0x001FA4, + { 0x001F64, /* 1FA4; 1F64 03B9; Case map */ + 0x0003B9 } }, + { 0x001FA5, + 0x001FA5, + { 0x001F65, /* 1FA5; 1F65 03B9; Case map */ + 0x0003B9 } }, + { 0x001FA6, + 0x001FA6, + { 0x001F66, /* 1FA6; 1F66 03B9; Case map */ + 0x0003B9 } }, + { 0x001FA7, + 0x001FA7, + { 0x001F67, /* 1FA7; 1F67 03B9; Case map */ + 0x0003B9 } }, + { 0x001FA8, + 0x001FA8, + { 0x001F60, /* 1FA8; 1F60 03B9; Case map */ + 0x0003B9 } }, + { 0x001FA9, + 0x001FA9, + { 0x001F61, /* 1FA9; 1F61 03B9; Case map */ + 0x0003B9 } }, + { 0x001FAA, + 0x001FAA, + { 0x001F62, /* 1FAA; 1F62 03B9; Case map */ + 0x0003B9 } }, + { 0x001FAB, + 0x001FAB, + { 0x001F63, /* 1FAB; 1F63 03B9; Case map */ + 0x0003B9 } }, + { 0x001FAC, + 0x001FAC, + { 0x001F64, /* 1FAC; 1F64 03B9; Case map */ + 0x0003B9 } }, + { 0x001FAD, + 0x001FAD, + { 0x001F65, /* 1FAD; 1F65 03B9; Case map */ + 0x0003B9 } }, + { 0x001FAE, + 0x001FAE, + { 0x001F66, /* 1FAE; 1F66 03B9; Case map */ + 0x0003B9 } }, + { 0x001FAF, + 0x001FAF, + { 0x001F67, /* 1FAF; 1F67 03B9; Case map */ + 0x0003B9 } }, + { 0x001FB2, + 0x001FB2, + { 0x001F70, /* 1FB2; 1F70 03B9; Case map */ + 0x0003B9 } }, + { 0x001FB3, + 0x001FB3, + { 0x0003B1, /* 1FB3; 03B1 03B9; Case map */ + 0x0003B9 } }, + { 0x001FB4, + 0x001FB4, + { 0x0003AC, /* 1FB4; 03AC 03B9; Case map */ + 0x0003B9 } }, + { 0x001FB6, + 0x001FB6, + { 0x0003B1, /* 1FB6; 03B1 0342; Case map */ + 0x000342 } }, + { 0x001FB7, + 0x001FB7, + { 0x0003B1, /* 1FB7; 03B1 0342 03B9; Case map */ + 0x000342, 0x0003B9 } }, + { 0x001FB8, 0x001FB8, { 0x001FB0 } }, /* 1FB8; 1FB0; Case map */ + { 0x001FB9, 0x001FB9, { 0x001FB1 } }, /* 1FB9; 1FB1; Case map */ + { 0x001FBA, 0x001FBA, { 0x001F70 } }, /* 1FBA; 1F70; Case map */ + { 0x001FBB, 0x001FBB, { 0x001F71 } }, /* 1FBB; 1F71; Case map */ + { 0x001FBC, + 0x001FBC, + { 0x0003B1, /* 1FBC; 03B1 03B9; Case map */ + 0x0003B9 } }, + { 0x001FBE, 0x001FBE, { 0x0003B9 } }, /* 1FBE; 03B9; Case map */ + { 0x001FC2, + 0x001FC2, + { 0x001F74, /* 1FC2; 1F74 03B9; Case map */ + 0x0003B9 } }, + { 0x001FC3, + 0x001FC3, + { 0x0003B7, /* 1FC3; 03B7 03B9; Case map */ + 0x0003B9 } }, + { 0x001FC4, + 0x001FC4, + { 0x0003AE, /* 1FC4; 03AE 03B9; Case map */ + 0x0003B9 } }, + { 0x001FC6, + 0x001FC6, + { 0x0003B7, /* 1FC6; 03B7 0342; Case map */ + 0x000342 } }, + { 0x001FC7, + 0x001FC7, + { 0x0003B7, /* 1FC7; 03B7 0342 03B9; Case map */ + 0x000342, 0x0003B9 } }, + { 0x001FC8, 0x001FC8, { 0x001F72 } }, /* 1FC8; 1F72; Case map */ + { 0x001FC9, 0x001FC9, { 0x001F73 } }, /* 1FC9; 1F73; Case map */ + { 0x001FCA, 0x001FCA, { 0x001F74 } }, /* 1FCA; 1F74; Case map */ + { 0x001FCB, 0x001FCB, { 0x001F75 } }, /* 1FCB; 1F75; Case map */ + { 0x001FCC, + 0x001FCC, + { 0x0003B7, /* 1FCC; 03B7 03B9; Case map */ + 0x0003B9 } }, + { 0x001FD2, + 0x001FD2, + { 0x0003B9, /* 1FD2; 03B9 0308 0300; Case map */ + 0x000308, 0x000300 } }, + { 0x001FD3, + 0x001FD3, + { 0x0003B9, /* 1FD3; 03B9 0308 0301; Case map */ + 0x000308, 0x000301 } }, + { 0x001FD6, + 0x001FD6, + { 0x0003B9, /* 1FD6; 03B9 0342; Case map */ + 0x000342 } }, + { 0x001FD7, + 0x001FD7, + { 0x0003B9, /* 1FD7; 03B9 0308 0342; Case map */ + 0x000308, 0x000342 } }, + { 0x001FD8, 0x001FD8, { 0x001FD0 } }, /* 1FD8; 1FD0; Case map */ + { 0x001FD9, 0x001FD9, { 0x001FD1 } }, /* 1FD9; 1FD1; Case map */ + { 0x001FDA, 0x001FDA, { 0x001F76 } }, /* 1FDA; 1F76; Case map */ + { 0x001FDB, 0x001FDB, { 0x001F77 } }, /* 1FDB; 1F77; Case map */ + { 0x001FE2, + 0x001FE2, + { 0x0003C5, /* 1FE2; 03C5 0308 0300; Case map */ + 0x000308, 0x000300 } }, + { 0x001FE3, + 0x001FE3, + { 0x0003C5, /* 1FE3; 03C5 0308 0301; Case map */ + 0x000308, 0x000301 } }, + { 0x001FE4, + 0x001FE4, + { 0x0003C1, /* 1FE4; 03C1 0313; Case map */ + 0x000313 } }, + { 0x001FE6, + 0x001FE6, + { 0x0003C5, /* 1FE6; 03C5 0342; Case map */ + 0x000342 } }, + { 0x001FE7, + 0x001FE7, + { 0x0003C5, /* 1FE7; 03C5 0308 0342; Case map */ + 0x000308, 0x000342 } }, + { 0x001FE8, 0x001FE8, { 0x001FE0 } }, /* 1FE8; 1FE0; Case map */ + { 0x001FE9, 0x001FE9, { 0x001FE1 } }, /* 1FE9; 1FE1; Case map */ + { 0x001FEA, 0x001FEA, { 0x001F7A } }, /* 1FEA; 1F7A; Case map */ + { 0x001FEB, 0x001FEB, { 0x001F7B } }, /* 1FEB; 1F7B; Case map */ + { 0x001FEC, 0x001FEC, { 0x001FE5 } }, /* 1FEC; 1FE5; Case map */ + { 0x001FF2, + 0x001FF2, + { 0x001F7C, /* 1FF2; 1F7C 03B9; Case map */ + 0x0003B9 } }, + { 0x001FF3, + 0x001FF3, + { 0x0003C9, /* 1FF3; 03C9 03B9; Case map */ + 0x0003B9 } }, + { 0x001FF4, + 0x001FF4, + { 0x0003CE, /* 1FF4; 03CE 03B9; Case map */ + 0x0003B9 } }, + { 0x001FF6, + 0x001FF6, + { 0x0003C9, /* 1FF6; 03C9 0342; Case map */ + 0x000342 } }, + { 0x001FF7, + 0x001FF7, + { 0x0003C9, /* 1FF7; 03C9 0342 03B9; Case map */ + 0x000342, 0x0003B9 } }, + { 0x001FF8, 0x001FF8, { 0x001F78 } }, /* 1FF8; 1F78; Case map */ + { 0x001FF9, 0x001FF9, { 0x001F79 } }, /* 1FF9; 1F79; Case map */ + { 0x001FFA, 0x001FFA, { 0x001F7C } }, /* 1FFA; 1F7C; Case map */ + { 0x001FFB, 0x001FFB, { 0x001F7D } }, /* 1FFB; 1F7D; Case map */ + { 0x001FFC, + 0x001FFC, + { 0x0003C9, /* 1FFC; 03C9 03B9; Case map */ + 0x0003B9 } }, + { 0x002126, 0x002126, { 0x0003C9 } }, /* 2126; 03C9; Case map */ + { 0x00212A, 0x00212A, { 0x00006B } }, /* 212A; 006B; Case map */ + { 0x00212B, 0x00212B, { 0x0000E5 } }, /* 212B; 00E5; Case map */ + { 0x002160, 0x002160, { 0x002170 } }, /* 2160; 2170; Case map */ + { 0x002161, 0x002161, { 0x002171 } }, /* 2161; 2171; Case map */ + { 0x002162, 0x002162, { 0x002172 } }, /* 2162; 2172; Case map */ + { 0x002163, 0x002163, { 0x002173 } }, /* 2163; 2173; Case map */ + { 0x002164, 0x002164, { 0x002174 } }, /* 2164; 2174; Case map */ + { 0x002165, 0x002165, { 0x002175 } }, /* 2165; 2175; Case map */ + { 0x002166, 0x002166, { 0x002176 } }, /* 2166; 2176; Case map */ + { 0x002167, 0x002167, { 0x002177 } }, /* 2167; 2177; Case map */ + { 0x002168, 0x002168, { 0x002178 } }, /* 2168; 2178; Case map */ + { 0x002169, 0x002169, { 0x002179 } }, /* 2169; 2179; Case map */ + { 0x00216A, 0x00216A, { 0x00217A } }, /* 216A; 217A; Case map */ + { 0x00216B, 0x00216B, { 0x00217B } }, /* 216B; 217B; Case map */ + { 0x00216C, 0x00216C, { 0x00217C } }, /* 216C; 217C; Case map */ + { 0x00216D, 0x00216D, { 0x00217D } }, /* 216D; 217D; Case map */ + { 0x00216E, 0x00216E, { 0x00217E } }, /* 216E; 217E; Case map */ + { 0x00216F, 0x00216F, { 0x00217F } }, /* 216F; 217F; Case map */ + { 0x0024B6, 0x0024B6, { 0x0024D0 } }, /* 24B6; 24D0; Case map */ + { 0x0024B7, 0x0024B7, { 0x0024D1 } }, /* 24B7; 24D1; Case map */ + { 0x0024B8, 0x0024B8, { 0x0024D2 } }, /* 24B8; 24D2; Case map */ + { 0x0024B9, 0x0024B9, { 0x0024D3 } }, /* 24B9; 24D3; Case map */ + { 0x0024BA, 0x0024BA, { 0x0024D4 } }, /* 24BA; 24D4; Case map */ + { 0x0024BB, 0x0024BB, { 0x0024D5 } }, /* 24BB; 24D5; Case map */ + { 0x0024BC, 0x0024BC, { 0x0024D6 } }, /* 24BC; 24D6; Case map */ + { 0x0024BD, 0x0024BD, { 0x0024D7 } }, /* 24BD; 24D7; Case map */ + { 0x0024BE, 0x0024BE, { 0x0024D8 } }, /* 24BE; 24D8; Case map */ + { 0x0024BF, 0x0024BF, { 0x0024D9 } }, /* 24BF; 24D9; Case map */ + { 0x0024C0, 0x0024C0, { 0x0024DA } }, /* 24C0; 24DA; Case map */ + { 0x0024C1, 0x0024C1, { 0x0024DB } }, /* 24C1; 24DB; Case map */ + { 0x0024C2, 0x0024C2, { 0x0024DC } }, /* 24C2; 24DC; Case map */ + { 0x0024C3, 0x0024C3, { 0x0024DD } }, /* 24C3; 24DD; Case map */ + { 0x0024C4, 0x0024C4, { 0x0024DE } }, /* 24C4; 24DE; Case map */ + { 0x0024C5, 0x0024C5, { 0x0024DF } }, /* 24C5; 24DF; Case map */ + { 0x0024C6, 0x0024C6, { 0x0024E0 } }, /* 24C6; 24E0; Case map */ + { 0x0024C7, 0x0024C7, { 0x0024E1 } }, /* 24C7; 24E1; Case map */ + { 0x0024C8, 0x0024C8, { 0x0024E2 } }, /* 24C8; 24E2; Case map */ + { 0x0024C9, 0x0024C9, { 0x0024E3 } }, /* 24C9; 24E3; Case map */ + { 0x0024CA, 0x0024CA, { 0x0024E4 } }, /* 24CA; 24E4; Case map */ + { 0x0024CB, 0x0024CB, { 0x0024E5 } }, /* 24CB; 24E5; Case map */ + { 0x0024CC, 0x0024CC, { 0x0024E6 } }, /* 24CC; 24E6; Case map */ + { 0x0024CD, 0x0024CD, { 0x0024E7 } }, /* 24CD; 24E7; Case map */ + { 0x0024CE, 0x0024CE, { 0x0024E8 } }, /* 24CE; 24E8; Case map */ + { 0x0024CF, 0x0024CF, { 0x0024E9 } }, /* 24CF; 24E9; Case map */ + { 0x00FB00, + 0x00FB00, + { 0x000066, /* FB00; 0066 0066; Case map */ + 0x000066 } }, + { 0x00FB01, + 0x00FB01, + { 0x000066, /* FB01; 0066 0069; Case map */ + 0x000069 } }, + { 0x00FB02, + 0x00FB02, + { 0x000066, /* FB02; 0066 006C; Case map */ + 0x00006C } }, + { 0x00FB03, + 0x00FB03, + { 0x000066, /* FB03; 0066 0066 0069; Case map */ + 0x000066, 0x000069 } }, + { 0x00FB04, + 0x00FB04, + { 0x000066, /* FB04; 0066 0066 006C; Case map */ + 0x000066, 0x00006C } }, + { 0x00FB05, + 0x00FB05, + { 0x000073, /* FB05; 0073 0074; Case map */ + 0x000074 } }, + { 0x00FB06, + 0x00FB06, + { 0x000073, /* FB06; 0073 0074; Case map */ + 0x000074 } }, + { 0x00FB13, + 0x00FB13, + { 0x000574, /* FB13; 0574 0576; Case map */ + 0x000576 } }, + { 0x00FB14, + 0x00FB14, + { 0x000574, /* FB14; 0574 0565; Case map */ + 0x000565 } }, + { 0x00FB15, + 0x00FB15, + { 0x000574, /* FB15; 0574 056B; Case map */ + 0x00056B } }, + { 0x00FB16, + 0x00FB16, + { 0x00057E, /* FB16; 057E 0576; Case map */ + 0x000576 } }, + { 0x00FB17, + 0x00FB17, + { 0x000574, /* FB17; 0574 056D; Case map */ + 0x00056D } }, + { 0x00FF21, 0x00FF21, { 0x00FF41 } }, /* FF21; FF41; Case map */ + { 0x00FF22, 0x00FF22, { 0x00FF42 } }, /* FF22; FF42; Case map */ + { 0x00FF23, 0x00FF23, { 0x00FF43 } }, /* FF23; FF43; Case map */ + { 0x00FF24, 0x00FF24, { 0x00FF44 } }, /* FF24; FF44; Case map */ + { 0x00FF25, 0x00FF25, { 0x00FF45 } }, /* FF25; FF45; Case map */ + { 0x00FF26, 0x00FF26, { 0x00FF46 } }, /* FF26; FF46; Case map */ + { 0x00FF27, 0x00FF27, { 0x00FF47 } }, /* FF27; FF47; Case map */ + { 0x00FF28, 0x00FF28, { 0x00FF48 } }, /* FF28; FF48; Case map */ + { 0x00FF29, 0x00FF29, { 0x00FF49 } }, /* FF29; FF49; Case map */ + { 0x00FF2A, 0x00FF2A, { 0x00FF4A } }, /* FF2A; FF4A; Case map */ + { 0x00FF2B, 0x00FF2B, { 0x00FF4B } }, /* FF2B; FF4B; Case map */ + { 0x00FF2C, 0x00FF2C, { 0x00FF4C } }, /* FF2C; FF4C; Case map */ + { 0x00FF2D, 0x00FF2D, { 0x00FF4D } }, /* FF2D; FF4D; Case map */ + { 0x00FF2E, 0x00FF2E, { 0x00FF4E } }, /* FF2E; FF4E; Case map */ + { 0x00FF2F, 0x00FF2F, { 0x00FF4F } }, /* FF2F; FF4F; Case map */ + { 0x00FF30, 0x00FF30, { 0x00FF50 } }, /* FF30; FF50; Case map */ + { 0x00FF31, 0x00FF31, { 0x00FF51 } }, /* FF31; FF51; Case map */ + { 0x00FF32, 0x00FF32, { 0x00FF52 } }, /* FF32; FF52; Case map */ + { 0x00FF33, 0x00FF33, { 0x00FF53 } }, /* FF33; FF53; Case map */ + { 0x00FF34, 0x00FF34, { 0x00FF54 } }, /* FF34; FF54; Case map */ + { 0x00FF35, 0x00FF35, { 0x00FF55 } }, /* FF35; FF55; Case map */ + { 0x00FF36, 0x00FF36, { 0x00FF56 } }, /* FF36; FF56; Case map */ + { 0x00FF37, 0x00FF37, { 0x00FF57 } }, /* FF37; FF57; Case map */ + { 0x00FF38, 0x00FF38, { 0x00FF58 } }, /* FF38; FF58; Case map */ + { 0x00FF39, 0x00FF39, { 0x00FF59 } }, /* FF39; FF59; Case map */ + { 0x00FF3A, 0x00FF3A, { 0x00FF5A } }, /* FF3A; FF5A; Case map */ + { 0x010400, 0x010400, { 0x010428 } }, /* 10400; 10428; Case map */ + { 0x010401, 0x010401, { 0x010429 } }, /* 10401; 10429; Case map */ + { 0x010402, 0x010402, { 0x01042A } }, /* 10402; 1042A; Case map */ + { 0x010403, 0x010403, { 0x01042B } }, /* 10403; 1042B; Case map */ + { 0x010404, 0x010404, { 0x01042C } }, /* 10404; 1042C; Case map */ + { 0x010405, 0x010405, { 0x01042D } }, /* 10405; 1042D; Case map */ + { 0x010406, 0x010406, { 0x01042E } }, /* 10406; 1042E; Case map */ + { 0x010407, 0x010407, { 0x01042F } }, /* 10407; 1042F; Case map */ + { 0x010408, 0x010408, { 0x010430 } }, /* 10408; 10430; Case map */ + { 0x010409, 0x010409, { 0x010431 } }, /* 10409; 10431; Case map */ + { 0x01040A, 0x01040A, { 0x010432 } }, /* 1040A; 10432; Case map */ + { 0x01040B, 0x01040B, { 0x010433 } }, /* 1040B; 10433; Case map */ + { 0x01040C, 0x01040C, { 0x010434 } }, /* 1040C; 10434; Case map */ + { 0x01040D, 0x01040D, { 0x010435 } }, /* 1040D; 10435; Case map */ + { 0x01040E, 0x01040E, { 0x010436 } }, /* 1040E; 10436; Case map */ + { 0x01040F, 0x01040F, { 0x010437 } }, /* 1040F; 10437; Case map */ + { 0x010410, 0x010410, { 0x010438 } }, /* 10410; 10438; Case map */ + { 0x010411, 0x010411, { 0x010439 } }, /* 10411; 10439; Case map */ + { 0x010412, 0x010412, { 0x01043A } }, /* 10412; 1043A; Case map */ + { 0x010413, 0x010413, { 0x01043B } }, /* 10413; 1043B; Case map */ + { 0x010414, 0x010414, { 0x01043C } }, /* 10414; 1043C; Case map */ + { 0x010415, 0x010415, { 0x01043D } }, /* 10415; 1043D; Case map */ + { 0x010416, 0x010416, { 0x01043E } }, /* 10416; 1043E; Case map */ + { 0x010417, 0x010417, { 0x01043F } }, /* 10417; 1043F; Case map */ + { 0x010418, 0x010418, { 0x010440 } }, /* 10418; 10440; Case map */ + { 0x010419, 0x010419, { 0x010441 } }, /* 10419; 10441; Case map */ + { 0x01041A, 0x01041A, { 0x010442 } }, /* 1041A; 10442; Case map */ + { 0x01041B, 0x01041B, { 0x010443 } }, /* 1041B; 10443; Case map */ + { 0x01041C, 0x01041C, { 0x010444 } }, /* 1041C; 10444; Case map */ + { 0x01041D, 0x01041D, { 0x010445 } }, /* 1041D; 10445; Case map */ + { 0x01041E, 0x01041E, { 0x010446 } }, /* 1041E; 10446; Case map */ + { 0x01041F, 0x01041F, { 0x010447 } }, /* 1041F; 10447; Case map */ + { 0x010420, 0x010420, { 0x010448 } }, /* 10420; 10448; Case map */ + { 0x010421, 0x010421, { 0x010449 } }, /* 10421; 10449; Case map */ + { 0x010422, 0x010422, { 0x01044A } }, /* 10422; 1044A; Case map */ + { 0x010423, 0x010423, { 0x01044B } }, /* 10423; 1044B; Case map */ + { 0x010424, 0x010424, { 0x01044C } }, /* 10424; 1044C; Case map */ + { 0x010425, 0x010425, { 0x01044D } }, /* 10425; 1044D; Case map */ + { 0 }, +}; + +/* + * FF3A; FF5A; Case map + * 10400; 10428; Case map +10401; 10429; Case map +10402; 1042A; Case map +10403; 1042B; Case map +10404; 1042C; Case map +10405; 1042D; Case map +10406; 1042E; Case map +10407; 1042F; Case map +10408; 10430; Case map +10409; 10431; Case map +1040A; 10432; Case map +1040B; 10433; Case map +1040C; 10434; Case map +1040D; 10435; Case map +1040E; 10436; Case map +1040F; 10437; Case map +10410; 10438; Case map +10411; 10439; Case map +10412; 1043A; Case map +10413; 1043B; Case map +10414; 1043C; Case map +10415; 1043D; Case map +10416; 1043E; Case map +10417; 1043F; Case map +10418; 10440; Case map +10419; 10441; Case map +1041A; 10442; Case map +1041B; 10443; Case map +1041C; 10444; Case map +1041D; 10445; Case map +1041E; 10446; Case map +1041F; 10447; Case map +10420; 10448; Case map +10421; 10449; Case map +10422; 1044A; Case map +10423; 1044B; Case map +10424; 1044C; Case map +10425; 1044D; Case map + + */ + +const Stringprep_table_element stringprep_rfc3454_C_1_1[] = { + { 0x000020, 0x000020 }, /* 0020; SPACE */ + { 0 }, +}; + +/* + * FF3A; FF5A; Case map + * * 10400; 10428; Case map +10401; 10429; Case map +10402; 1042A; Case map +10403; 1042B; Case map +10404; 1042C; Case map +10405; 1042D; Case map +10406; 1042E; Case map +10407; 1042F; Case map +10408; 10430; Case map +10409; 10431; Case map +1040A; 10432; Case map +1040B; 10433; Case map +1040C; 10434; Case map +1040D; 10435; Case map +1040E; 10436; Case map +1040F; 10437; Case map +10410; 10438; Case map +10411; 10439; Case map +10412; 1043A; Case map +10413; 1043B; Case map +10414; 1043C; Case map +10415; 1043D; Case map +10416; 1043E; Case map +10417; 1043F; Case map +10418; 10440; Case map +10419; 10441; Case map +1041A; 10442; Case map +1041B; 10443; Case map +1041C; 10444; Case map +1041D; 10445; Case map +1041E; 10446; Case map +1041F; 10447; Case map +10420; 10448; Case map +10421; 10449; Case map +10422; 1044A; Case map +10423; 1044B; Case map +10424; 1044C; Case map +10425; 1044D; Case map + +0020; SPACE + + */ + +const Stringprep_table_element stringprep_rfc3454_C_1_2[] = { + { 0x0000A0, 0x0000A0 }, /* 00A0; NO-BREAK SPACE */ + { 0x001680, 0x001680 }, /* 1680; OGHAM SPACE MARK */ + { 0x002000, 0x002000 }, /* 2000; EN QUAD */ + { 0x002001, 0x002001 }, /* 2001; EM QUAD */ + { 0x002002, 0x002002 }, /* 2002; EN SPACE */ + { 0x002003, 0x002003 }, /* 2003; EM SPACE */ + { 0x002004, 0x002004 }, /* 2004; THREE-PER-EM SPACE */ + { 0x002005, 0x002005 }, /* 2005; FOUR-PER-EM SPACE */ + { 0x002006, 0x002006 }, /* 2006; SIX-PER-EM SPACE */ + { 0x002007, 0x002007 }, /* 2007; FIGURE SPACE */ + { 0x002008, 0x002008 }, /* 2008; PUNCTUATION SPACE */ + { 0x002009, 0x002009 }, /* 2009; THIN SPACE */ + { 0x00200A, 0x00200A }, /* 200A; HAIR SPACE */ + { 0x00200B, 0x00200B }, /* 200B; ZERO WIDTH SPACE */ + { 0x00202F, 0x00202F }, /* 202F; NARROW NO-BREAK SPACE */ + { 0x00205F, 0x00205F }, /* 205F; MEDIUM MATHEMATICAL SPACE */ + { 0x003000, 0x003000 }, /* 3000; IDEOGRAPHIC SPACE */ + { 0 }, +}; + +/* + * FF3A; FF5A; Case map + * * * 10400; 10428; Case map +10401; 10429; Case map +10402; 1042A; Case map +10403; 1042B; Case map +10404; 1042C; Case map +10405; 1042D; Case map +10406; 1042E; Case map +10407; 1042F; Case map +10408; 10430; Case map +10409; 10431; Case map +1040A; 10432; Case map +1040B; 10433; Case map +1040C; 10434; Case map +1040D; 10435; Case map +1040E; 10436; Case map +1040F; 10437; Case map +10410; 10438; Case map +10411; 10439; Case map +10412; 1043A; Case map +10413; 1043B; Case map +10414; 1043C; Case map +10415; 1043D; Case map +10416; 1043E; Case map +10417; 1043F; Case map +10418; 10440; Case map +10419; 10441; Case map +1041A; 10442; Case map +1041B; 10443; Case map +1041C; 10444; Case map +1041D; 10445; Case map +1041E; 10446; Case map +1041F; 10447; Case map +10420; 10448; Case map +10421; 10449; Case map +10422; 1044A; Case map +10423; 1044B; Case map +10424; 1044C; Case map +10425; 1044D; Case map + +0020; SPACE + +00A0; NO-BREAK SPACE +1680; OGHAM SPACE MARK +2000; EN QUAD +2001; EM QUAD +2002; EN SPACE +2003; EM SPACE +2004; THREE-PER-EM SPACE +2005; FOUR-PER-EM SPACE +2006; SIX-PER-EM SPACE +2007; FIGURE SPACE +2008; PUNCTUATION SPACE +2009; THIN SPACE +200A; HAIR SPACE +200B; ZERO WIDTH SPACE +202F; NARROW NO-BREAK SPACE +205F; MEDIUM MATHEMATICAL SPACE +3000; IDEOGRAPHIC SPACE + + */ + +const Stringprep_table_element stringprep_rfc3454_C_2_1[] = { + { 0x000000, 0x00001F }, /* 0000-001F; [CONTROL CHARACTERS] */ + { 0x00007F, 0x00007F }, /* 007F; DELETE */ + { 0 }, +}; + +/* + * FF3A; FF5A; Case map + * * * * 10400; 10428; Case map +10401; 10429; Case map +10402; 1042A; Case map +10403; 1042B; Case map +10404; 1042C; Case map +10405; 1042D; Case map +10406; 1042E; Case map +10407; 1042F; Case map +10408; 10430; Case map +10409; 10431; Case map +1040A; 10432; Case map +1040B; 10433; Case map +1040C; 10434; Case map +1040D; 10435; Case map +1040E; 10436; Case map +1040F; 10437; Case map +10410; 10438; Case map +10411; 10439; Case map +10412; 1043A; Case map +10413; 1043B; Case map +10414; 1043C; Case map +10415; 1043D; Case map +10416; 1043E; Case map +10417; 1043F; Case map +10418; 10440; Case map +10419; 10441; Case map +1041A; 10442; Case map +1041B; 10443; Case map +1041C; 10444; Case map +1041D; 10445; Case map +1041E; 10446; Case map +1041F; 10447; Case map +10420; 10448; Case map +10421; 10449; Case map +10422; 1044A; Case map +10423; 1044B; Case map +10424; 1044C; Case map +10425; 1044D; Case map + +0020; SPACE + +00A0; NO-BREAK SPACE +1680; OGHAM SPACE MARK +2000; EN QUAD +2001; EM QUAD +2002; EN SPACE +2003; EM SPACE +2004; THREE-PER-EM SPACE +2005; FOUR-PER-EM SPACE +2006; SIX-PER-EM SPACE +2007; FIGURE SPACE +2008; PUNCTUATION SPACE +2009; THIN SPACE +200A; HAIR SPACE +200B; ZERO WIDTH SPACE +202F; NARROW NO-BREAK SPACE +205F; MEDIUM MATHEMATICAL SPACE +3000; IDEOGRAPHIC SPACE + +0000-001F; [CONTROL CHARACTERS] +007F; DELETE + + */ + +const Stringprep_table_element stringprep_rfc3454_C_2_2[] = { + { 0x000080, 0x00009F }, /* 0080-009F; [CONTROL CHARACTERS] */ + { 0x0006DD, 0x0006DD }, /* 06DD; ARABIC END OF AYAH */ + { 0x00070F, 0x00070F }, /* 070F; SYRIAC ABBREVIATION MARK */ + { 0x00180E, 0x00180E }, /* 180E; MONGOLIAN VOWEL SEPARATOR */ + { 0x00200C, 0x00200C }, /* 200C; ZERO WIDTH NON-JOINER */ + { 0x00200D, 0x00200D }, /* 200D; ZERO WIDTH JOINER */ + { 0x002028, 0x002028 }, /* 2028; LINE SEPARATOR */ + { 0x002029, 0x002029 }, /* 2029; PARAGRAPH SEPARATOR */ + { 0x002060, 0x002060 }, /* 2060; WORD JOINER */ + { 0x002061, 0x002061 }, /* 2061; FUNCTION APPLICATION */ + { 0x002062, 0x002062 }, /* 2062; INVISIBLE TIMES */ + { 0x002063, 0x002063 }, /* 2063; INVISIBLE SEPARATOR */ + { 0x00206A, 0x00206F }, /* 206A-206F; [CONTROL CHARACTERS] */ + { 0x00FEFF, 0x00FEFF }, /* FEFF; ZERO WIDTH NO-BREAK SPACE */ + { 0x00FFF9, 0x00FFFC }, /* FFF9-FFFC; [CONTROL CHARACTERS] */ + { 0x01D173, 0x01D17A }, /* 1D173-1D17A; [MUSICAL CONTROL CHARACTERS] */ + { 0 }, +}; + +/* + * FFF9-FFFC; [CONTROL CHARACTERS] + * 1D173-1D17A; [MUSICAL CONTROL CHARACTERS] + + */ + +const Stringprep_table_element stringprep_rfc3454_C_3[] = { + { 0x00E000, 0x00F8FF }, /* E000-F8FF; [PRIVATE USE, PLANE 0] */ + { 0x0F0000, 0x0FFFFD }, /* F0000-FFFFD; [PRIVATE USE, PLANE 15] */ + { 0x100000, 0x10FFFD }, /* 100000-10FFFD; [PRIVATE USE, PLANE 16] */ + { 0 }, +}; + +/* + * F0000-FFFFD; [PRIVATE USE, PLANE 15] + * 100000-10FFFD; [PRIVATE USE, PLANE 16] + + */ + +const Stringprep_table_element stringprep_rfc3454_C_4[] = { + { 0x00FDD0, 0x00FDEF }, /* FDD0-FDEF; [NONCHARACTER CODE POINTS] */ + { 0x00FFFE, 0x00FFFF }, /* FFFE-FFFF; [NONCHARACTER CODE POINTS] */ + { 0x01FFFE, 0x01FFFF }, /* 1FFFE-1FFFF; [NONCHARACTER CODE POINTS] */ + { 0x02FFFE, 0x02FFFF }, /* 2FFFE-2FFFF; [NONCHARACTER CODE POINTS] */ + { 0x03FFFE, 0x03FFFF }, /* 3FFFE-3FFFF; [NONCHARACTER CODE POINTS] */ + { 0x04FFFE, 0x04FFFF }, /* 4FFFE-4FFFF; [NONCHARACTER CODE POINTS] */ + { 0x05FFFE, 0x05FFFF }, /* 5FFFE-5FFFF; [NONCHARACTER CODE POINTS] */ + { 0x06FFFE, 0x06FFFF }, /* 6FFFE-6FFFF; [NONCHARACTER CODE POINTS] */ + { 0x07FFFE, 0x07FFFF }, /* 7FFFE-7FFFF; [NONCHARACTER CODE POINTS] */ + { 0x08FFFE, 0x08FFFF }, /* 8FFFE-8FFFF; [NONCHARACTER CODE POINTS] */ + { 0x09FFFE, 0x09FFFF }, /* 9FFFE-9FFFF; [NONCHARACTER CODE POINTS] */ + { 0x0AFFFE, 0x0AFFFF }, /* AFFFE-AFFFF; [NONCHARACTER CODE POINTS] */ + { 0x0BFFFE, 0x0BFFFF }, /* BFFFE-BFFFF; [NONCHARACTER CODE POINTS] */ + { 0x0CFFFE, 0x0CFFFF }, /* CFFFE-CFFFF; [NONCHARACTER CODE POINTS] */ + { 0x0DFFFE, 0x0DFFFF }, /* DFFFE-DFFFF; [NONCHARACTER CODE POINTS] */ + { 0x0EFFFE, 0x0EFFFF }, /* EFFFE-EFFFF; [NONCHARACTER CODE POINTS] */ + { 0x0FFFFE, 0x0FFFFF }, /* FFFFE-FFFFF; [NONCHARACTER CODE POINTS] */ + { 0x10FFFE, 0x10FFFF }, /* 10FFFE-10FFFF; [NONCHARACTER CODE POINTS] */ + { 0 }, +}; + +/* + * FFFFE-FFFFF; [NONCHARACTER CODE POINTS] + * 10FFFE-10FFFF; [NONCHARACTER CODE POINTS] + + */ + +const Stringprep_table_element stringprep_rfc3454_C_5[] = { + { 0x00D800, 0x00DFFF }, /* D800-DFFF; [SURROGATE CODES] */ + { 0 }, +}; + +/* + * D800-DFFF; [SURROGATE CODES] + * + */ + +const Stringprep_table_element stringprep_rfc3454_C_6[] = { + { 0x00FFF9, 0x00FFF9 }, /* FFF9; INTERLINEAR ANNOTATION ANCHOR */ + { 0x00FFFA, 0x00FFFA }, /* FFFA; INTERLINEAR ANNOTATION SEPARATOR */ + { 0x00FFFB, 0x00FFFB }, /* FFFB; INTERLINEAR ANNOTATION TERMINATOR */ + { 0x00FFFC, 0x00FFFC }, /* FFFC; OBJECT REPLACEMENT CHARACTER */ + { 0x00FFFD, 0x00FFFD }, /* FFFD; REPLACEMENT CHARACTER */ + { 0 }, +}; + +/* + * FFFD; REPLACEMENT CHARACTER + * + */ + +const Stringprep_table_element stringprep_rfc3454_C_7[] = { + { 0x002FF0, 0x002FFB }, /* 2FF0-2FFB; [IDEOGRAPHIC DESCRIPTION CHARACTERS] */ + { 0 }, +}; + +/* + * FFFD; REPLACEMENT CHARACTER + * * +2FF0-2FFB; [IDEOGRAPHIC DESCRIPTION CHARACTERS] + + */ + +const Stringprep_table_element stringprep_rfc3454_C_8[] = { + { 0x000340, 0x000340 }, /* 0340; COMBINING GRAVE TONE MARK */ + { 0x000341, 0x000341 }, /* 0341; COMBINING ACUTE TONE MARK */ + { 0x00200E, 0x00200E }, /* 200E; LEFT-TO-RIGHT MARK */ + { 0x00200F, 0x00200F }, /* 200F; RIGHT-TO-LEFT MARK */ + { 0x00202A, 0x00202A }, /* 202A; LEFT-TO-RIGHT EMBEDDING */ + { 0x00202B, 0x00202B }, /* 202B; RIGHT-TO-LEFT EMBEDDING */ + { 0x00202C, 0x00202C }, /* 202C; POP DIRECTIONAL FORMATTING */ + { 0x00202D, 0x00202D }, /* 202D; LEFT-TO-RIGHT OVERRIDE */ + { 0x00202E, 0x00202E }, /* 202E; RIGHT-TO-LEFT OVERRIDE */ + { 0x00206A, 0x00206A }, /* 206A; INHIBIT SYMMETRIC SWAPPING */ + { 0x00206B, 0x00206B }, /* 206B; ACTIVATE SYMMETRIC SWAPPING */ + { 0x00206C, 0x00206C }, /* 206C; INHIBIT ARABIC FORM SHAPING */ + { 0x00206D, 0x00206D }, /* 206D; ACTIVATE ARABIC FORM SHAPING */ + { 0x00206E, 0x00206E }, /* 206E; NATIONAL DIGIT SHAPES */ + { 0x00206F, 0x00206F }, /* 206F; NOMINAL DIGIT SHAPES */ + { 0 }, +}; + +/* + * FFFD; REPLACEMENT CHARACTER + * * * +2FF0-2FFB; [IDEOGRAPHIC DESCRIPTION CHARACTERS] + +0340; COMBINING GRAVE TONE MARK +0341; COMBINING ACUTE TONE MARK +200E; LEFT-TO-RIGHT MARK +200F; RIGHT-TO-LEFT MARK +202A; LEFT-TO-RIGHT EMBEDDING +202B; RIGHT-TO-LEFT EMBEDDING +202C; POP DIRECTIONAL FORMATTING +202D; LEFT-TO-RIGHT OVERRIDE +202E; RIGHT-TO-LEFT OVERRIDE +206A; INHIBIT SYMMETRIC SWAPPING +206B; ACTIVATE SYMMETRIC SWAPPING +206C; INHIBIT ARABIC FORM SHAPING +206D; ACTIVATE ARABIC FORM SHAPING +206E; NATIONAL DIGIT SHAPES +206F; NOMINAL DIGIT SHAPES + + */ + +const Stringprep_table_element stringprep_rfc3454_C_9[] = { + { 0x0E0001, 0x0E0001 }, /* E0001; LANGUAGE TAG */ + { 0x0E0020, 0x0E007F }, /* E0020-E007F; [TAGGING CHARACTERS] */ + { 0 }, +}; + +/* + * E0020-E007F; [TAGGING CHARACTERS] + * + */ + +const Stringprep_table_element stringprep_rfc3454_D_1[] = { + { 0x0005BE, 0x0005BE }, /* 05BE */ + { 0x0005C0, 0x0005C0 }, /* 05C0 */ + { 0x0005C3, 0x0005C3 }, /* 05C3 */ + { 0x0005D0, 0x0005EA }, /* 05D0-05EA */ + { 0x0005F0, 0x0005F4 }, /* 05F0-05F4 */ + { 0x00061B, 0x00061B }, /* 061B */ + { 0x00061F, 0x00061F }, /* 061F */ + { 0x000621, 0x00063A }, /* 0621-063A */ + { 0x000640, 0x00064A }, /* 0640-064A */ + { 0x00066D, 0x00066F }, /* 066D-066F */ + { 0x000671, 0x0006D5 }, /* 0671-06D5 */ + { 0x0006DD, 0x0006DD }, /* 06DD */ + { 0x0006E5, 0x0006E6 }, /* 06E5-06E6 */ + { 0x0006FA, 0x0006FE }, /* 06FA-06FE */ + { 0x000700, 0x00070D }, /* 0700-070D */ + { 0x000710, 0x000710 }, /* 0710 */ + { 0x000712, 0x00072C }, /* 0712-072C */ + { 0x000780, 0x0007A5 }, /* 0780-07A5 */ + { 0x0007B1, 0x0007B1 }, /* 07B1 */ + { 0x00200F, 0x00200F }, /* 200F */ + { 0x00FB1D, 0x00FB1D }, /* FB1D */ + { 0x00FB1F, 0x00FB28 }, /* FB1F-FB28 */ + { 0x00FB2A, 0x00FB36 }, /* FB2A-FB36 */ + { 0x00FB38, 0x00FB3C }, /* FB38-FB3C */ + { 0x00FB3E, 0x00FB3E }, /* FB3E */ + { 0x00FB40, 0x00FB41 }, /* FB40-FB41 */ + { 0x00FB43, 0x00FB44 }, /* FB43-FB44 */ + { 0x00FB46, 0x00FBB1 }, /* FB46-FBB1 */ + { 0x00FBD3, 0x00FD3D }, /* FBD3-FD3D */ + { 0x00FD50, 0x00FD8F }, /* FD50-FD8F */ + { 0x00FD92, 0x00FDC7 }, /* FD92-FDC7 */ + { 0x00FDF0, 0x00FDFC }, /* FDF0-FDFC */ + { 0x00FE70, 0x00FE74 }, /* FE70-FE74 */ + { 0x00FE76, 0x00FEFC }, /* FE76-FEFC */ + { 0 }, +}; + +/* + * FE76-FEFC + * + */ + +const Stringprep_table_element stringprep_rfc3454_D_2[] = { + { 0x000041, 0x00005A }, /* 0041-005A */ + { 0x000061, 0x00007A }, /* 0061-007A */ + { 0x0000AA, 0x0000AA }, /* 00AA */ + { 0x0000B5, 0x0000B5 }, /* 00B5 */ + { 0x0000BA, 0x0000BA }, /* 00BA */ + { 0x0000C0, 0x0000D6 }, /* 00C0-00D6 */ + { 0x0000D8, 0x0000F6 }, /* 00D8-00F6 */ + { 0x0000F8, 0x000220 }, /* 00F8-0220 */ + { 0x000222, 0x000233 }, /* 0222-0233 */ + { 0x000250, 0x0002AD }, /* 0250-02AD */ + { 0x0002B0, 0x0002B8 }, /* 02B0-02B8 */ + { 0x0002BB, 0x0002C1 }, /* 02BB-02C1 */ + { 0x0002D0, 0x0002D1 }, /* 02D0-02D1 */ + { 0x0002E0, 0x0002E4 }, /* 02E0-02E4 */ + { 0x0002EE, 0x0002EE }, /* 02EE */ + { 0x00037A, 0x00037A }, /* 037A */ + { 0x000386, 0x000386 }, /* 0386 */ + { 0x000388, 0x00038A }, /* 0388-038A */ + { 0x00038C, 0x00038C }, /* 038C */ + { 0x00038E, 0x0003A1 }, /* 038E-03A1 */ + { 0x0003A3, 0x0003CE }, /* 03A3-03CE */ + { 0x0003D0, 0x0003F5 }, /* 03D0-03F5 */ + { 0x000400, 0x000482 }, /* 0400-0482 */ + { 0x00048A, 0x0004CE }, /* 048A-04CE */ + { 0x0004D0, 0x0004F5 }, /* 04D0-04F5 */ + { 0x0004F8, 0x0004F9 }, /* 04F8-04F9 */ + { 0x000500, 0x00050F }, /* 0500-050F */ + { 0x000531, 0x000556 }, /* 0531-0556 */ + { 0x000559, 0x00055F }, /* 0559-055F */ + { 0x000561, 0x000587 }, /* 0561-0587 */ + { 0x000589, 0x000589 }, /* 0589 */ + { 0x000903, 0x000903 }, /* 0903 */ + { 0x000905, 0x000939 }, /* 0905-0939 */ + { 0x00093D, 0x000940 }, /* 093D-0940 */ + { 0x000949, 0x00094C }, /* 0949-094C */ + { 0x000950, 0x000950 }, /* 0950 */ + { 0x000958, 0x000961 }, /* 0958-0961 */ + { 0x000964, 0x000970 }, /* 0964-0970 */ + { 0x000982, 0x000983 }, /* 0982-0983 */ + { 0x000985, 0x00098C }, /* 0985-098C */ + { 0x00098F, 0x000990 }, /* 098F-0990 */ + { 0x000993, 0x0009A8 }, /* 0993-09A8 */ + { 0x0009AA, 0x0009B0 }, /* 09AA-09B0 */ + { 0x0009B2, 0x0009B2 }, /* 09B2 */ + { 0x0009B6, 0x0009B9 }, /* 09B6-09B9 */ + { 0x0009BE, 0x0009C0 }, /* 09BE-09C0 */ + { 0x0009C7, 0x0009C8 }, /* 09C7-09C8 */ + { 0x0009CB, 0x0009CC }, /* 09CB-09CC */ + { 0x0009D7, 0x0009D7 }, /* 09D7 */ + { 0x0009DC, 0x0009DD }, /* 09DC-09DD */ + { 0x0009DF, 0x0009E1 }, /* 09DF-09E1 */ + { 0x0009E6, 0x0009F1 }, /* 09E6-09F1 */ + { 0x0009F4, 0x0009FA }, /* 09F4-09FA */ + { 0x000A05, 0x000A0A }, /* 0A05-0A0A */ + { 0x000A0F, 0x000A10 }, /* 0A0F-0A10 */ + { 0x000A13, 0x000A28 }, /* 0A13-0A28 */ + { 0x000A2A, 0x000A30 }, /* 0A2A-0A30 */ + { 0x000A32, 0x000A33 }, /* 0A32-0A33 */ + { 0x000A35, 0x000A36 }, /* 0A35-0A36 */ + { 0x000A38, 0x000A39 }, /* 0A38-0A39 */ + { 0x000A3E, 0x000A40 }, /* 0A3E-0A40 */ + { 0x000A59, 0x000A5C }, /* 0A59-0A5C */ + { 0x000A5E, 0x000A5E }, /* 0A5E */ + { 0x000A66, 0x000A6F }, /* 0A66-0A6F */ + { 0x000A72, 0x000A74 }, /* 0A72-0A74 */ + { 0x000A83, 0x000A83 }, /* 0A83 */ + { 0x000A85, 0x000A8B }, /* 0A85-0A8B */ + { 0x000A8D, 0x000A8D }, /* 0A8D */ + { 0x000A8F, 0x000A91 }, /* 0A8F-0A91 */ + { 0x000A93, 0x000AA8 }, /* 0A93-0AA8 */ + { 0x000AAA, 0x000AB0 }, /* 0AAA-0AB0 */ + { 0x000AB2, 0x000AB3 }, /* 0AB2-0AB3 */ + { 0x000AB5, 0x000AB9 }, /* 0AB5-0AB9 */ + { 0x000ABD, 0x000AC0 }, /* 0ABD-0AC0 */ + { 0x000AC9, 0x000AC9 }, /* 0AC9 */ + { 0x000ACB, 0x000ACC }, /* 0ACB-0ACC */ + { 0x000AD0, 0x000AD0 }, /* 0AD0 */ + { 0x000AE0, 0x000AE0 }, /* 0AE0 */ + { 0x000AE6, 0x000AEF }, /* 0AE6-0AEF */ + { 0x000B02, 0x000B03 }, /* 0B02-0B03 */ + { 0x000B05, 0x000B0C }, /* 0B05-0B0C */ + { 0x000B0F, 0x000B10 }, /* 0B0F-0B10 */ + { 0x000B13, 0x000B28 }, /* 0B13-0B28 */ + { 0x000B2A, 0x000B30 }, /* 0B2A-0B30 */ + { 0x000B32, 0x000B33 }, /* 0B32-0B33 */ + { 0x000B36, 0x000B39 }, /* 0B36-0B39 */ + { 0x000B3D, 0x000B3E }, /* 0B3D-0B3E */ + { 0x000B40, 0x000B40 }, /* 0B40 */ + { 0x000B47, 0x000B48 }, /* 0B47-0B48 */ + { 0x000B4B, 0x000B4C }, /* 0B4B-0B4C */ + { 0x000B57, 0x000B57 }, /* 0B57 */ + { 0x000B5C, 0x000B5D }, /* 0B5C-0B5D */ + { 0x000B5F, 0x000B61 }, /* 0B5F-0B61 */ + { 0x000B66, 0x000B70 }, /* 0B66-0B70 */ + { 0x000B83, 0x000B83 }, /* 0B83 */ + { 0x000B85, 0x000B8A }, /* 0B85-0B8A */ + { 0x000B8E, 0x000B90 }, /* 0B8E-0B90 */ + { 0x000B92, 0x000B95 }, /* 0B92-0B95 */ + { 0x000B99, 0x000B9A }, /* 0B99-0B9A */ + { 0x000B9C, 0x000B9C }, /* 0B9C */ + { 0x000B9E, 0x000B9F }, /* 0B9E-0B9F */ + { 0x000BA3, 0x000BA4 }, /* 0BA3-0BA4 */ + { 0x000BA8, 0x000BAA }, /* 0BA8-0BAA */ + { 0x000BAE, 0x000BB5 }, /* 0BAE-0BB5 */ + { 0x000BB7, 0x000BB9 }, /* 0BB7-0BB9 */ + { 0x000BBE, 0x000BBF }, /* 0BBE-0BBF */ + { 0x000BC1, 0x000BC2 }, /* 0BC1-0BC2 */ + { 0x000BC6, 0x000BC8 }, /* 0BC6-0BC8 */ + { 0x000BCA, 0x000BCC }, /* 0BCA-0BCC */ + { 0x000BD7, 0x000BD7 }, /* 0BD7 */ + { 0x000BE7, 0x000BF2 }, /* 0BE7-0BF2 */ + { 0x000C01, 0x000C03 }, /* 0C01-0C03 */ + { 0x000C05, 0x000C0C }, /* 0C05-0C0C */ + { 0x000C0E, 0x000C10 }, /* 0C0E-0C10 */ + { 0x000C12, 0x000C28 }, /* 0C12-0C28 */ + { 0x000C2A, 0x000C33 }, /* 0C2A-0C33 */ + { 0x000C35, 0x000C39 }, /* 0C35-0C39 */ + { 0x000C41, 0x000C44 }, /* 0C41-0C44 */ + { 0x000C60, 0x000C61 }, /* 0C60-0C61 */ + { 0x000C66, 0x000C6F }, /* 0C66-0C6F */ + { 0x000C82, 0x000C83 }, /* 0C82-0C83 */ + { 0x000C85, 0x000C8C }, /* 0C85-0C8C */ + { 0x000C8E, 0x000C90 }, /* 0C8E-0C90 */ + { 0x000C92, 0x000CA8 }, /* 0C92-0CA8 */ + { 0x000CAA, 0x000CB3 }, /* 0CAA-0CB3 */ + { 0x000CB5, 0x000CB9 }, /* 0CB5-0CB9 */ + { 0x000CBE, 0x000CBE }, /* 0CBE */ + { 0x000CC0, 0x000CC4 }, /* 0CC0-0CC4 */ + { 0x000CC7, 0x000CC8 }, /* 0CC7-0CC8 */ + { 0x000CCA, 0x000CCB }, /* 0CCA-0CCB */ + { 0x000CD5, 0x000CD6 }, /* 0CD5-0CD6 */ + { 0x000CDE, 0x000CDE }, /* 0CDE */ + { 0x000CE0, 0x000CE1 }, /* 0CE0-0CE1 */ + { 0x000CE6, 0x000CEF }, /* 0CE6-0CEF */ + { 0x000D02, 0x000D03 }, /* 0D02-0D03 */ + { 0x000D05, 0x000D0C }, /* 0D05-0D0C */ + { 0x000D0E, 0x000D10 }, /* 0D0E-0D10 */ + { 0x000D12, 0x000D28 }, /* 0D12-0D28 */ + { 0x000D2A, 0x000D39 }, /* 0D2A-0D39 */ + { 0x000D3E, 0x000D40 }, /* 0D3E-0D40 */ + { 0x000D46, 0x000D48 }, /* 0D46-0D48 */ + { 0x000D4A, 0x000D4C }, /* 0D4A-0D4C */ + { 0x000D57, 0x000D57 }, /* 0D57 */ + { 0x000D60, 0x000D61 }, /* 0D60-0D61 */ + { 0x000D66, 0x000D6F }, /* 0D66-0D6F */ + { 0x000D82, 0x000D83 }, /* 0D82-0D83 */ + { 0x000D85, 0x000D96 }, /* 0D85-0D96 */ + { 0x000D9A, 0x000DB1 }, /* 0D9A-0DB1 */ + { 0x000DB3, 0x000DBB }, /* 0DB3-0DBB */ + { 0x000DBD, 0x000DBD }, /* 0DBD */ + { 0x000DC0, 0x000DC6 }, /* 0DC0-0DC6 */ + { 0x000DCF, 0x000DD1 }, /* 0DCF-0DD1 */ + { 0x000DD8, 0x000DDF }, /* 0DD8-0DDF */ + { 0x000DF2, 0x000DF4 }, /* 0DF2-0DF4 */ + { 0x000E01, 0x000E30 }, /* 0E01-0E30 */ + { 0x000E32, 0x000E33 }, /* 0E32-0E33 */ + { 0x000E40, 0x000E46 }, /* 0E40-0E46 */ + { 0x000E4F, 0x000E5B }, /* 0E4F-0E5B */ + { 0x000E81, 0x000E82 }, /* 0E81-0E82 */ + { 0x000E84, 0x000E84 }, /* 0E84 */ + { 0x000E87, 0x000E88 }, /* 0E87-0E88 */ + { 0x000E8A, 0x000E8A }, /* 0E8A */ + { 0x000E8D, 0x000E8D }, /* 0E8D */ + { 0x000E94, 0x000E97 }, /* 0E94-0E97 */ + { 0x000E99, 0x000E9F }, /* 0E99-0E9F */ + { 0x000EA1, 0x000EA3 }, /* 0EA1-0EA3 */ + { 0x000EA5, 0x000EA5 }, /* 0EA5 */ + { 0x000EA7, 0x000EA7 }, /* 0EA7 */ + { 0x000EAA, 0x000EAB }, /* 0EAA-0EAB */ + { 0x000EAD, 0x000EB0 }, /* 0EAD-0EB0 */ + { 0x000EB2, 0x000EB3 }, /* 0EB2-0EB3 */ + { 0x000EBD, 0x000EBD }, /* 0EBD */ + { 0x000EC0, 0x000EC4 }, /* 0EC0-0EC4 */ + { 0x000EC6, 0x000EC6 }, /* 0EC6 */ + { 0x000ED0, 0x000ED9 }, /* 0ED0-0ED9 */ + { 0x000EDC, 0x000EDD }, /* 0EDC-0EDD */ + { 0x000F00, 0x000F17 }, /* 0F00-0F17 */ + { 0x000F1A, 0x000F34 }, /* 0F1A-0F34 */ + { 0x000F36, 0x000F36 }, /* 0F36 */ + { 0x000F38, 0x000F38 }, /* 0F38 */ + { 0x000F3E, 0x000F47 }, /* 0F3E-0F47 */ + { 0x000F49, 0x000F6A }, /* 0F49-0F6A */ + { 0x000F7F, 0x000F7F }, /* 0F7F */ + { 0x000F85, 0x000F85 }, /* 0F85 */ + { 0x000F88, 0x000F8B }, /* 0F88-0F8B */ + { 0x000FBE, 0x000FC5 }, /* 0FBE-0FC5 */ + { 0x000FC7, 0x000FCC }, /* 0FC7-0FCC */ + { 0x000FCF, 0x000FCF }, /* 0FCF */ + { 0x001000, 0x001021 }, /* 1000-1021 */ + { 0x001023, 0x001027 }, /* 1023-1027 */ + { 0x001029, 0x00102A }, /* 1029-102A */ + { 0x00102C, 0x00102C }, /* 102C */ + { 0x001031, 0x001031 }, /* 1031 */ + { 0x001038, 0x001038 }, /* 1038 */ + { 0x001040, 0x001057 }, /* 1040-1057 */ + { 0x0010A0, 0x0010C5 }, /* 10A0-10C5 */ + { 0x0010D0, 0x0010F8 }, /* 10D0-10F8 */ + { 0x0010FB, 0x0010FB }, /* 10FB */ + { 0x001100, 0x001159 }, /* 1100-1159 */ + { 0x00115F, 0x0011A2 }, /* 115F-11A2 */ + { 0x0011A8, 0x0011F9 }, /* 11A8-11F9 */ + { 0x001200, 0x001206 }, /* 1200-1206 */ + { 0x001208, 0x001246 }, /* 1208-1246 */ + { 0x001248, 0x001248 }, /* 1248 */ + { 0x00124A, 0x00124D }, /* 124A-124D */ + { 0x001250, 0x001256 }, /* 1250-1256 */ + { 0x001258, 0x001258 }, /* 1258 */ + { 0x00125A, 0x00125D }, /* 125A-125D */ + { 0x001260, 0x001286 }, /* 1260-1286 */ + { 0x001288, 0x001288 }, /* 1288 */ + { 0x00128A, 0x00128D }, /* 128A-128D */ + { 0x001290, 0x0012AE }, /* 1290-12AE */ + { 0x0012B0, 0x0012B0 }, /* 12B0 */ + { 0x0012B2, 0x0012B5 }, /* 12B2-12B5 */ + { 0x0012B8, 0x0012BE }, /* 12B8-12BE */ + { 0x0012C0, 0x0012C0 }, /* 12C0 */ + { 0x0012C2, 0x0012C5 }, /* 12C2-12C5 */ + { 0x0012C8, 0x0012CE }, /* 12C8-12CE */ + { 0x0012D0, 0x0012D6 }, /* 12D0-12D6 */ + { 0x0012D8, 0x0012EE }, /* 12D8-12EE */ + { 0x0012F0, 0x00130E }, /* 12F0-130E */ + { 0x001310, 0x001310 }, /* 1310 */ + { 0x001312, 0x001315 }, /* 1312-1315 */ + { 0x001318, 0x00131E }, /* 1318-131E */ + { 0x001320, 0x001346 }, /* 1320-1346 */ + { 0x001348, 0x00135A }, /* 1348-135A */ + { 0x001361, 0x00137C }, /* 1361-137C */ + { 0x0013A0, 0x0013F4 }, /* 13A0-13F4 */ + { 0x001401, 0x001676 }, /* 1401-1676 */ + { 0x001681, 0x00169A }, /* 1681-169A */ + { 0x0016A0, 0x0016F0 }, /* 16A0-16F0 */ + { 0x001700, 0x00170C }, /* 1700-170C */ + { 0x00170E, 0x001711 }, /* 170E-1711 */ + { 0x001720, 0x001731 }, /* 1720-1731 */ + { 0x001735, 0x001736 }, /* 1735-1736 */ + { 0x001740, 0x001751 }, /* 1740-1751 */ + { 0x001760, 0x00176C }, /* 1760-176C */ + { 0x00176E, 0x001770 }, /* 176E-1770 */ + { 0x001780, 0x0017B6 }, /* 1780-17B6 */ + { 0x0017BE, 0x0017C5 }, /* 17BE-17C5 */ + { 0x0017C7, 0x0017C8 }, /* 17C7-17C8 */ + { 0x0017D4, 0x0017DA }, /* 17D4-17DA */ + { 0x0017DC, 0x0017DC }, /* 17DC */ + { 0x0017E0, 0x0017E9 }, /* 17E0-17E9 */ + { 0x001810, 0x001819 }, /* 1810-1819 */ + { 0x001820, 0x001877 }, /* 1820-1877 */ + { 0x001880, 0x0018A8 }, /* 1880-18A8 */ + { 0x001E00, 0x001E9B }, /* 1E00-1E9B */ + { 0x001EA0, 0x001EF9 }, /* 1EA0-1EF9 */ + { 0x001F00, 0x001F15 }, /* 1F00-1F15 */ + { 0x001F18, 0x001F1D }, /* 1F18-1F1D */ + { 0x001F20, 0x001F45 }, /* 1F20-1F45 */ + { 0x001F48, 0x001F4D }, /* 1F48-1F4D */ + { 0x001F50, 0x001F57 }, /* 1F50-1F57 */ + { 0x001F59, 0x001F59 }, /* 1F59 */ + { 0x001F5B, 0x001F5B }, /* 1F5B */ + { 0x001F5D, 0x001F5D }, /* 1F5D */ + { 0x001F5F, 0x001F7D }, /* 1F5F-1F7D */ + { 0x001F80, 0x001FB4 }, /* 1F80-1FB4 */ + { 0x001FB6, 0x001FBC }, /* 1FB6-1FBC */ + { 0x001FBE, 0x001FBE }, /* 1FBE */ + { 0x001FC2, 0x001FC4 }, /* 1FC2-1FC4 */ + { 0x001FC6, 0x001FCC }, /* 1FC6-1FCC */ + { 0x001FD0, 0x001FD3 }, /* 1FD0-1FD3 */ + { 0x001FD6, 0x001FDB }, /* 1FD6-1FDB */ + { 0x001FE0, 0x001FEC }, /* 1FE0-1FEC */ + { 0x001FF2, 0x001FF4 }, /* 1FF2-1FF4 */ + { 0x001FF6, 0x001FFC }, /* 1FF6-1FFC */ + { 0x00200E, 0x00200E }, /* 200E */ + { 0x002071, 0x002071 }, /* 2071 */ + { 0x00207F, 0x00207F }, /* 207F */ + { 0x002102, 0x002102 }, /* 2102 */ + { 0x002107, 0x002107 }, /* 2107 */ + { 0x00210A, 0x002113 }, /* 210A-2113 */ + { 0x002115, 0x002115 }, /* 2115 */ + { 0x002119, 0x00211D }, /* 2119-211D */ + { 0x002124, 0x002124 }, /* 2124 */ + { 0x002126, 0x002126 }, /* 2126 */ + { 0x002128, 0x002128 }, /* 2128 */ + { 0x00212A, 0x00212D }, /* 212A-212D */ + { 0x00212F, 0x002131 }, /* 212F-2131 */ + { 0x002133, 0x002139 }, /* 2133-2139 */ + { 0x00213D, 0x00213F }, /* 213D-213F */ + { 0x002145, 0x002149 }, /* 2145-2149 */ + { 0x002160, 0x002183 }, /* 2160-2183 */ + { 0x002336, 0x00237A }, /* 2336-237A */ + { 0x002395, 0x002395 }, /* 2395 */ + { 0x00249C, 0x0024E9 }, /* 249C-24E9 */ + { 0x003005, 0x003007 }, /* 3005-3007 */ + { 0x003021, 0x003029 }, /* 3021-3029 */ + { 0x003031, 0x003035 }, /* 3031-3035 */ + { 0x003038, 0x00303C }, /* 3038-303C */ + { 0x003041, 0x003096 }, /* 3041-3096 */ + { 0x00309D, 0x00309F }, /* 309D-309F */ + { 0x0030A1, 0x0030FA }, /* 30A1-30FA */ + { 0x0030FC, 0x0030FF }, /* 30FC-30FF */ + { 0x003105, 0x00312C }, /* 3105-312C */ + { 0x003131, 0x00318E }, /* 3131-318E */ + { 0x003190, 0x0031B7 }, /* 3190-31B7 */ + { 0x0031F0, 0x00321C }, /* 31F0-321C */ + { 0x003220, 0x003243 }, /* 3220-3243 */ + { 0x003260, 0x00327B }, /* 3260-327B */ + { 0x00327F, 0x0032B0 }, /* 327F-32B0 */ + { 0x0032C0, 0x0032CB }, /* 32C0-32CB */ + { 0x0032D0, 0x0032FE }, /* 32D0-32FE */ + { 0x003300, 0x003376 }, /* 3300-3376 */ + { 0x00337B, 0x0033DD }, /* 337B-33DD */ + { 0x0033E0, 0x0033FE }, /* 33E0-33FE */ + { 0x003400, 0x004DB5 }, /* 3400-4DB5 */ + { 0x004E00, 0x009FA5 }, /* 4E00-9FA5 */ + { 0x00A000, 0x00A48C }, /* A000-A48C */ + { 0x00AC00, 0x00D7A3 }, /* AC00-D7A3 */ + { 0x00D800, 0x00FA2D }, /* D800-FA2D */ + { 0x00FA30, 0x00FA6A }, /* FA30-FA6A */ + { 0x00FB00, 0x00FB06 }, /* FB00-FB06 */ + { 0x00FB13, 0x00FB17 }, /* FB13-FB17 */ + { 0x00FF21, 0x00FF3A }, /* FF21-FF3A */ + { 0x00FF41, 0x00FF5A }, /* FF41-FF5A */ + { 0x00FF66, 0x00FFBE }, /* FF66-FFBE */ + { 0x00FFC2, 0x00FFC7 }, /* FFC2-FFC7 */ + { 0x00FFCA, 0x00FFCF }, /* FFCA-FFCF */ + { 0x00FFD2, 0x00FFD7 }, /* FFD2-FFD7 */ + { 0x00FFDA, 0x00FFDC }, /* FFDA-FFDC */ + { 0x010300, 0x01031E }, /* 10300-1031E */ + { 0x010320, 0x010323 }, /* 10320-10323 */ + { 0x010330, 0x01034A }, /* 10330-1034A */ + { 0x010400, 0x010425 }, /* 10400-10425 */ + { 0x010428, 0x01044D }, /* 10428-1044D */ + { 0x01D000, 0x01D0F5 }, /* 1D000-1D0F5 */ + { 0x01D100, 0x01D126 }, /* 1D100-1D126 */ + { 0x01D12A, 0x01D166 }, /* 1D12A-1D166 */ + { 0x01D16A, 0x01D172 }, /* 1D16A-1D172 */ + { 0x01D183, 0x01D184 }, /* 1D183-1D184 */ + { 0x01D18C, 0x01D1A9 }, /* 1D18C-1D1A9 */ + { 0x01D1AE, 0x01D1DD }, /* 1D1AE-1D1DD */ + { 0x01D400, 0x01D454 }, /* 1D400-1D454 */ + { 0x01D456, 0x01D49C }, /* 1D456-1D49C */ + { 0x01D49E, 0x01D49F }, /* 1D49E-1D49F */ + { 0x01D4A2, 0x01D4A2 }, /* 1D4A2 */ + { 0x01D4A5, 0x01D4A6 }, /* 1D4A5-1D4A6 */ + { 0x01D4A9, 0x01D4AC }, /* 1D4A9-1D4AC */ + { 0x01D4AE, 0x01D4B9 }, /* 1D4AE-1D4B9 */ + { 0x01D4BB, 0x01D4BB }, /* 1D4BB */ + { 0x01D4BD, 0x01D4C0 }, /* 1D4BD-1D4C0 */ + { 0x01D4C2, 0x01D4C3 }, /* 1D4C2-1D4C3 */ + { 0x01D4C5, 0x01D505 }, /* 1D4C5-1D505 */ + { 0x01D507, 0x01D50A }, /* 1D507-1D50A */ + { 0x01D50D, 0x01D514 }, /* 1D50D-1D514 */ + { 0x01D516, 0x01D51C }, /* 1D516-1D51C */ + { 0x01D51E, 0x01D539 }, /* 1D51E-1D539 */ + { 0x01D53B, 0x01D53E }, /* 1D53B-1D53E */ + { 0x01D540, 0x01D544 }, /* 1D540-1D544 */ + { 0x01D546, 0x01D546 }, /* 1D546 */ + { 0x01D54A, 0x01D550 }, /* 1D54A-1D550 */ + { 0x01D552, 0x01D6A3 }, /* 1D552-1D6A3 */ + { 0x01D6A8, 0x01D7C9 }, /* 1D6A8-1D7C9 */ + { 0x020000, 0x02A6D6 }, /* 20000-2A6D6 */ + { 0x02F800, 0x02FA1D }, /* 2F800-2FA1D */ + { 0x0F0000, 0x0FFFFD }, /* F0000-FFFFD */ + { 0x100000, 0x10FFFD }, /* 100000-10FFFD */ + { 0 }, +}; diff --git a/3rdparty/stringprep/rfc3454.h b/3rdparty/stringprep/rfc3454.h new file mode 100644 index 00000000..eead9964 --- /dev/null +++ b/3rdparty/stringprep/rfc3454.h @@ -0,0 +1,20 @@ +/* This file is automatically generated. DO NOT EDIT! + Instead, edit gen-stringprep-tables.pl and re-run. */ + +#define N_STRINGPREP_rfc3454_A_1 396 +#define N_STRINGPREP_rfc3454_B_1 27 +#define N_STRINGPREP_rfc3454_B_2 1371 +#define N_STRINGPREP_rfc3454_B_3 838 +#define N_STRINGPREP_rfc3454_C_1_1 1 +#define N_STRINGPREP_rfc3454_C_1_2 17 +#define N_STRINGPREP_rfc3454_C_2_1 2 +#define N_STRINGPREP_rfc3454_C_2_2 16 +#define N_STRINGPREP_rfc3454_C_3 3 +#define N_STRINGPREP_rfc3454_C_4 18 +#define N_STRINGPREP_rfc3454_C_5 1 +#define N_STRINGPREP_rfc3454_C_6 5 +#define N_STRINGPREP_rfc3454_C_7 1 +#define N_STRINGPREP_rfc3454_C_8 15 +#define N_STRINGPREP_rfc3454_C_9 2 +#define N_STRINGPREP_rfc3454_D_1 34 +#define N_STRINGPREP_rfc3454_D_2 360 diff --git a/3rdparty/stringprep/rfc3454.txt b/3rdparty/stringprep/rfc3454.txt new file mode 100644 index 00000000..bdf8edb5 --- /dev/null +++ b/3rdparty/stringprep/rfc3454.txt @@ -0,0 +1,3246 @@ +Below there are data tables extracted from RFC 3454. The license for +RFC 3454 is: + + Copyright (C) The Internet Society (2002). All Rights Reserved. + + This document and translations of it may be copied and furnished to + others, and derivative works that comment on or otherwise explain it + or assist in its implementation may be prepared, copied, published + and distributed, in whole or in part, without restriction of any + kind, provided that the above copyright notice and this paragraph are + included on all such copies and derivative works. However, this + document itself may not be modified in any way, such as by removing + the copyright notice or references to the Internet Society or other + Internet organizations, except as needed for the purpose of + developing Internet standards in which case the procedures for + copyrights defined in the Internet Standards process must be + followed, or as required to translate it into languages other than + English. + + The limited permissions granted above are perpetual and will not be + revoked by the Internet Society or its successors or assigns. + + This document and the information contained herein is provided on an + "AS IS" basis and THE INTERNET SOCIETY AND THE INTERNET ENGINEERING + TASK FORCE DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING + BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION + HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF + MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. + +Unfortunately, that license is non-free according to +licensing@fsf.org: + + From: "Dave Turner via RT" + Date: Thu, 08 Dec 2005 16:33:57 -0500 + Subject: [gnu.org #211910] Are RFC specifications freely licensed? + + > Hi Dave! I know this is an old issue (last post in this thread was + > about a year ago), but have you heard back from the licensing + > committee on the RFC 2026 and RFC 3978 licenses? + + Sorry for the delay. The old terms are officially non-free. + +However, to be copyrightable there needs to be some originality, which +isn't present here, see: + + From: "novalis@fsf.org via RT" + Subject: Re: [gnu.org #211910] Are RFC specifications freely licensed? + Date: Thu, 06 Jan 2005 18:32:02 -0500 + + >> >> > If not, it's entirely possible that they're not copyrightable, or that + >> >> > they have very thin protection. I would need to see them specifically + >> >> > to be able to determine this. + >> >> + >> >> The table fall under the old license. The ASN.1 schema fall under the + >> >> new license. I'm attaching the tables, and the ASN.1 schema below. + >> >> If it matters: Some tables are derived from Unicode tables, and some + >> >> were probably created by hand. + >> > + >> > It looks to me like these tables do not have enough originality to be + >> > copyrightable. The text surrounding them ("The following is the mapping + >> > table from section 3....") is probably minimally original and should be + >> > removed. + >> + >> Are you sure about this? I'm looking for something strong than "It + >> looks". The tables were not trivial to create, it took the IETF some + >> time (years) to arrive with the final tables. Perhaps the tables + >> could be argued to contain some artistic value. But I'm not an expert + >> on this, so these are just my thoughts. + > + > I'm not a lawyer. This isn't legal advice. If you need legal advice, + > talk to a lawyer. I can get a second opinion on this from our lawyers, + > if you think it's important, but it still won't be legal advice. + > + > From a copyright perspective, the time and effort ("sweat of the brow") + > that a given thing takes don't matter (Fiest v. Rural Telephone + > Service). What matters is the originality. + > + > I don't see a lot here that's original. The names are probably + > original, but names are generally unprotected. And if the names are + > needed for compatibility, the scenes a faire doctrine comes into play. + + Right, the names are not required, only the actual numbers are. + + Thanks, this information seem to solve my problem for the source code + in libidn. + +So we can use this material. There may be other legal analysis that +also reach the same conclusion. + + ----- Start Table A.1 ----- + 0221 + 0234-024F + 02AE-02AF + 02EF-02FF + 0350-035F + 0370-0373 + 0376-0379 + 037B-037D + 037F-0383 + 038B + 038D + 03A2 + 03CF + 03F7-03FF + 0487 + 04CF + 04F6-04F7 + 04FA-04FF + 0510-0530 + 0557-0558 + 0560 + 0588 + 058B-0590 + 05A2 + 05BA + 05C5-05CF + 05EB-05EF + 05F5-060B + 060D-061A + 061C-061E + 0620 + 063B-063F + 0656-065F + 06EE-06EF + 06FF + 070E + 072D-072F + 074B-077F + 07B2-0900 + 0904 + 093A-093B + 094E-094F + 0955-0957 + 0971-0980 + 0984 + 098D-098E + 0991-0992 + 09A9 + 09B1 + 09B3-09B5 + 09BA-09BB + 09BD + 09C5-09C6 + 09C9-09CA + 09CE-09D6 + 09D8-09DB + 09DE + 09E4-09E5 + 09FB-0A01 + 0A03-0A04 + 0A0B-0A0E + 0A11-0A12 + 0A29 + 0A31 + 0A34 + 0A37 + 0A3A-0A3B + 0A3D + 0A43-0A46 + 0A49-0A4A + 0A4E-0A58 + 0A5D + 0A5F-0A65 + 0A75-0A80 + 0A84 + 0A8C + 0A8E + 0A92 + 0AA9 + 0AB1 + 0AB4 + 0ABA-0ABB + 0AC6 + 0ACA + 0ACE-0ACF + 0AD1-0ADF + 0AE1-0AE5 + 0AF0-0B00 + 0B04 + 0B0D-0B0E + 0B11-0B12 + 0B29 + 0B31 + 0B34-0B35 + 0B3A-0B3B + 0B44-0B46 + 0B49-0B4A + 0B4E-0B55 + 0B58-0B5B + 0B5E + 0B62-0B65 + 0B71-0B81 + 0B84 + 0B8B-0B8D + 0B91 + 0B96-0B98 + 0B9B + 0B9D + 0BA0-0BA2 + 0BA5-0BA7 + 0BAB-0BAD + 0BB6 + 0BBA-0BBD + 0BC3-0BC5 + 0BC9 + 0BCE-0BD6 + 0BD8-0BE6 + 0BF3-0C00 + 0C04 + 0C0D + 0C11 + 0C29 + 0C34 + 0C3A-0C3D + 0C45 + 0C49 + 0C4E-0C54 + 0C57-0C5F + 0C62-0C65 + 0C70-0C81 + 0C84 + 0C8D + 0C91 + 0CA9 + 0CB4 + 0CBA-0CBD + 0CC5 + 0CC9 + 0CCE-0CD4 + 0CD7-0CDD + 0CDF + 0CE2-0CE5 + 0CF0-0D01 + 0D04 + 0D0D + 0D11 + 0D29 + 0D3A-0D3D + 0D44-0D45 + 0D49 + 0D4E-0D56 + 0D58-0D5F + 0D62-0D65 + 0D70-0D81 + 0D84 + 0D97-0D99 + 0DB2 + 0DBC + 0DBE-0DBF + 0DC7-0DC9 + 0DCB-0DCE + 0DD5 + 0DD7 + 0DE0-0DF1 + 0DF5-0E00 + 0E3B-0E3E + 0E5C-0E80 + 0E83 + 0E85-0E86 + 0E89 + 0E8B-0E8C + 0E8E-0E93 + 0E98 + 0EA0 + 0EA4 + 0EA6 + 0EA8-0EA9 + 0EAC + 0EBA + 0EBE-0EBF + 0EC5 + 0EC7 + 0ECE-0ECF + 0EDA-0EDB + 0EDE-0EFF + 0F48 + 0F6B-0F70 + 0F8C-0F8F + 0F98 + 0FBD + 0FCD-0FCE + 0FD0-0FFF + 1022 + 1028 + 102B + 1033-1035 + 103A-103F + 105A-109F + 10C6-10CF + 10F9-10FA + 10FC-10FF + 115A-115E + 11A3-11A7 + 11FA-11FF + 1207 + 1247 + 1249 + 124E-124F + 1257 + 1259 + 125E-125F + 1287 + 1289 + 128E-128F + 12AF + 12B1 + 12B6-12B7 + 12BF + 12C1 + 12C6-12C7 + 12CF + 12D7 + 12EF + 130F + 1311 + 1316-1317 + 131F + 1347 + 135B-1360 + 137D-139F + 13F5-1400 + 1677-167F + 169D-169F + 16F1-16FF + 170D + 1715-171F + 1737-173F + 1754-175F + 176D + 1771 + 1774-177F + 17DD-17DF + 17EA-17FF + 180F + 181A-181F + 1878-187F + 18AA-1DFF + 1E9C-1E9F + 1EFA-1EFF + 1F16-1F17 + 1F1E-1F1F + 1F46-1F47 + 1F4E-1F4F + 1F58 + 1F5A + 1F5C + 1F5E + 1F7E-1F7F + 1FB5 + 1FC5 + 1FD4-1FD5 + 1FDC + 1FF0-1FF1 + 1FF5 + 1FFF + 2053-2056 + 2058-205E + 2064-2069 + 2072-2073 + 208F-209F + 20B2-20CF + 20EB-20FF + 213B-213C + 214C-2152 + 2184-218F + 23CF-23FF + 2427-243F + 244B-245F + 24FF + 2614-2615 + 2618 + 267E-267F + 268A-2700 + 2705 + 270A-270B + 2728 + 274C + 274E + 2753-2755 + 2757 + 275F-2760 + 2795-2797 + 27B0 + 27BF-27CF + 27EC-27EF + 2B00-2E7F + 2E9A + 2EF4-2EFF + 2FD6-2FEF + 2FFC-2FFF + 3040 + 3097-3098 + 3100-3104 + 312D-3130 + 318F + 31B8-31EF + 321D-321F + 3244-3250 + 327C-327E + 32CC-32CF + 32FF + 3377-337A + 33DE-33DF + 33FF + 4DB6-4DFF + 9FA6-9FFF + A48D-A48F + A4C7-ABFF + D7A4-D7FF + FA2E-FA2F + FA6B-FAFF + FB07-FB12 + FB18-FB1C + FB37 + FB3D + FB3F + FB42 + FB45 + FBB2-FBD2 + FD40-FD4F + FD90-FD91 + FDC8-FDCF + FDFD-FDFF + FE10-FE1F + FE24-FE2F + FE47-FE48 + FE53 + FE67 + FE6C-FE6F + FE75 + FEFD-FEFE + FF00 + FFBF-FFC1 + FFC8-FFC9 + FFD0-FFD1 + FFD8-FFD9 + FFDD-FFDF + FFE7 + FFEF-FFF8 + 10000-102FF + 1031F + 10324-1032F + 1034B-103FF + 10426-10427 + 1044E-1CFFF + 1D0F6-1D0FF + 1D127-1D129 + 1D1DE-1D3FF + 1D455 + 1D49D + 1D4A0-1D4A1 + 1D4A3-1D4A4 + 1D4A7-1D4A8 + 1D4AD + 1D4BA + 1D4BC + 1D4C1 + 1D4C4 + 1D506 + 1D50B-1D50C + 1D515 + 1D51D + 1D53A + 1D53F + 1D545 + 1D547-1D549 + 1D551 + 1D6A4-1D6A7 + 1D7CA-1D7CD + 1D800-1FFFD + 2A6D7-2F7FF + 2FA1E-2FFFD + 30000-3FFFD + 40000-4FFFD + 50000-5FFFD + 60000-6FFFD + 70000-7FFFD + 80000-8FFFD + 90000-9FFFD + A0000-AFFFD + B0000-BFFFD + C0000-CFFFD + D0000-DFFFD + E0000 + E0002-E001F + E0080-EFFFD + ----- End Table A.1 ----- + + ----- Start Table B.1 ----- + 00AD; ; Map to nothing + 034F; ; Map to nothing + 1806; ; Map to nothing + 180B; ; Map to nothing + 180C; ; Map to nothing + 180D; ; Map to nothing + 200B; ; Map to nothing + 200C; ; Map to nothing + 200D; ; Map to nothing + 2060; ; Map to nothing + FE00; ; Map to nothing + FE01; ; Map to nothing + FE02; ; Map to nothing + FE03; ; Map to nothing + FE04; ; Map to nothing + FE05; ; Map to nothing + FE06; ; Map to nothing + FE07; ; Map to nothing + FE08; ; Map to nothing + FE09; ; Map to nothing + FE0A; ; Map to nothing + FE0B; ; Map to nothing + FE0C; ; Map to nothing + FE0D; ; Map to nothing + FE0E; ; Map to nothing + FE0F; ; Map to nothing + FEFF; ; Map to nothing + ----- End Table B.1 ----- + + ----- Start Table B.2 ----- + 0041; 0061; Case map + 0042; 0062; Case map + 0043; 0063; Case map + 0044; 0064; Case map + 0045; 0065; Case map + 0046; 0066; Case map + 0047; 0067; Case map + 0048; 0068; Case map + 0049; 0069; Case map + 004A; 006A; Case map + 004B; 006B; Case map + 004C; 006C; Case map + 004D; 006D; Case map + 004E; 006E; Case map + 004F; 006F; Case map + 0050; 0070; Case map + 0051; 0071; Case map + 0052; 0072; Case map + 0053; 0073; Case map + 0054; 0074; Case map + 0055; 0075; Case map + 0056; 0076; Case map + 0057; 0077; Case map + 0058; 0078; Case map + 0059; 0079; Case map + 005A; 007A; Case map + 00B5; 03BC; Case map + 00C0; 00E0; Case map + 00C1; 00E1; Case map + 00C2; 00E2; Case map + 00C3; 00E3; Case map + 00C4; 00E4; Case map + 00C5; 00E5; Case map + 00C6; 00E6; Case map + 00C7; 00E7; Case map + 00C8; 00E8; Case map + 00C9; 00E9; Case map + 00CA; 00EA; Case map + 00CB; 00EB; Case map + 00CC; 00EC; Case map + 00CD; 00ED; Case map + 00CE; 00EE; Case map + 00CF; 00EF; Case map + 00D0; 00F0; Case map + 00D1; 00F1; Case map + 00D2; 00F2; Case map + 00D3; 00F3; Case map + 00D4; 00F4; Case map + 00D5; 00F5; Case map + 00D6; 00F6; Case map + 00D8; 00F8; Case map + 00D9; 00F9; Case map + 00DA; 00FA; Case map + 00DB; 00FB; Case map + 00DC; 00FC; Case map + 00DD; 00FD; Case map + 00DE; 00FE; Case map + 00DF; 0073 0073; Case map + 0100; 0101; Case map + 0102; 0103; Case map + 0104; 0105; Case map + 0106; 0107; Case map + 0108; 0109; Case map + 010A; 010B; Case map + 010C; 010D; Case map + 010E; 010F; Case map + 0110; 0111; Case map + 0112; 0113; Case map + 0114; 0115; Case map + 0116; 0117; Case map + 0118; 0119; Case map + 011A; 011B; Case map + 011C; 011D; Case map + 011E; 011F; Case map + 0120; 0121; Case map + 0122; 0123; Case map + 0124; 0125; Case map + 0126; 0127; Case map + 0128; 0129; Case map + 012A; 012B; Case map + 012C; 012D; Case map + 012E; 012F; Case map + 0130; 0069 0307; Case map + 0132; 0133; Case map + 0134; 0135; Case map + 0136; 0137; Case map + 0139; 013A; Case map + 013B; 013C; Case map + 013D; 013E; Case map + 013F; 0140; Case map + 0141; 0142; Case map + 0143; 0144; Case map + 0145; 0146; Case map + 0147; 0148; Case map + 0149; 02BC 006E; Case map + 014A; 014B; Case map + 014C; 014D; Case map + 014E; 014F; Case map + 0150; 0151; Case map + 0152; 0153; Case map + 0154; 0155; Case map + 0156; 0157; Case map + 0158; 0159; Case map + 015A; 015B; Case map + 015C; 015D; Case map + 015E; 015F; Case map + 0160; 0161; Case map + 0162; 0163; Case map + 0164; 0165; Case map + 0166; 0167; Case map + 0168; 0169; Case map + 016A; 016B; Case map + 016C; 016D; Case map + 016E; 016F; Case map + 0170; 0171; Case map + 0172; 0173; Case map + 0174; 0175; Case map + 0176; 0177; Case map + 0178; 00FF; Case map + 0179; 017A; Case map + 017B; 017C; Case map + 017D; 017E; Case map + 017F; 0073; Case map + 0181; 0253; Case map + 0182; 0183; Case map + 0184; 0185; Case map + 0186; 0254; Case map + 0187; 0188; Case map + 0189; 0256; Case map + 018A; 0257; Case map + 018B; 018C; Case map + 018E; 01DD; Case map + 018F; 0259; Case map + 0190; 025B; Case map + 0191; 0192; Case map + 0193; 0260; Case map + 0194; 0263; Case map + 0196; 0269; Case map + 0197; 0268; Case map + 0198; 0199; Case map + 019C; 026F; Case map + 019D; 0272; Case map + 019F; 0275; Case map + 01A0; 01A1; Case map + 01A2; 01A3; Case map + 01A4; 01A5; Case map + 01A6; 0280; Case map + 01A7; 01A8; Case map + 01A9; 0283; Case map + 01AC; 01AD; Case map + 01AE; 0288; Case map + 01AF; 01B0; Case map + 01B1; 028A; Case map + 01B2; 028B; Case map + 01B3; 01B4; Case map + 01B5; 01B6; Case map + 01B7; 0292; Case map + 01B8; 01B9; Case map + 01BC; 01BD; Case map + 01C4; 01C6; Case map + 01C5; 01C6; Case map + 01C7; 01C9; Case map + 01C8; 01C9; Case map + 01CA; 01CC; Case map + 01CB; 01CC; Case map + 01CD; 01CE; Case map + 01CF; 01D0; Case map + 01D1; 01D2; Case map + 01D3; 01D4; Case map + 01D5; 01D6; Case map + 01D7; 01D8; Case map + 01D9; 01DA; Case map + 01DB; 01DC; Case map + 01DE; 01DF; Case map + 01E0; 01E1; Case map + 01E2; 01E3; Case map + 01E4; 01E5; Case map + 01E6; 01E7; Case map + 01E8; 01E9; Case map + 01EA; 01EB; Case map + 01EC; 01ED; Case map + 01EE; 01EF; Case map + 01F0; 006A 030C; Case map + 01F1; 01F3; Case map + 01F2; 01F3; Case map + 01F4; 01F5; Case map + 01F6; 0195; Case map + 01F7; 01BF; Case map + 01F8; 01F9; Case map + 01FA; 01FB; Case map + 01FC; 01FD; Case map + 01FE; 01FF; Case map + 0200; 0201; Case map + 0202; 0203; Case map + 0204; 0205; Case map + 0206; 0207; Case map + 0208; 0209; Case map + 020A; 020B; Case map + 020C; 020D; Case map + 020E; 020F; Case map + 0210; 0211; Case map + 0212; 0213; Case map + 0214; 0215; Case map + 0216; 0217; Case map + 0218; 0219; Case map + 021A; 021B; Case map + 021C; 021D; Case map + 021E; 021F; Case map + 0220; 019E; Case map + 0222; 0223; Case map + 0224; 0225; Case map + 0226; 0227; Case map + 0228; 0229; Case map + 022A; 022B; Case map + 022C; 022D; Case map + 022E; 022F; Case map + 0230; 0231; Case map + 0232; 0233; Case map + 0345; 03B9; Case map + 037A; 0020 03B9; Additional folding + 0386; 03AC; Case map + 0388; 03AD; Case map + 0389; 03AE; Case map + 038A; 03AF; Case map + 038C; 03CC; Case map + 038E; 03CD; Case map + 038F; 03CE; Case map + 0390; 03B9 0308 0301; Case map + 0391; 03B1; Case map + 0392; 03B2; Case map + 0393; 03B3; Case map + 0394; 03B4; Case map + 0395; 03B5; Case map + 0396; 03B6; Case map + 0397; 03B7; Case map + 0398; 03B8; Case map + 0399; 03B9; Case map + 039A; 03BA; Case map + 039B; 03BB; Case map + 039C; 03BC; Case map + 039D; 03BD; Case map + 039E; 03BE; Case map + 039F; 03BF; Case map + 03A0; 03C0; Case map + 03A1; 03C1; Case map + 03A3; 03C3; Case map + 03A4; 03C4; Case map + 03A5; 03C5; Case map + 03A6; 03C6; Case map + 03A7; 03C7; Case map + 03A8; 03C8; Case map + 03A9; 03C9; Case map + 03AA; 03CA; Case map + 03AB; 03CB; Case map + 03B0; 03C5 0308 0301; Case map + 03C2; 03C3; Case map + 03D0; 03B2; Case map + 03D1; 03B8; Case map + 03D2; 03C5; Additional folding + 03D3; 03CD; Additional folding + 03D4; 03CB; Additional folding + 03D5; 03C6; Case map + 03D6; 03C0; Case map + 03D8; 03D9; Case map + 03DA; 03DB; Case map + 03DC; 03DD; Case map + 03DE; 03DF; Case map + 03E0; 03E1; Case map + 03E2; 03E3; Case map + 03E4; 03E5; Case map + 03E6; 03E7; Case map + 03E8; 03E9; Case map + 03EA; 03EB; Case map + 03EC; 03ED; Case map + 03EE; 03EF; Case map + 03F0; 03BA; Case map + 03F1; 03C1; Case map + 03F2; 03C3; Case map + 03F4; 03B8; Case map + 03F5; 03B5; Case map + 0400; 0450; Case map + 0401; 0451; Case map + 0402; 0452; Case map + 0403; 0453; Case map + 0404; 0454; Case map + 0405; 0455; Case map + 0406; 0456; Case map + 0407; 0457; Case map + 0408; 0458; Case map + 0409; 0459; Case map + 040A; 045A; Case map + 040B; 045B; Case map + 040C; 045C; Case map + 040D; 045D; Case map + 040E; 045E; Case map + 040F; 045F; Case map + 0410; 0430; Case map + 0411; 0431; Case map + 0412; 0432; Case map + 0413; 0433; Case map + 0414; 0434; Case map + 0415; 0435; Case map + 0416; 0436; Case map + 0417; 0437; Case map + 0418; 0438; Case map + 0419; 0439; Case map + 041A; 043A; Case map + 041B; 043B; Case map + 041C; 043C; Case map + 041D; 043D; Case map + 041E; 043E; Case map + 041F; 043F; Case map + 0420; 0440; Case map + 0421; 0441; Case map + 0422; 0442; Case map + 0423; 0443; Case map + 0424; 0444; Case map + 0425; 0445; Case map + 0426; 0446; Case map + 0427; 0447; Case map + 0428; 0448; Case map + 0429; 0449; Case map + 042A; 044A; Case map + 042B; 044B; Case map + 042C; 044C; Case map + 042D; 044D; Case map + 042E; 044E; Case map + 042F; 044F; Case map + 0460; 0461; Case map + 0462; 0463; Case map + 0464; 0465; Case map + 0466; 0467; Case map + 0468; 0469; Case map + 046A; 046B; Case map + 046C; 046D; Case map + 046E; 046F; Case map + 0470; 0471; Case map + 0472; 0473; Case map + 0474; 0475; Case map + 0476; 0477; Case map + 0478; 0479; Case map + 047A; 047B; Case map + 047C; 047D; Case map + 047E; 047F; Case map + 0480; 0481; Case map + 048A; 048B; Case map + 048C; 048D; Case map + 048E; 048F; Case map + 0490; 0491; Case map + 0492; 0493; Case map + 0494; 0495; Case map + 0496; 0497; Case map + 0498; 0499; Case map + 049A; 049B; Case map + 049C; 049D; Case map + 049E; 049F; Case map + 04A0; 04A1; Case map + 04A2; 04A3; Case map + 04A4; 04A5; Case map + 04A6; 04A7; Case map + 04A8; 04A9; Case map + 04AA; 04AB; Case map + 04AC; 04AD; Case map + 04AE; 04AF; Case map + 04B0; 04B1; Case map + 04B2; 04B3; Case map + 04B4; 04B5; Case map + 04B6; 04B7; Case map + 04B8; 04B9; Case map + 04BA; 04BB; Case map + 04BC; 04BD; Case map + 04BE; 04BF; Case map + 04C1; 04C2; Case map + 04C3; 04C4; Case map + 04C5; 04C6; Case map + 04C7; 04C8; Case map + 04C9; 04CA; Case map + 04CB; 04CC; Case map + 04CD; 04CE; Case map + 04D0; 04D1; Case map + 04D2; 04D3; Case map + 04D4; 04D5; Case map + 04D6; 04D7; Case map + 04D8; 04D9; Case map + 04DA; 04DB; Case map + 04DC; 04DD; Case map + 04DE; 04DF; Case map + 04E0; 04E1; Case map + 04E2; 04E3; Case map + 04E4; 04E5; Case map + 04E6; 04E7; Case map + 04E8; 04E9; Case map + 04EA; 04EB; Case map + 04EC; 04ED; Case map + 04EE; 04EF; Case map + 04F0; 04F1; Case map + 04F2; 04F3; Case map + 04F4; 04F5; Case map + 04F8; 04F9; Case map + 0500; 0501; Case map + 0502; 0503; Case map + 0504; 0505; Case map + 0506; 0507; Case map + 0508; 0509; Case map + 050A; 050B; Case map + 050C; 050D; Case map + 050E; 050F; Case map + 0531; 0561; Case map + 0532; 0562; Case map + 0533; 0563; Case map + 0534; 0564; Case map + 0535; 0565; Case map + 0536; 0566; Case map + 0537; 0567; Case map + 0538; 0568; Case map + 0539; 0569; Case map + 053A; 056A; Case map + 053B; 056B; Case map + 053C; 056C; Case map + 053D; 056D; Case map + 053E; 056E; Case map + 053F; 056F; Case map + 0540; 0570; Case map + 0541; 0571; Case map + 0542; 0572; Case map + 0543; 0573; Case map + 0544; 0574; Case map + 0545; 0575; Case map + 0546; 0576; Case map + 0547; 0577; Case map + 0548; 0578; Case map + 0549; 0579; Case map + 054A; 057A; Case map + 054B; 057B; Case map + 054C; 057C; Case map + 054D; 057D; Case map + 054E; 057E; Case map + 054F; 057F; Case map + 0550; 0580; Case map + 0551; 0581; Case map + 0552; 0582; Case map + 0553; 0583; Case map + 0554; 0584; Case map + 0555; 0585; Case map + 0556; 0586; Case map + 0587; 0565 0582; Case map + 1E00; 1E01; Case map + 1E02; 1E03; Case map + 1E04; 1E05; Case map + 1E06; 1E07; Case map + 1E08; 1E09; Case map + 1E0A; 1E0B; Case map + 1E0C; 1E0D; Case map + 1E0E; 1E0F; Case map + 1E10; 1E11; Case map + 1E12; 1E13; Case map + 1E14; 1E15; Case map + 1E16; 1E17; Case map + 1E18; 1E19; Case map + 1E1A; 1E1B; Case map + 1E1C; 1E1D; Case map + 1E1E; 1E1F; Case map + 1E20; 1E21; Case map + 1E22; 1E23; Case map + 1E24; 1E25; Case map + 1E26; 1E27; Case map + 1E28; 1E29; Case map + 1E2A; 1E2B; Case map + 1E2C; 1E2D; Case map + 1E2E; 1E2F; Case map + 1E30; 1E31; Case map + 1E32; 1E33; Case map + 1E34; 1E35; Case map + 1E36; 1E37; Case map + 1E38; 1E39; Case map + 1E3A; 1E3B; Case map + 1E3C; 1E3D; Case map + 1E3E; 1E3F; Case map + 1E40; 1E41; Case map + 1E42; 1E43; Case map + 1E44; 1E45; Case map + 1E46; 1E47; Case map + 1E48; 1E49; Case map + 1E4A; 1E4B; Case map + 1E4C; 1E4D; Case map + 1E4E; 1E4F; Case map + 1E50; 1E51; Case map + 1E52; 1E53; Case map + 1E54; 1E55; Case map + 1E56; 1E57; Case map + 1E58; 1E59; Case map + 1E5A; 1E5B; Case map + 1E5C; 1E5D; Case map + 1E5E; 1E5F; Case map + 1E60; 1E61; Case map + 1E62; 1E63; Case map + 1E64; 1E65; Case map + 1E66; 1E67; Case map + 1E68; 1E69; Case map + 1E6A; 1E6B; Case map + 1E6C; 1E6D; Case map + 1E6E; 1E6F; Case map + 1E70; 1E71; Case map + 1E72; 1E73; Case map + 1E74; 1E75; Case map + 1E76; 1E77; Case map + 1E78; 1E79; Case map + 1E7A; 1E7B; Case map + 1E7C; 1E7D; Case map + 1E7E; 1E7F; Case map + 1E80; 1E81; Case map + 1E82; 1E83; Case map + 1E84; 1E85; Case map + 1E86; 1E87; Case map + 1E88; 1E89; Case map + 1E8A; 1E8B; Case map + 1E8C; 1E8D; Case map + 1E8E; 1E8F; Case map + 1E90; 1E91; Case map + 1E92; 1E93; Case map + 1E94; 1E95; Case map + 1E96; 0068 0331; Case map + 1E97; 0074 0308; Case map + 1E98; 0077 030A; Case map + 1E99; 0079 030A; Case map + 1E9A; 0061 02BE; Case map + 1E9B; 1E61; Case map + 1EA0; 1EA1; Case map + 1EA2; 1EA3; Case map + 1EA4; 1EA5; Case map + 1EA6; 1EA7; Case map + 1EA8; 1EA9; Case map + 1EAA; 1EAB; Case map + 1EAC; 1EAD; Case map + 1EAE; 1EAF; Case map + 1EB0; 1EB1; Case map + 1EB2; 1EB3; Case map + 1EB4; 1EB5; Case map + 1EB6; 1EB7; Case map + 1EB8; 1EB9; Case map + 1EBA; 1EBB; Case map + 1EBC; 1EBD; Case map + 1EBE; 1EBF; Case map + 1EC0; 1EC1; Case map + 1EC2; 1EC3; Case map + 1EC4; 1EC5; Case map + 1EC6; 1EC7; Case map + 1EC8; 1EC9; Case map + 1ECA; 1ECB; Case map + 1ECC; 1ECD; Case map + 1ECE; 1ECF; Case map + 1ED0; 1ED1; Case map + 1ED2; 1ED3; Case map + 1ED4; 1ED5; Case map + 1ED6; 1ED7; Case map + 1ED8; 1ED9; Case map + 1EDA; 1EDB; Case map + 1EDC; 1EDD; Case map + 1EDE; 1EDF; Case map + 1EE0; 1EE1; Case map + 1EE2; 1EE3; Case map + 1EE4; 1EE5; Case map + 1EE6; 1EE7; Case map + 1EE8; 1EE9; Case map + 1EEA; 1EEB; Case map + 1EEC; 1EED; Case map + 1EEE; 1EEF; Case map + 1EF0; 1EF1; Case map + 1EF2; 1EF3; Case map + 1EF4; 1EF5; Case map + 1EF6; 1EF7; Case map + 1EF8; 1EF9; Case map + 1F08; 1F00; Case map + 1F09; 1F01; Case map + 1F0A; 1F02; Case map + 1F0B; 1F03; Case map + 1F0C; 1F04; Case map + 1F0D; 1F05; Case map + 1F0E; 1F06; Case map + 1F0F; 1F07; Case map + 1F18; 1F10; Case map + 1F19; 1F11; Case map + 1F1A; 1F12; Case map + 1F1B; 1F13; Case map + 1F1C; 1F14; Case map + 1F1D; 1F15; Case map + 1F28; 1F20; Case map + 1F29; 1F21; Case map + 1F2A; 1F22; Case map + 1F2B; 1F23; Case map + 1F2C; 1F24; Case map + 1F2D; 1F25; Case map + 1F2E; 1F26; Case map + 1F2F; 1F27; Case map + 1F38; 1F30; Case map + 1F39; 1F31; Case map + 1F3A; 1F32; Case map + 1F3B; 1F33; Case map + 1F3C; 1F34; Case map + 1F3D; 1F35; Case map + 1F3E; 1F36; Case map + 1F3F; 1F37; Case map + 1F48; 1F40; Case map + 1F49; 1F41; Case map + 1F4A; 1F42; Case map + 1F4B; 1F43; Case map + 1F4C; 1F44; Case map + 1F4D; 1F45; Case map + 1F50; 03C5 0313; Case map + 1F52; 03C5 0313 0300; Case map + 1F54; 03C5 0313 0301; Case map + 1F56; 03C5 0313 0342; Case map + 1F59; 1F51; Case map + 1F5B; 1F53; Case map + 1F5D; 1F55; Case map + 1F5F; 1F57; Case map + 1F68; 1F60; Case map + 1F69; 1F61; Case map + 1F6A; 1F62; Case map + 1F6B; 1F63; Case map + 1F6C; 1F64; Case map + 1F6D; 1F65; Case map + 1F6E; 1F66; Case map + 1F6F; 1F67; Case map + 1F80; 1F00 03B9; Case map + 1F81; 1F01 03B9; Case map + 1F82; 1F02 03B9; Case map + 1F83; 1F03 03B9; Case map + 1F84; 1F04 03B9; Case map + 1F85; 1F05 03B9; Case map + 1F86; 1F06 03B9; Case map + 1F87; 1F07 03B9; Case map + 1F88; 1F00 03B9; Case map + 1F89; 1F01 03B9; Case map + 1F8A; 1F02 03B9; Case map + 1F8B; 1F03 03B9; Case map + 1F8C; 1F04 03B9; Case map + 1F8D; 1F05 03B9; Case map + 1F8E; 1F06 03B9; Case map + 1F8F; 1F07 03B9; Case map + 1F90; 1F20 03B9; Case map + 1F91; 1F21 03B9; Case map + 1F92; 1F22 03B9; Case map + 1F93; 1F23 03B9; Case map + 1F94; 1F24 03B9; Case map + 1F95; 1F25 03B9; Case map + 1F96; 1F26 03B9; Case map + 1F97; 1F27 03B9; Case map + 1F98; 1F20 03B9; Case map + 1F99; 1F21 03B9; Case map + 1F9A; 1F22 03B9; Case map + 1F9B; 1F23 03B9; Case map + 1F9C; 1F24 03B9; Case map + 1F9D; 1F25 03B9; Case map + 1F9E; 1F26 03B9; Case map + 1F9F; 1F27 03B9; Case map + 1FA0; 1F60 03B9; Case map + 1FA1; 1F61 03B9; Case map + 1FA2; 1F62 03B9; Case map + 1FA3; 1F63 03B9; Case map + 1FA4; 1F64 03B9; Case map + 1FA5; 1F65 03B9; Case map + 1FA6; 1F66 03B9; Case map + 1FA7; 1F67 03B9; Case map + 1FA8; 1F60 03B9; Case map + 1FA9; 1F61 03B9; Case map + 1FAA; 1F62 03B9; Case map + 1FAB; 1F63 03B9; Case map + 1FAC; 1F64 03B9; Case map + 1FAD; 1F65 03B9; Case map + 1FAE; 1F66 03B9; Case map + 1FAF; 1F67 03B9; Case map + 1FB2; 1F70 03B9; Case map + 1FB3; 03B1 03B9; Case map + 1FB4; 03AC 03B9; Case map + 1FB6; 03B1 0342; Case map + 1FB7; 03B1 0342 03B9; Case map + 1FB8; 1FB0; Case map + 1FB9; 1FB1; Case map + 1FBA; 1F70; Case map + 1FBB; 1F71; Case map + 1FBC; 03B1 03B9; Case map + 1FBE; 03B9; Case map + 1FC2; 1F74 03B9; Case map + 1FC3; 03B7 03B9; Case map + 1FC4; 03AE 03B9; Case map + 1FC6; 03B7 0342; Case map + 1FC7; 03B7 0342 03B9; Case map + 1FC8; 1F72; Case map + 1FC9; 1F73; Case map + 1FCA; 1F74; Case map + 1FCB; 1F75; Case map + 1FCC; 03B7 03B9; Case map + 1FD2; 03B9 0308 0300; Case map + 1FD3; 03B9 0308 0301; Case map + 1FD6; 03B9 0342; Case map + 1FD7; 03B9 0308 0342; Case map + 1FD8; 1FD0; Case map + 1FD9; 1FD1; Case map + 1FDA; 1F76; Case map + 1FDB; 1F77; Case map + 1FE2; 03C5 0308 0300; Case map + 1FE3; 03C5 0308 0301; Case map + 1FE4; 03C1 0313; Case map + 1FE6; 03C5 0342; Case map + 1FE7; 03C5 0308 0342; Case map + 1FE8; 1FE0; Case map + 1FE9; 1FE1; Case map + 1FEA; 1F7A; Case map + 1FEB; 1F7B; Case map + 1FEC; 1FE5; Case map + 1FF2; 1F7C 03B9; Case map + 1FF3; 03C9 03B9; Case map + 1FF4; 03CE 03B9; Case map + 1FF6; 03C9 0342; Case map + 1FF7; 03C9 0342 03B9; Case map + 1FF8; 1F78; Case map + 1FF9; 1F79; Case map + 1FFA; 1F7C; Case map + 1FFB; 1F7D; Case map + 1FFC; 03C9 03B9; Case map + 20A8; 0072 0073; Additional folding + 2102; 0063; Additional folding + 2103; 00B0 0063; Additional folding + 2107; 025B; Additional folding + 2109; 00B0 0066; Additional folding + 210B; 0068; Additional folding + 210C; 0068; Additional folding + 210D; 0068; Additional folding + 2110; 0069; Additional folding + 2111; 0069; Additional folding + 2112; 006C; Additional folding + 2115; 006E; Additional folding + 2116; 006E 006F; Additional folding + 2119; 0070; Additional folding + 211A; 0071; Additional folding + 211B; 0072; Additional folding + 211C; 0072; Additional folding + 211D; 0072; Additional folding + 2120; 0073 006D; Additional folding + 2121; 0074 0065 006C; Additional folding + 2122; 0074 006D; Additional folding + 2124; 007A; Additional folding + 2126; 03C9; Case map + 2128; 007A; Additional folding + 212A; 006B; Case map + 212B; 00E5; Case map + 212C; 0062; Additional folding + 212D; 0063; Additional folding + 2130; 0065; Additional folding + 2131; 0066; Additional folding + 2133; 006D; Additional folding + 213E; 03B3; Additional folding + 213F; 03C0; Additional folding + 2145; 0064; Additional folding + 2160; 2170; Case map + 2161; 2171; Case map + 2162; 2172; Case map + 2163; 2173; Case map + 2164; 2174; Case map + 2165; 2175; Case map + 2166; 2176; Case map + 2167; 2177; Case map + 2168; 2178; Case map + 2169; 2179; Case map + 216A; 217A; Case map + 216B; 217B; Case map + 216C; 217C; Case map + 216D; 217D; Case map + 216E; 217E; Case map + 216F; 217F; Case map + 24B6; 24D0; Case map + 24B7; 24D1; Case map + 24B8; 24D2; Case map + 24B9; 24D3; Case map + 24BA; 24D4; Case map + 24BB; 24D5; Case map + 24BC; 24D6; Case map + 24BD; 24D7; Case map + 24BE; 24D8; Case map + 24BF; 24D9; Case map + 24C0; 24DA; Case map + 24C1; 24DB; Case map + 24C2; 24DC; Case map + 24C3; 24DD; Case map + 24C4; 24DE; Case map + 24C5; 24DF; Case map + 24C6; 24E0; Case map + 24C7; 24E1; Case map + 24C8; 24E2; Case map + 24C9; 24E3; Case map + 24CA; 24E4; Case map + 24CB; 24E5; Case map + 24CC; 24E6; Case map + 24CD; 24E7; Case map + 24CE; 24E8; Case map + 24CF; 24E9; Case map + 3371; 0068 0070 0061; Additional folding + 3373; 0061 0075; Additional folding + 3375; 006F 0076; Additional folding + 3380; 0070 0061; Additional folding + 3381; 006E 0061; Additional folding + 3382; 03BC 0061; Additional folding + 3383; 006D 0061; Additional folding + 3384; 006B 0061; Additional folding + 3385; 006B 0062; Additional folding + 3386; 006D 0062; Additional folding + 3387; 0067 0062; Additional folding + 338A; 0070 0066; Additional folding + 338B; 006E 0066; Additional folding + 338C; 03BC 0066; Additional folding + 3390; 0068 007A; Additional folding + 3391; 006B 0068 007A; Additional folding + 3392; 006D 0068 007A; Additional folding + 3393; 0067 0068 007A; Additional folding + 3394; 0074 0068 007A; Additional folding + 33A9; 0070 0061; Additional folding + 33AA; 006B 0070 0061; Additional folding + 33AB; 006D 0070 0061; Additional folding + 33AC; 0067 0070 0061; Additional folding + 33B4; 0070 0076; Additional folding + 33B5; 006E 0076; Additional folding + 33B6; 03BC 0076; Additional folding + 33B7; 006D 0076; Additional folding + 33B8; 006B 0076; Additional folding + 33B9; 006D 0076; Additional folding + 33BA; 0070 0077; Additional folding + 33BB; 006E 0077; Additional folding + 33BC; 03BC 0077; Additional folding + 33BD; 006D 0077; Additional folding + 33BE; 006B 0077; Additional folding + 33BF; 006D 0077; Additional folding + 33C0; 006B 03C9; Additional folding + 33C1; 006D 03C9; Additional folding + 33C3; 0062 0071; Additional folding + 33C6; 0063 2215 006B 0067; Additional folding + 33C7; 0063 006F 002E; Additional folding + 33C8; 0064 0062; Additional folding + 33C9; 0067 0079; Additional folding + 33CB; 0068 0070; Additional folding + 33CD; 006B 006B; Additional folding + 33CE; 006B 006D; Additional folding + 33D7; 0070 0068; Additional folding + 33D9; 0070 0070 006D; Additional folding + 33DA; 0070 0072; Additional folding + 33DC; 0073 0076; Additional folding + 33DD; 0077 0062; Additional folding + FB00; 0066 0066; Case map + FB01; 0066 0069; Case map + FB02; 0066 006C; Case map + FB03; 0066 0066 0069; Case map + FB04; 0066 0066 006C; Case map + FB05; 0073 0074; Case map + FB06; 0073 0074; Case map + FB13; 0574 0576; Case map + FB14; 0574 0565; Case map + FB15; 0574 056B; Case map + FB16; 057E 0576; Case map + FB17; 0574 056D; Case map + FF21; FF41; Case map + FF22; FF42; Case map + FF23; FF43; Case map + FF24; FF44; Case map + FF25; FF45; Case map + FF26; FF46; Case map + FF27; FF47; Case map + FF28; FF48; Case map + FF29; FF49; Case map + FF2A; FF4A; Case map + FF2B; FF4B; Case map + FF2C; FF4C; Case map + FF2D; FF4D; Case map + FF2E; FF4E; Case map + FF2F; FF4F; Case map + FF30; FF50; Case map + FF31; FF51; Case map + FF32; FF52; Case map + FF33; FF53; Case map + FF34; FF54; Case map + FF35; FF55; Case map + FF36; FF56; Case map + FF37; FF57; Case map + FF38; FF58; Case map + FF39; FF59; Case map + FF3A; FF5A; Case map + 10400; 10428; Case map + 10401; 10429; Case map + 10402; 1042A; Case map + 10403; 1042B; Case map + 10404; 1042C; Case map + 10405; 1042D; Case map + 10406; 1042E; Case map + 10407; 1042F; Case map + 10408; 10430; Case map + 10409; 10431; Case map + 1040A; 10432; Case map + 1040B; 10433; Case map + 1040C; 10434; Case map + 1040D; 10435; Case map + 1040E; 10436; Case map + 1040F; 10437; Case map + 10410; 10438; Case map + 10411; 10439; Case map + 10412; 1043A; Case map + 10413; 1043B; Case map + 10414; 1043C; Case map + 10415; 1043D; Case map + 10416; 1043E; Case map + 10417; 1043F; Case map + 10418; 10440; Case map + 10419; 10441; Case map + 1041A; 10442; Case map + 1041B; 10443; Case map + 1041C; 10444; Case map + 1041D; 10445; Case map + 1041E; 10446; Case map + 1041F; 10447; Case map + 10420; 10448; Case map + 10421; 10449; Case map + 10422; 1044A; Case map + 10423; 1044B; Case map + 10424; 1044C; Case map + 10425; 1044D; Case map + 1D400; 0061; Additional folding + 1D401; 0062; Additional folding + 1D402; 0063; Additional folding + 1D403; 0064; Additional folding + 1D404; 0065; Additional folding + 1D405; 0066; Additional folding + 1D406; 0067; Additional folding + 1D407; 0068; Additional folding + 1D408; 0069; Additional folding + 1D409; 006A; Additional folding + 1D40A; 006B; Additional folding + 1D40B; 006C; Additional folding + 1D40C; 006D; Additional folding + 1D40D; 006E; Additional folding + 1D40E; 006F; Additional folding + 1D40F; 0070; Additional folding + 1D410; 0071; Additional folding + 1D411; 0072; Additional folding + 1D412; 0073; Additional folding + 1D413; 0074; Additional folding + 1D414; 0075; Additional folding + 1D415; 0076; Additional folding + 1D416; 0077; Additional folding + 1D417; 0078; Additional folding + 1D418; 0079; Additional folding + 1D419; 007A; Additional folding + 1D434; 0061; Additional folding + 1D435; 0062; Additional folding + 1D436; 0063; Additional folding + 1D437; 0064; Additional folding + 1D438; 0065; Additional folding + 1D439; 0066; Additional folding + 1D43A; 0067; Additional folding + 1D43B; 0068; Additional folding + 1D43C; 0069; Additional folding + 1D43D; 006A; Additional folding + 1D43E; 006B; Additional folding + 1D43F; 006C; Additional folding + 1D440; 006D; Additional folding + 1D441; 006E; Additional folding + 1D442; 006F; Additional folding + 1D443; 0070; Additional folding + 1D444; 0071; Additional folding + 1D445; 0072; Additional folding + 1D446; 0073; Additional folding + 1D447; 0074; Additional folding + 1D448; 0075; Additional folding + 1D449; 0076; Additional folding + 1D44A; 0077; Additional folding + 1D44B; 0078; Additional folding + 1D44C; 0079; Additional folding + 1D44D; 007A; Additional folding + 1D468; 0061; Additional folding + 1D469; 0062; Additional folding + 1D46A; 0063; Additional folding + 1D46B; 0064; Additional folding + 1D46C; 0065; Additional folding + 1D46D; 0066; Additional folding + 1D46E; 0067; Additional folding + 1D46F; 0068; Additional folding + 1D470; 0069; Additional folding + 1D471; 006A; Additional folding + 1D472; 006B; Additional folding + 1D473; 006C; Additional folding + 1D474; 006D; Additional folding + 1D475; 006E; Additional folding + 1D476; 006F; Additional folding + 1D477; 0070; Additional folding + 1D478; 0071; Additional folding + 1D479; 0072; Additional folding + 1D47A; 0073; Additional folding + 1D47B; 0074; Additional folding + 1D47C; 0075; Additional folding + 1D47D; 0076; Additional folding + 1D47E; 0077; Additional folding + 1D47F; 0078; Additional folding + 1D480; 0079; Additional folding + 1D481; 007A; Additional folding + 1D49C; 0061; Additional folding + 1D49E; 0063; Additional folding + 1D49F; 0064; Additional folding + 1D4A2; 0067; Additional folding + 1D4A5; 006A; Additional folding + 1D4A6; 006B; Additional folding + 1D4A9; 006E; Additional folding + 1D4AA; 006F; Additional folding + 1D4AB; 0070; Additional folding + 1D4AC; 0071; Additional folding + 1D4AE; 0073; Additional folding + 1D4AF; 0074; Additional folding + 1D4B0; 0075; Additional folding + 1D4B1; 0076; Additional folding + 1D4B2; 0077; Additional folding + 1D4B3; 0078; Additional folding + 1D4B4; 0079; Additional folding + 1D4B5; 007A; Additional folding + 1D4D0; 0061; Additional folding + 1D4D1; 0062; Additional folding + 1D4D2; 0063; Additional folding + 1D4D3; 0064; Additional folding + 1D4D4; 0065; Additional folding + 1D4D5; 0066; Additional folding + 1D4D6; 0067; Additional folding + 1D4D7; 0068; Additional folding + 1D4D8; 0069; Additional folding + 1D4D9; 006A; Additional folding + 1D4DA; 006B; Additional folding + 1D4DB; 006C; Additional folding + 1D4DC; 006D; Additional folding + 1D4DD; 006E; Additional folding + 1D4DE; 006F; Additional folding + 1D4DF; 0070; Additional folding + 1D4E0; 0071; Additional folding + 1D4E1; 0072; Additional folding + 1D4E2; 0073; Additional folding + 1D4E3; 0074; Additional folding + 1D4E4; 0075; Additional folding + 1D4E5; 0076; Additional folding + 1D4E6; 0077; Additional folding + 1D4E7; 0078; Additional folding + 1D4E8; 0079; Additional folding + 1D4E9; 007A; Additional folding + 1D504; 0061; Additional folding + 1D505; 0062; Additional folding + 1D507; 0064; Additional folding + 1D508; 0065; Additional folding + 1D509; 0066; Additional folding + 1D50A; 0067; Additional folding + 1D50D; 006A; Additional folding + 1D50E; 006B; Additional folding + 1D50F; 006C; Additional folding + 1D510; 006D; Additional folding + 1D511; 006E; Additional folding + 1D512; 006F; Additional folding + 1D513; 0070; Additional folding + 1D514; 0071; Additional folding + 1D516; 0073; Additional folding + 1D517; 0074; Additional folding + 1D518; 0075; Additional folding + 1D519; 0076; Additional folding + 1D51A; 0077; Additional folding + 1D51B; 0078; Additional folding + 1D51C; 0079; Additional folding + 1D538; 0061; Additional folding + 1D539; 0062; Additional folding + 1D53B; 0064; Additional folding + 1D53C; 0065; Additional folding + 1D53D; 0066; Additional folding + 1D53E; 0067; Additional folding + 1D540; 0069; Additional folding + 1D541; 006A; Additional folding + 1D542; 006B; Additional folding + 1D543; 006C; Additional folding + 1D544; 006D; Additional folding + 1D546; 006F; Additional folding + 1D54A; 0073; Additional folding + 1D54B; 0074; Additional folding + 1D54C; 0075; Additional folding + 1D54D; 0076; Additional folding + 1D54E; 0077; Additional folding + 1D54F; 0078; Additional folding + 1D550; 0079; Additional folding + 1D56C; 0061; Additional folding + 1D56D; 0062; Additional folding + 1D56E; 0063; Additional folding + 1D56F; 0064; Additional folding + 1D570; 0065; Additional folding + 1D571; 0066; Additional folding + 1D572; 0067; Additional folding + 1D573; 0068; Additional folding + 1D574; 0069; Additional folding + 1D575; 006A; Additional folding + 1D576; 006B; Additional folding + 1D577; 006C; Additional folding + 1D578; 006D; Additional folding + 1D579; 006E; Additional folding + 1D57A; 006F; Additional folding + 1D57B; 0070; Additional folding + 1D57C; 0071; Additional folding + 1D57D; 0072; Additional folding + 1D57E; 0073; Additional folding + 1D57F; 0074; Additional folding + 1D580; 0075; Additional folding + 1D581; 0076; Additional folding + 1D582; 0077; Additional folding + 1D583; 0078; Additional folding + 1D584; 0079; Additional folding + 1D585; 007A; Additional folding + 1D5A0; 0061; Additional folding + 1D5A1; 0062; Additional folding + 1D5A2; 0063; Additional folding + 1D5A3; 0064; Additional folding + 1D5A4; 0065; Additional folding + 1D5A5; 0066; Additional folding + 1D5A6; 0067; Additional folding + 1D5A7; 0068; Additional folding + 1D5A8; 0069; Additional folding + 1D5A9; 006A; Additional folding + 1D5AA; 006B; Additional folding + 1D5AB; 006C; Additional folding + 1D5AC; 006D; Additional folding + 1D5AD; 006E; Additional folding + 1D5AE; 006F; Additional folding + 1D5AF; 0070; Additional folding + 1D5B0; 0071; Additional folding + 1D5B1; 0072; Additional folding + 1D5B2; 0073; Additional folding + 1D5B3; 0074; Additional folding + 1D5B4; 0075; Additional folding + 1D5B5; 0076; Additional folding + 1D5B6; 0077; Additional folding + 1D5B7; 0078; Additional folding + 1D5B8; 0079; Additional folding + 1D5B9; 007A; Additional folding + 1D5D4; 0061; Additional folding + 1D5D5; 0062; Additional folding + 1D5D6; 0063; Additional folding + 1D5D7; 0064; Additional folding + 1D5D8; 0065; Additional folding + 1D5D9; 0066; Additional folding + 1D5DA; 0067; Additional folding + 1D5DB; 0068; Additional folding + 1D5DC; 0069; Additional folding + 1D5DD; 006A; Additional folding + 1D5DE; 006B; Additional folding + 1D5DF; 006C; Additional folding + 1D5E0; 006D; Additional folding + 1D5E1; 006E; Additional folding + 1D5E2; 006F; Additional folding + 1D5E3; 0070; Additional folding + 1D5E4; 0071; Additional folding + 1D5E5; 0072; Additional folding + 1D5E6; 0073; Additional folding + 1D5E7; 0074; Additional folding + 1D5E8; 0075; Additional folding + 1D5E9; 0076; Additional folding + 1D5EA; 0077; Additional folding + 1D5EB; 0078; Additional folding + 1D5EC; 0079; Additional folding + 1D5ED; 007A; Additional folding + 1D608; 0061; Additional folding + 1D609; 0062; Additional folding + 1D60A; 0063; Additional folding + 1D60B; 0064; Additional folding + 1D60C; 0065; Additional folding + 1D60D; 0066; Additional folding + 1D60E; 0067; Additional folding + 1D60F; 0068; Additional folding + 1D610; 0069; Additional folding + 1D611; 006A; Additional folding + 1D612; 006B; Additional folding + 1D613; 006C; Additional folding + 1D614; 006D; Additional folding + 1D615; 006E; Additional folding + 1D616; 006F; Additional folding + 1D617; 0070; Additional folding + 1D618; 0071; Additional folding + 1D619; 0072; Additional folding + 1D61A; 0073; Additional folding + 1D61B; 0074; Additional folding + 1D61C; 0075; Additional folding + 1D61D; 0076; Additional folding + 1D61E; 0077; Additional folding + 1D61F; 0078; Additional folding + 1D620; 0079; Additional folding + 1D621; 007A; Additional folding + 1D63C; 0061; Additional folding + 1D63D; 0062; Additional folding + 1D63E; 0063; Additional folding + 1D63F; 0064; Additional folding + 1D640; 0065; Additional folding + 1D641; 0066; Additional folding + 1D642; 0067; Additional folding + 1D643; 0068; Additional folding + 1D644; 0069; Additional folding + 1D645; 006A; Additional folding + 1D646; 006B; Additional folding + 1D647; 006C; Additional folding + 1D648; 006D; Additional folding + 1D649; 006E; Additional folding + 1D64A; 006F; Additional folding + 1D64B; 0070; Additional folding + 1D64C; 0071; Additional folding + 1D64D; 0072; Additional folding + 1D64E; 0073; Additional folding + 1D64F; 0074; Additional folding + 1D650; 0075; Additional folding + 1D651; 0076; Additional folding + 1D652; 0077; Additional folding + 1D653; 0078; Additional folding + 1D654; 0079; Additional folding + 1D655; 007A; Additional folding + 1D670; 0061; Additional folding + 1D671; 0062; Additional folding + 1D672; 0063; Additional folding + 1D673; 0064; Additional folding + 1D674; 0065; Additional folding + 1D675; 0066; Additional folding + 1D676; 0067; Additional folding + 1D677; 0068; Additional folding + 1D678; 0069; Additional folding + 1D679; 006A; Additional folding + 1D67A; 006B; Additional folding + 1D67B; 006C; Additional folding + 1D67C; 006D; Additional folding + 1D67D; 006E; Additional folding + 1D67E; 006F; Additional folding + 1D67F; 0070; Additional folding + 1D680; 0071; Additional folding + 1D681; 0072; Additional folding + 1D682; 0073; Additional folding + 1D683; 0074; Additional folding + 1D684; 0075; Additional folding + 1D685; 0076; Additional folding + 1D686; 0077; Additional folding + 1D687; 0078; Additional folding + 1D688; 0079; Additional folding + 1D689; 007A; Additional folding + 1D6A8; 03B1; Additional folding + 1D6A9; 03B2; Additional folding + 1D6AA; 03B3; Additional folding + 1D6AB; 03B4; Additional folding + 1D6AC; 03B5; Additional folding + 1D6AD; 03B6; Additional folding + 1D6AE; 03B7; Additional folding + 1D6AF; 03B8; Additional folding + 1D6B0; 03B9; Additional folding + 1D6B1; 03BA; Additional folding + 1D6B2; 03BB; Additional folding + 1D6B3; 03BC; Additional folding + 1D6B4; 03BD; Additional folding + 1D6B5; 03BE; Additional folding + 1D6B6; 03BF; Additional folding + 1D6B7; 03C0; Additional folding + 1D6B8; 03C1; Additional folding + 1D6B9; 03B8; Additional folding + 1D6BA; 03C3; Additional folding + 1D6BB; 03C4; Additional folding + 1D6BC; 03C5; Additional folding + 1D6BD; 03C6; Additional folding + 1D6BE; 03C7; Additional folding + 1D6BF; 03C8; Additional folding + 1D6C0; 03C9; Additional folding + 1D6D3; 03C3; Additional folding + 1D6E2; 03B1; Additional folding + 1D6E3; 03B2; Additional folding + 1D6E4; 03B3; Additional folding + 1D6E5; 03B4; Additional folding + 1D6E6; 03B5; Additional folding + 1D6E7; 03B6; Additional folding + 1D6E8; 03B7; Additional folding + 1D6E9; 03B8; Additional folding + 1D6EA; 03B9; Additional folding + 1D6EB; 03BA; Additional folding + 1D6EC; 03BB; Additional folding + 1D6ED; 03BC; Additional folding + 1D6EE; 03BD; Additional folding + 1D6EF; 03BE; Additional folding + 1D6F0; 03BF; Additional folding + 1D6F1; 03C0; Additional folding + 1D6F2; 03C1; Additional folding + 1D6F3; 03B8; Additional folding + 1D6F4; 03C3; Additional folding + 1D6F5; 03C4; Additional folding + 1D6F6; 03C5; Additional folding + 1D6F7; 03C6; Additional folding + 1D6F8; 03C7; Additional folding + 1D6F9; 03C8; Additional folding + 1D6FA; 03C9; Additional folding + 1D70D; 03C3; Additional folding + 1D71C; 03B1; Additional folding + 1D71D; 03B2; Additional folding + 1D71E; 03B3; Additional folding + 1D71F; 03B4; Additional folding + 1D720; 03B5; Additional folding + 1D721; 03B6; Additional folding + 1D722; 03B7; Additional folding + 1D723; 03B8; Additional folding + 1D724; 03B9; Additional folding + 1D725; 03BA; Additional folding + 1D726; 03BB; Additional folding + 1D727; 03BC; Additional folding + 1D728; 03BD; Additional folding + 1D729; 03BE; Additional folding + 1D72A; 03BF; Additional folding + 1D72B; 03C0; Additional folding + 1D72C; 03C1; Additional folding + 1D72D; 03B8; Additional folding + 1D72E; 03C3; Additional folding + 1D72F; 03C4; Additional folding + 1D730; 03C5; Additional folding + 1D731; 03C6; Additional folding + 1D732; 03C7; Additional folding + 1D733; 03C8; Additional folding + 1D734; 03C9; Additional folding + 1D747; 03C3; Additional folding + 1D756; 03B1; Additional folding + 1D757; 03B2; Additional folding + 1D758; 03B3; Additional folding + 1D759; 03B4; Additional folding + 1D75A; 03B5; Additional folding + 1D75B; 03B6; Additional folding + 1D75C; 03B7; Additional folding + 1D75D; 03B8; Additional folding + 1D75E; 03B9; Additional folding + 1D75F; 03BA; Additional folding + 1D760; 03BB; Additional folding + 1D761; 03BC; Additional folding + 1D762; 03BD; Additional folding + 1D763; 03BE; Additional folding + 1D764; 03BF; Additional folding + 1D765; 03C0; Additional folding + 1D766; 03C1; Additional folding + 1D767; 03B8; Additional folding + 1D768; 03C3; Additional folding + 1D769; 03C4; Additional folding + 1D76A; 03C5; Additional folding + 1D76B; 03C6; Additional folding + 1D76C; 03C7; Additional folding + 1D76D; 03C8; Additional folding + 1D76E; 03C9; Additional folding + 1D781; 03C3; Additional folding + 1D790; 03B1; Additional folding + 1D791; 03B2; Additional folding + 1D792; 03B3; Additional folding + 1D793; 03B4; Additional folding + 1D794; 03B5; Additional folding + 1D795; 03B6; Additional folding + 1D796; 03B7; Additional folding + 1D797; 03B8; Additional folding + 1D798; 03B9; Additional folding + 1D799; 03BA; Additional folding + 1D79A; 03BB; Additional folding + 1D79B; 03BC; Additional folding + 1D79C; 03BD; Additional folding + 1D79D; 03BE; Additional folding + 1D79E; 03BF; Additional folding + 1D79F; 03C0; Additional folding + 1D7A0; 03C1; Additional folding + 1D7A1; 03B8; Additional folding + 1D7A2; 03C3; Additional folding + 1D7A3; 03C4; Additional folding + 1D7A4; 03C5; Additional folding + 1D7A5; 03C6; Additional folding + 1D7A6; 03C7; Additional folding + 1D7A7; 03C8; Additional folding + 1D7A8; 03C9; Additional folding + 1D7BB; 03C3; Additional folding + ----- End Table B.2 ----- + + ----- Start Table B.3 ----- + 0041; 0061; Case map + 0042; 0062; Case map + 0043; 0063; Case map + 0044; 0064; Case map + 0045; 0065; Case map + 0046; 0066; Case map + 0047; 0067; Case map + 0048; 0068; Case map + 0049; 0069; Case map + 004A; 006A; Case map + 004B; 006B; Case map + 004C; 006C; Case map + 004D; 006D; Case map + 004E; 006E; Case map + 004F; 006F; Case map + 0050; 0070; Case map + 0051; 0071; Case map + 0052; 0072; Case map + 0053; 0073; Case map + 0054; 0074; Case map + 0055; 0075; Case map + 0056; 0076; Case map + 0057; 0077; Case map + 0058; 0078; Case map + 0059; 0079; Case map + 005A; 007A; Case map + 00B5; 03BC; Case map + 00C0; 00E0; Case map + 00C1; 00E1; Case map + 00C2; 00E2; Case map + 00C3; 00E3; Case map + 00C4; 00E4; Case map + 00C5; 00E5; Case map + 00C6; 00E6; Case map + 00C7; 00E7; Case map + 00C8; 00E8; Case map + 00C9; 00E9; Case map + 00CA; 00EA; Case map + 00CB; 00EB; Case map + 00CC; 00EC; Case map + 00CD; 00ED; Case map + 00CE; 00EE; Case map + 00CF; 00EF; Case map + 00D0; 00F0; Case map + 00D1; 00F1; Case map + 00D2; 00F2; Case map + 00D3; 00F3; Case map + 00D4; 00F4; Case map + 00D5; 00F5; Case map + 00D6; 00F6; Case map + 00D8; 00F8; Case map + 00D9; 00F9; Case map + 00DA; 00FA; Case map + 00DB; 00FB; Case map + 00DC; 00FC; Case map + 00DD; 00FD; Case map + 00DE; 00FE; Case map + 00DF; 0073 0073; Case map + 0100; 0101; Case map + 0102; 0103; Case map + 0104; 0105; Case map + 0106; 0107; Case map + 0108; 0109; Case map + 010A; 010B; Case map + 010C; 010D; Case map + 010E; 010F; Case map + 0110; 0111; Case map + 0112; 0113; Case map + 0114; 0115; Case map + 0116; 0117; Case map + 0118; 0119; Case map + 011A; 011B; Case map + 011C; 011D; Case map + 011E; 011F; Case map + 0120; 0121; Case map + 0122; 0123; Case map + 0124; 0125; Case map + 0126; 0127; Case map + 0128; 0129; Case map + 012A; 012B; Case map + 012C; 012D; Case map + 012E; 012F; Case map + 0130; 0069 0307; Case map + 0132; 0133; Case map + 0134; 0135; Case map + 0136; 0137; Case map + 0139; 013A; Case map + 013B; 013C; Case map + 013D; 013E; Case map + 013F; 0140; Case map + 0141; 0142; Case map + 0143; 0144; Case map + 0145; 0146; Case map + 0147; 0148; Case map + 0149; 02BC 006E; Case map + 014A; 014B; Case map + 014C; 014D; Case map + 014E; 014F; Case map + 0150; 0151; Case map + 0152; 0153; Case map + 0154; 0155; Case map + 0156; 0157; Case map + 0158; 0159; Case map + 015A; 015B; Case map + 015C; 015D; Case map + 015E; 015F; Case map + 0160; 0161; Case map + 0162; 0163; Case map + 0164; 0165; Case map + 0166; 0167; Case map + 0168; 0169; Case map + 016A; 016B; Case map + 016C; 016D; Case map + 016E; 016F; Case map + 0170; 0171; Case map + 0172; 0173; Case map + 0174; 0175; Case map + 0176; 0177; Case map + 0178; 00FF; Case map + 0179; 017A; Case map + 017B; 017C; Case map + 017D; 017E; Case map + 017F; 0073; Case map + 0181; 0253; Case map + 0182; 0183; Case map + 0184; 0185; Case map + 0186; 0254; Case map + 0187; 0188; Case map + 0189; 0256; Case map + 018A; 0257; Case map + 018B; 018C; Case map + 018E; 01DD; Case map + 018F; 0259; Case map + 0190; 025B; Case map + 0191; 0192; Case map + 0193; 0260; Case map + 0194; 0263; Case map + 0196; 0269; Case map + 0197; 0268; Case map + 0198; 0199; Case map + 019C; 026F; Case map + 019D; 0272; Case map + 019F; 0275; Case map + 01A0; 01A1; Case map + 01A2; 01A3; Case map + 01A4; 01A5; Case map + 01A6; 0280; Case map + 01A7; 01A8; Case map + 01A9; 0283; Case map + 01AC; 01AD; Case map + 01AE; 0288; Case map + 01AF; 01B0; Case map + 01B1; 028A; Case map + 01B2; 028B; Case map + 01B3; 01B4; Case map + 01B5; 01B6; Case map + 01B7; 0292; Case map + 01B8; 01B9; Case map + 01BC; 01BD; Case map + 01C4; 01C6; Case map + 01C5; 01C6; Case map + 01C7; 01C9; Case map + 01C8; 01C9; Case map + 01CA; 01CC; Case map + 01CB; 01CC; Case map + 01CD; 01CE; Case map + 01CF; 01D0; Case map + 01D1; 01D2; Case map + 01D3; 01D4; Case map + 01D5; 01D6; Case map + 01D7; 01D8; Case map + 01D9; 01DA; Case map + 01DB; 01DC; Case map + 01DE; 01DF; Case map + 01E0; 01E1; Case map + 01E2; 01E3; Case map + 01E4; 01E5; Case map + 01E6; 01E7; Case map + 01E8; 01E9; Case map + 01EA; 01EB; Case map + 01EC; 01ED; Case map + 01EE; 01EF; Case map + 01F0; 006A 030C; Case map + 01F1; 01F3; Case map + 01F2; 01F3; Case map + 01F4; 01F5; Case map + 01F6; 0195; Case map + 01F7; 01BF; Case map + 01F8; 01F9; Case map + 01FA; 01FB; Case map + 01FC; 01FD; Case map + 01FE; 01FF; Case map + 0200; 0201; Case map + 0202; 0203; Case map + 0204; 0205; Case map + 0206; 0207; Case map + 0208; 0209; Case map + 020A; 020B; Case map + 020C; 020D; Case map + 020E; 020F; Case map + 0210; 0211; Case map + 0212; 0213; Case map + 0214; 0215; Case map + 0216; 0217; Case map + 0218; 0219; Case map + 021A; 021B; Case map + 021C; 021D; Case map + 021E; 021F; Case map + 0220; 019E; Case map + 0222; 0223; Case map + 0224; 0225; Case map + 0226; 0227; Case map + 0228; 0229; Case map + 022A; 022B; Case map + 022C; 022D; Case map + 022E; 022F; Case map + 0230; 0231; Case map + 0232; 0233; Case map + 0345; 03B9; Case map + 0386; 03AC; Case map + 0388; 03AD; Case map + 0389; 03AE; Case map + 038A; 03AF; Case map + 038C; 03CC; Case map + 038E; 03CD; Case map + 038F; 03CE; Case map + 0390; 03B9 0308 0301; Case map + 0391; 03B1; Case map + 0392; 03B2; Case map + 0393; 03B3; Case map + 0394; 03B4; Case map + 0395; 03B5; Case map + 0396; 03B6; Case map + 0397; 03B7; Case map + 0398; 03B8; Case map + 0399; 03B9; Case map + 039A; 03BA; Case map + 039B; 03BB; Case map + 039C; 03BC; Case map + 039D; 03BD; Case map + 039E; 03BE; Case map + 039F; 03BF; Case map + 03A0; 03C0; Case map + 03A1; 03C1; Case map + 03A3; 03C3; Case map + 03A4; 03C4; Case map + 03A5; 03C5; Case map + 03A6; 03C6; Case map + 03A7; 03C7; Case map + 03A8; 03C8; Case map + 03A9; 03C9; Case map + 03AA; 03CA; Case map + 03AB; 03CB; Case map + 03B0; 03C5 0308 0301; Case map + 03C2; 03C3; Case map + 03D0; 03B2; Case map + 03D1; 03B8; Case map + 03D5; 03C6; Case map + 03D6; 03C0; Case map + 03D8; 03D9; Case map + 03DA; 03DB; Case map + 03DC; 03DD; Case map + 03DE; 03DF; Case map + 03E0; 03E1; Case map + 03E2; 03E3; Case map + 03E4; 03E5; Case map + 03E6; 03E7; Case map + 03E8; 03E9; Case map + 03EA; 03EB; Case map + 03EC; 03ED; Case map + 03EE; 03EF; Case map + 03F0; 03BA; Case map + 03F1; 03C1; Case map + 03F2; 03C3; Case map + 03F4; 03B8; Case map + 03F5; 03B5; Case map + 0400; 0450; Case map + 0401; 0451; Case map + 0402; 0452; Case map + 0403; 0453; Case map + 0404; 0454; Case map + 0405; 0455; Case map + 0406; 0456; Case map + 0407; 0457; Case map + 0408; 0458; Case map + 0409; 0459; Case map + 040A; 045A; Case map + 040B; 045B; Case map + 040C; 045C; Case map + 040D; 045D; Case map + 040E; 045E; Case map + 040F; 045F; Case map + 0410; 0430; Case map + 0411; 0431; Case map + 0412; 0432; Case map + 0413; 0433; Case map + 0414; 0434; Case map + 0415; 0435; Case map + 0416; 0436; Case map + 0417; 0437; Case map + 0418; 0438; Case map + 0419; 0439; Case map + 041A; 043A; Case map + 041B; 043B; Case map + 041C; 043C; Case map + 041D; 043D; Case map + 041E; 043E; Case map + 041F; 043F; Case map + 0420; 0440; Case map + 0421; 0441; Case map + 0422; 0442; Case map + 0423; 0443; Case map + 0424; 0444; Case map + 0425; 0445; Case map + 0426; 0446; Case map + 0427; 0447; Case map + 0428; 0448; Case map + 0429; 0449; Case map + 042A; 044A; Case map + 042B; 044B; Case map + 042C; 044C; Case map + 042D; 044D; Case map + 042E; 044E; Case map + 042F; 044F; Case map + 0460; 0461; Case map + 0462; 0463; Case map + 0464; 0465; Case map + 0466; 0467; Case map + 0468; 0469; Case map + 046A; 046B; Case map + 046C; 046D; Case map + 046E; 046F; Case map + 0470; 0471; Case map + 0472; 0473; Case map + 0474; 0475; Case map + 0476; 0477; Case map + 0478; 0479; Case map + 047A; 047B; Case map + 047C; 047D; Case map + 047E; 047F; Case map + 0480; 0481; Case map + 048A; 048B; Case map + 048C; 048D; Case map + 048E; 048F; Case map + 0490; 0491; Case map + 0492; 0493; Case map + 0494; 0495; Case map + 0496; 0497; Case map + 0498; 0499; Case map + 049A; 049B; Case map + 049C; 049D; Case map + 049E; 049F; Case map + 04A0; 04A1; Case map + 04A2; 04A3; Case map + 04A4; 04A5; Case map + 04A6; 04A7; Case map + 04A8; 04A9; Case map + 04AA; 04AB; Case map + 04AC; 04AD; Case map + 04AE; 04AF; Case map + 04B0; 04B1; Case map + 04B2; 04B3; Case map + 04B4; 04B5; Case map + 04B6; 04B7; Case map + 04B8; 04B9; Case map + 04BA; 04BB; Case map + 04BC; 04BD; Case map + 04BE; 04BF; Case map + 04C1; 04C2; Case map + 04C3; 04C4; Case map + 04C5; 04C6; Case map + 04C7; 04C8; Case map + 04C9; 04CA; Case map + 04CB; 04CC; Case map + 04CD; 04CE; Case map + 04D0; 04D1; Case map + 04D2; 04D3; Case map + 04D4; 04D5; Case map + 04D6; 04D7; Case map + 04D8; 04D9; Case map + 04DA; 04DB; Case map + 04DC; 04DD; Case map + 04DE; 04DF; Case map + 04E0; 04E1; Case map + 04E2; 04E3; Case map + 04E4; 04E5; Case map + 04E6; 04E7; Case map + 04E8; 04E9; Case map + 04EA; 04EB; Case map + 04EC; 04ED; Case map + 04EE; 04EF; Case map + 04F0; 04F1; Case map + 04F2; 04F3; Case map + 04F4; 04F5; Case map + 04F8; 04F9; Case map + 0500; 0501; Case map + 0502; 0503; Case map + 0504; 0505; Case map + 0506; 0507; Case map + 0508; 0509; Case map + 050A; 050B; Case map + 050C; 050D; Case map + 050E; 050F; Case map + 0531; 0561; Case map + 0532; 0562; Case map + 0533; 0563; Case map + 0534; 0564; Case map + 0535; 0565; Case map + 0536; 0566; Case map + 0537; 0567; Case map + 0538; 0568; Case map + 0539; 0569; Case map + 053A; 056A; Case map + 053B; 056B; Case map + 053C; 056C; Case map + 053D; 056D; Case map + 053E; 056E; Case map + 053F; 056F; Case map + 0540; 0570; Case map + 0541; 0571; Case map + 0542; 0572; Case map + 0543; 0573; Case map + 0544; 0574; Case map + 0545; 0575; Case map + 0546; 0576; Case map + 0547; 0577; Case map + 0548; 0578; Case map + 0549; 0579; Case map + 054A; 057A; Case map + 054B; 057B; Case map + 054C; 057C; Case map + 054D; 057D; Case map + 054E; 057E; Case map + 054F; 057F; Case map + 0550; 0580; Case map + 0551; 0581; Case map + 0552; 0582; Case map + 0553; 0583; Case map + 0554; 0584; Case map + 0555; 0585; Case map + 0556; 0586; Case map + 0587; 0565 0582; Case map + 1E00; 1E01; Case map + 1E02; 1E03; Case map + 1E04; 1E05; Case map + 1E06; 1E07; Case map + 1E08; 1E09; Case map + 1E0A; 1E0B; Case map + 1E0C; 1E0D; Case map + 1E0E; 1E0F; Case map + 1E10; 1E11; Case map + 1E12; 1E13; Case map + 1E14; 1E15; Case map + 1E16; 1E17; Case map + 1E18; 1E19; Case map + 1E1A; 1E1B; Case map + 1E1C; 1E1D; Case map + 1E1E; 1E1F; Case map + 1E20; 1E21; Case map + 1E22; 1E23; Case map + 1E24; 1E25; Case map + 1E26; 1E27; Case map + 1E28; 1E29; Case map + 1E2A; 1E2B; Case map + 1E2C; 1E2D; Case map + 1E2E; 1E2F; Case map + 1E30; 1E31; Case map + 1E32; 1E33; Case map + 1E34; 1E35; Case map + 1E36; 1E37; Case map + 1E38; 1E39; Case map + 1E3A; 1E3B; Case map + 1E3C; 1E3D; Case map + 1E3E; 1E3F; Case map + 1E40; 1E41; Case map + 1E42; 1E43; Case map + 1E44; 1E45; Case map + 1E46; 1E47; Case map + 1E48; 1E49; Case map + 1E4A; 1E4B; Case map + 1E4C; 1E4D; Case map + 1E4E; 1E4F; Case map + 1E50; 1E51; Case map + 1E52; 1E53; Case map + 1E54; 1E55; Case map + 1E56; 1E57; Case map + 1E58; 1E59; Case map + 1E5A; 1E5B; Case map + 1E5C; 1E5D; Case map + 1E5E; 1E5F; Case map + 1E60; 1E61; Case map + 1E62; 1E63; Case map + 1E64; 1E65; Case map + 1E66; 1E67; Case map + 1E68; 1E69; Case map + 1E6A; 1E6B; Case map + 1E6C; 1E6D; Case map + 1E6E; 1E6F; Case map + 1E70; 1E71; Case map + 1E72; 1E73; Case map + 1E74; 1E75; Case map + 1E76; 1E77; Case map + 1E78; 1E79; Case map + 1E7A; 1E7B; Case map + 1E7C; 1E7D; Case map + 1E7E; 1E7F; Case map + 1E80; 1E81; Case map + 1E82; 1E83; Case map + 1E84; 1E85; Case map + 1E86; 1E87; Case map + 1E88; 1E89; Case map + 1E8A; 1E8B; Case map + 1E8C; 1E8D; Case map + 1E8E; 1E8F; Case map + 1E90; 1E91; Case map + 1E92; 1E93; Case map + 1E94; 1E95; Case map + 1E96; 0068 0331; Case map + 1E97; 0074 0308; Case map + 1E98; 0077 030A; Case map + 1E99; 0079 030A; Case map + 1E9A; 0061 02BE; Case map + 1E9B; 1E61; Case map + 1EA0; 1EA1; Case map + 1EA2; 1EA3; Case map + 1EA4; 1EA5; Case map + 1EA6; 1EA7; Case map + 1EA8; 1EA9; Case map + 1EAA; 1EAB; Case map + 1EAC; 1EAD; Case map + 1EAE; 1EAF; Case map + 1EB0; 1EB1; Case map + 1EB2; 1EB3; Case map + 1EB4; 1EB5; Case map + 1EB6; 1EB7; Case map + 1EB8; 1EB9; Case map + 1EBA; 1EBB; Case map + 1EBC; 1EBD; Case map + 1EBE; 1EBF; Case map + 1EC0; 1EC1; Case map + 1EC2; 1EC3; Case map + 1EC4; 1EC5; Case map + 1EC6; 1EC7; Case map + 1EC8; 1EC9; Case map + 1ECA; 1ECB; Case map + 1ECC; 1ECD; Case map + 1ECE; 1ECF; Case map + 1ED0; 1ED1; Case map + 1ED2; 1ED3; Case map + 1ED4; 1ED5; Case map + 1ED6; 1ED7; Case map + 1ED8; 1ED9; Case map + 1EDA; 1EDB; Case map + 1EDC; 1EDD; Case map + 1EDE; 1EDF; Case map + 1EE0; 1EE1; Case map + 1EE2; 1EE3; Case map + 1EE4; 1EE5; Case map + 1EE6; 1EE7; Case map + 1EE8; 1EE9; Case map + 1EEA; 1EEB; Case map + 1EEC; 1EED; Case map + 1EEE; 1EEF; Case map + 1EF0; 1EF1; Case map + 1EF2; 1EF3; Case map + 1EF4; 1EF5; Case map + 1EF6; 1EF7; Case map + 1EF8; 1EF9; Case map + 1F08; 1F00; Case map + 1F09; 1F01; Case map + 1F0A; 1F02; Case map + 1F0B; 1F03; Case map + 1F0C; 1F04; Case map + 1F0D; 1F05; Case map + 1F0E; 1F06; Case map + 1F0F; 1F07; Case map + 1F18; 1F10; Case map + 1F19; 1F11; Case map + 1F1A; 1F12; Case map + 1F1B; 1F13; Case map + 1F1C; 1F14; Case map + 1F1D; 1F15; Case map + 1F28; 1F20; Case map + 1F29; 1F21; Case map + 1F2A; 1F22; Case map + 1F2B; 1F23; Case map + 1F2C; 1F24; Case map + 1F2D; 1F25; Case map + 1F2E; 1F26; Case map + 1F2F; 1F27; Case map + 1F38; 1F30; Case map + 1F39; 1F31; Case map + 1F3A; 1F32; Case map + 1F3B; 1F33; Case map + 1F3C; 1F34; Case map + 1F3D; 1F35; Case map + 1F3E; 1F36; Case map + 1F3F; 1F37; Case map + 1F48; 1F40; Case map + 1F49; 1F41; Case map + 1F4A; 1F42; Case map + 1F4B; 1F43; Case map + 1F4C; 1F44; Case map + 1F4D; 1F45; Case map + 1F50; 03C5 0313; Case map + 1F52; 03C5 0313 0300; Case map + 1F54; 03C5 0313 0301; Case map + 1F56; 03C5 0313 0342; Case map + 1F59; 1F51; Case map + 1F5B; 1F53; Case map + 1F5D; 1F55; Case map + 1F5F; 1F57; Case map + 1F68; 1F60; Case map + 1F69; 1F61; Case map + 1F6A; 1F62; Case map + 1F6B; 1F63; Case map + 1F6C; 1F64; Case map + 1F6D; 1F65; Case map + 1F6E; 1F66; Case map + 1F6F; 1F67; Case map + 1F80; 1F00 03B9; Case map + 1F81; 1F01 03B9; Case map + 1F82; 1F02 03B9; Case map + 1F83; 1F03 03B9; Case map + 1F84; 1F04 03B9; Case map + 1F85; 1F05 03B9; Case map + 1F86; 1F06 03B9; Case map + 1F87; 1F07 03B9; Case map + 1F88; 1F00 03B9; Case map + 1F89; 1F01 03B9; Case map + 1F8A; 1F02 03B9; Case map + 1F8B; 1F03 03B9; Case map + 1F8C; 1F04 03B9; Case map + 1F8D; 1F05 03B9; Case map + 1F8E; 1F06 03B9; Case map + 1F8F; 1F07 03B9; Case map + 1F90; 1F20 03B9; Case map + 1F91; 1F21 03B9; Case map + 1F92; 1F22 03B9; Case map + 1F93; 1F23 03B9; Case map + 1F94; 1F24 03B9; Case map + 1F95; 1F25 03B9; Case map + 1F96; 1F26 03B9; Case map + 1F97; 1F27 03B9; Case map + 1F98; 1F20 03B9; Case map + 1F99; 1F21 03B9; Case map + 1F9A; 1F22 03B9; Case map + 1F9B; 1F23 03B9; Case map + 1F9C; 1F24 03B9; Case map + 1F9D; 1F25 03B9; Case map + 1F9E; 1F26 03B9; Case map + 1F9F; 1F27 03B9; Case map + 1FA0; 1F60 03B9; Case map + 1FA1; 1F61 03B9; Case map + 1FA2; 1F62 03B9; Case map + 1FA3; 1F63 03B9; Case map + 1FA4; 1F64 03B9; Case map + 1FA5; 1F65 03B9; Case map + 1FA6; 1F66 03B9; Case map + 1FA7; 1F67 03B9; Case map + 1FA8; 1F60 03B9; Case map + 1FA9; 1F61 03B9; Case map + 1FAA; 1F62 03B9; Case map + 1FAB; 1F63 03B9; Case map + 1FAC; 1F64 03B9; Case map + 1FAD; 1F65 03B9; Case map + 1FAE; 1F66 03B9; Case map + 1FAF; 1F67 03B9; Case map + 1FB2; 1F70 03B9; Case map + 1FB3; 03B1 03B9; Case map + 1FB4; 03AC 03B9; Case map + 1FB6; 03B1 0342; Case map + 1FB7; 03B1 0342 03B9; Case map + 1FB8; 1FB0; Case map + 1FB9; 1FB1; Case map + 1FBA; 1F70; Case map + 1FBB; 1F71; Case map + 1FBC; 03B1 03B9; Case map + 1FBE; 03B9; Case map + 1FC2; 1F74 03B9; Case map + 1FC3; 03B7 03B9; Case map + 1FC4; 03AE 03B9; Case map + 1FC6; 03B7 0342; Case map + 1FC7; 03B7 0342 03B9; Case map + 1FC8; 1F72; Case map + 1FC9; 1F73; Case map + 1FCA; 1F74; Case map + 1FCB; 1F75; Case map + 1FCC; 03B7 03B9; Case map + 1FD2; 03B9 0308 0300; Case map + 1FD3; 03B9 0308 0301; Case map + 1FD6; 03B9 0342; Case map + 1FD7; 03B9 0308 0342; Case map + 1FD8; 1FD0; Case map + 1FD9; 1FD1; Case map + 1FDA; 1F76; Case map + 1FDB; 1F77; Case map + 1FE2; 03C5 0308 0300; Case map + 1FE3; 03C5 0308 0301; Case map + 1FE4; 03C1 0313; Case map + 1FE6; 03C5 0342; Case map + 1FE7; 03C5 0308 0342; Case map + 1FE8; 1FE0; Case map + 1FE9; 1FE1; Case map + 1FEA; 1F7A; Case map + 1FEB; 1F7B; Case map + 1FEC; 1FE5; Case map + 1FF2; 1F7C 03B9; Case map + 1FF3; 03C9 03B9; Case map + 1FF4; 03CE 03B9; Case map + 1FF6; 03C9 0342; Case map + 1FF7; 03C9 0342 03B9; Case map + 1FF8; 1F78; Case map + 1FF9; 1F79; Case map + 1FFA; 1F7C; Case map + 1FFB; 1F7D; Case map + 1FFC; 03C9 03B9; Case map + 2126; 03C9; Case map + 212A; 006B; Case map + 212B; 00E5; Case map + 2160; 2170; Case map + 2161; 2171; Case map + 2162; 2172; Case map + 2163; 2173; Case map + 2164; 2174; Case map + 2165; 2175; Case map + 2166; 2176; Case map + 2167; 2177; Case map + 2168; 2178; Case map + 2169; 2179; Case map + 216A; 217A; Case map + 216B; 217B; Case map + 216C; 217C; Case map + 216D; 217D; Case map + 216E; 217E; Case map + 216F; 217F; Case map + 24B6; 24D0; Case map + 24B7; 24D1; Case map + 24B8; 24D2; Case map + 24B9; 24D3; Case map + 24BA; 24D4; Case map + 24BB; 24D5; Case map + 24BC; 24D6; Case map + 24BD; 24D7; Case map + 24BE; 24D8; Case map + 24BF; 24D9; Case map + 24C0; 24DA; Case map + 24C1; 24DB; Case map + 24C2; 24DC; Case map + 24C3; 24DD; Case map + 24C4; 24DE; Case map + 24C5; 24DF; Case map + 24C6; 24E0; Case map + 24C7; 24E1; Case map + 24C8; 24E2; Case map + 24C9; 24E3; Case map + 24CA; 24E4; Case map + 24CB; 24E5; Case map + 24CC; 24E6; Case map + 24CD; 24E7; Case map + 24CE; 24E8; Case map + 24CF; 24E9; Case map + FB00; 0066 0066; Case map + FB01; 0066 0069; Case map + FB02; 0066 006C; Case map + FB03; 0066 0066 0069; Case map + FB04; 0066 0066 006C; Case map + FB05; 0073 0074; Case map + FB06; 0073 0074; Case map + FB13; 0574 0576; Case map + FB14; 0574 0565; Case map + FB15; 0574 056B; Case map + FB16; 057E 0576; Case map + FB17; 0574 056D; Case map + FF21; FF41; Case map + FF22; FF42; Case map + FF23; FF43; Case map + FF24; FF44; Case map + FF25; FF45; Case map + FF26; FF46; Case map + FF27; FF47; Case map + FF28; FF48; Case map + FF29; FF49; Case map + FF2A; FF4A; Case map + FF2B; FF4B; Case map + FF2C; FF4C; Case map + FF2D; FF4D; Case map + FF2E; FF4E; Case map + FF2F; FF4F; Case map + FF30; FF50; Case map + FF31; FF51; Case map + FF32; FF52; Case map + FF33; FF53; Case map + FF34; FF54; Case map + FF35; FF55; Case map + FF36; FF56; Case map + FF37; FF57; Case map + FF38; FF58; Case map + FF39; FF59; Case map + FF3A; FF5A; Case map + 10400; 10428; Case map + 10401; 10429; Case map + 10402; 1042A; Case map + 10403; 1042B; Case map + 10404; 1042C; Case map + 10405; 1042D; Case map + 10406; 1042E; Case map + 10407; 1042F; Case map + 10408; 10430; Case map + 10409; 10431; Case map + 1040A; 10432; Case map + 1040B; 10433; Case map + 1040C; 10434; Case map + 1040D; 10435; Case map + 1040E; 10436; Case map + 1040F; 10437; Case map + 10410; 10438; Case map + 10411; 10439; Case map + 10412; 1043A; Case map + 10413; 1043B; Case map + 10414; 1043C; Case map + 10415; 1043D; Case map + 10416; 1043E; Case map + 10417; 1043F; Case map + 10418; 10440; Case map + 10419; 10441; Case map + 1041A; 10442; Case map + 1041B; 10443; Case map + 1041C; 10444; Case map + 1041D; 10445; Case map + 1041E; 10446; Case map + 1041F; 10447; Case map + 10420; 10448; Case map + 10421; 10449; Case map + 10422; 1044A; Case map + 10423; 1044B; Case map + 10424; 1044C; Case map + 10425; 1044D; Case map + ----- End Table B.3 ----- + + ----- Start Table C.1.1 ----- + 0020; SPACE + ----- End Table C.1.1 ----- + + ----- Start Table C.1.2 ----- + 00A0; NO-BREAK SPACE + 1680; OGHAM SPACE MARK + 2000; EN QUAD + 2001; EM QUAD + 2002; EN SPACE + 2003; EM SPACE + 2004; THREE-PER-EM SPACE + 2005; FOUR-PER-EM SPACE + 2006; SIX-PER-EM SPACE + 2007; FIGURE SPACE + 2008; PUNCTUATION SPACE + 2009; THIN SPACE + 200A; HAIR SPACE + 200B; ZERO WIDTH SPACE + 202F; NARROW NO-BREAK SPACE + 205F; MEDIUM MATHEMATICAL SPACE + 3000; IDEOGRAPHIC SPACE + ----- End Table C.1.2 ----- + + ----- Start Table C.2.1 ----- + 0000-001F; [CONTROL CHARACTERS] + 007F; DELETE + ----- End Table C.2.1 ----- + + ----- Start Table C.2.2 ----- + 0080-009F; [CONTROL CHARACTERS] + 06DD; ARABIC END OF AYAH + 070F; SYRIAC ABBREVIATION MARK + 180E; MONGOLIAN VOWEL SEPARATOR + 200C; ZERO WIDTH NON-JOINER + 200D; ZERO WIDTH JOINER + 2028; LINE SEPARATOR + 2029; PARAGRAPH SEPARATOR + 2060; WORD JOINER + 2061; FUNCTION APPLICATION + 2062; INVISIBLE TIMES + 2063; INVISIBLE SEPARATOR + 206A-206F; [CONTROL CHARACTERS] + FEFF; ZERO WIDTH NO-BREAK SPACE + FFF9-FFFC; [CONTROL CHARACTERS] + 1D173-1D17A; [MUSICAL CONTROL CHARACTERS] + ----- End Table C.2.2 ----- + + ----- Start Table C.3 ----- + E000-F8FF; [PRIVATE USE, PLANE 0] + F0000-FFFFD; [PRIVATE USE, PLANE 15] + 100000-10FFFD; [PRIVATE USE, PLANE 16] + ----- End Table C.3 ----- + + ----- Start Table C.4 ----- + FDD0-FDEF; [NONCHARACTER CODE POINTS] + FFFE-FFFF; [NONCHARACTER CODE POINTS] + 1FFFE-1FFFF; [NONCHARACTER CODE POINTS] + 2FFFE-2FFFF; [NONCHARACTER CODE POINTS] + 3FFFE-3FFFF; [NONCHARACTER CODE POINTS] + 4FFFE-4FFFF; [NONCHARACTER CODE POINTS] + 5FFFE-5FFFF; [NONCHARACTER CODE POINTS] + 6FFFE-6FFFF; [NONCHARACTER CODE POINTS] + 7FFFE-7FFFF; [NONCHARACTER CODE POINTS] + 8FFFE-8FFFF; [NONCHARACTER CODE POINTS] + 9FFFE-9FFFF; [NONCHARACTER CODE POINTS] + AFFFE-AFFFF; [NONCHARACTER CODE POINTS] + BFFFE-BFFFF; [NONCHARACTER CODE POINTS] + CFFFE-CFFFF; [NONCHARACTER CODE POINTS] + DFFFE-DFFFF; [NONCHARACTER CODE POINTS] + EFFFE-EFFFF; [NONCHARACTER CODE POINTS] + FFFFE-FFFFF; [NONCHARACTER CODE POINTS] + 10FFFE-10FFFF; [NONCHARACTER CODE POINTS] + ----- End Table C.4 ----- + + ----- Start Table C.5 ----- + D800-DFFF; [SURROGATE CODES] + ----- End Table C.5 ----- + + ----- Start Table C.6 ----- + FFF9; INTERLINEAR ANNOTATION ANCHOR + FFFA; INTERLINEAR ANNOTATION SEPARATOR + FFFB; INTERLINEAR ANNOTATION TERMINATOR + FFFC; OBJECT REPLACEMENT CHARACTER + FFFD; REPLACEMENT CHARACTER + ----- End Table C.6 ----- + + ----- Start Table C.7 ----- + 2FF0-2FFB; [IDEOGRAPHIC DESCRIPTION CHARACTERS] + ----- End Table C.7 ----- + + ----- Start Table C.8 ----- + 0340; COMBINING GRAVE TONE MARK + 0341; COMBINING ACUTE TONE MARK + 200E; LEFT-TO-RIGHT MARK + 200F; RIGHT-TO-LEFT MARK + 202A; LEFT-TO-RIGHT EMBEDDING + 202B; RIGHT-TO-LEFT EMBEDDING + 202C; POP DIRECTIONAL FORMATTING + 202D; LEFT-TO-RIGHT OVERRIDE + 202E; RIGHT-TO-LEFT OVERRIDE + 206A; INHIBIT SYMMETRIC SWAPPING + 206B; ACTIVATE SYMMETRIC SWAPPING + 206C; INHIBIT ARABIC FORM SHAPING + 206D; ACTIVATE ARABIC FORM SHAPING + 206E; NATIONAL DIGIT SHAPES + 206F; NOMINAL DIGIT SHAPES + ----- End Table C.8 ----- + + ----- Start Table C.9 ----- + E0001; LANGUAGE TAG + E0020-E007F; [TAGGING CHARACTERS] + ----- End Table C.9 ----- + + ----- Start Table D.1 ----- + 05BE + 05C0 + 05C3 + 05D0-05EA + 05F0-05F4 + 061B + 061F + 0621-063A + 0640-064A + 066D-066F + 0671-06D5 + 06DD + 06E5-06E6 + 06FA-06FE + 0700-070D + 0710 + 0712-072C + 0780-07A5 + 07B1 + 200F + FB1D + FB1F-FB28 + FB2A-FB36 + FB38-FB3C + FB3E + FB40-FB41 + FB43-FB44 + FB46-FBB1 + FBD3-FD3D + FD50-FD8F + FD92-FDC7 + FDF0-FDFC + FE70-FE74 + FE76-FEFC + ----- End Table D.1 ----- + + ----- Start Table D.2 ----- + 0041-005A + 0061-007A + 00AA + 00B5 + 00BA + 00C0-00D6 + 00D8-00F6 + 00F8-0220 + 0222-0233 + 0250-02AD + 02B0-02B8 + 02BB-02C1 + 02D0-02D1 + 02E0-02E4 + 02EE + 037A + 0386 + 0388-038A + 038C + 038E-03A1 + 03A3-03CE + 03D0-03F5 + 0400-0482 + 048A-04CE + 04D0-04F5 + 04F8-04F9 + 0500-050F + 0531-0556 + 0559-055F + 0561-0587 + 0589 + 0903 + 0905-0939 + 093D-0940 + 0949-094C + 0950 + 0958-0961 + 0964-0970 + 0982-0983 + 0985-098C + 098F-0990 + 0993-09A8 + 09AA-09B0 + 09B2 + 09B6-09B9 + 09BE-09C0 + 09C7-09C8 + 09CB-09CC + 09D7 + 09DC-09DD + 09DF-09E1 + 09E6-09F1 + 09F4-09FA + 0A05-0A0A + 0A0F-0A10 + 0A13-0A28 + 0A2A-0A30 + 0A32-0A33 + 0A35-0A36 + 0A38-0A39 + 0A3E-0A40 + 0A59-0A5C + 0A5E + 0A66-0A6F + 0A72-0A74 + 0A83 + 0A85-0A8B + 0A8D + 0A8F-0A91 + 0A93-0AA8 + 0AAA-0AB0 + 0AB2-0AB3 + 0AB5-0AB9 + 0ABD-0AC0 + 0AC9 + 0ACB-0ACC + 0AD0 + 0AE0 + 0AE6-0AEF + 0B02-0B03 + 0B05-0B0C + 0B0F-0B10 + 0B13-0B28 + 0B2A-0B30 + 0B32-0B33 + 0B36-0B39 + 0B3D-0B3E + 0B40 + 0B47-0B48 + 0B4B-0B4C + 0B57 + 0B5C-0B5D + 0B5F-0B61 + 0B66-0B70 + 0B83 + 0B85-0B8A + 0B8E-0B90 + 0B92-0B95 + 0B99-0B9A + 0B9C + 0B9E-0B9F + 0BA3-0BA4 + 0BA8-0BAA + 0BAE-0BB5 + 0BB7-0BB9 + 0BBE-0BBF + 0BC1-0BC2 + 0BC6-0BC8 + 0BCA-0BCC + 0BD7 + 0BE7-0BF2 + 0C01-0C03 + 0C05-0C0C + 0C0E-0C10 + 0C12-0C28 + 0C2A-0C33 + 0C35-0C39 + 0C41-0C44 + 0C60-0C61 + 0C66-0C6F + 0C82-0C83 + 0C85-0C8C + 0C8E-0C90 + 0C92-0CA8 + 0CAA-0CB3 + 0CB5-0CB9 + 0CBE + 0CC0-0CC4 + 0CC7-0CC8 + 0CCA-0CCB + 0CD5-0CD6 + 0CDE + 0CE0-0CE1 + 0CE6-0CEF + 0D02-0D03 + 0D05-0D0C + 0D0E-0D10 + 0D12-0D28 + 0D2A-0D39 + 0D3E-0D40 + 0D46-0D48 + 0D4A-0D4C + 0D57 + 0D60-0D61 + 0D66-0D6F + 0D82-0D83 + 0D85-0D96 + 0D9A-0DB1 + 0DB3-0DBB + 0DBD + 0DC0-0DC6 + 0DCF-0DD1 + 0DD8-0DDF + 0DF2-0DF4 + 0E01-0E30 + 0E32-0E33 + 0E40-0E46 + 0E4F-0E5B + 0E81-0E82 + 0E84 + 0E87-0E88 + 0E8A + 0E8D + 0E94-0E97 + 0E99-0E9F + 0EA1-0EA3 + 0EA5 + 0EA7 + 0EAA-0EAB + 0EAD-0EB0 + 0EB2-0EB3 + 0EBD + 0EC0-0EC4 + 0EC6 + 0ED0-0ED9 + 0EDC-0EDD + 0F00-0F17 + 0F1A-0F34 + 0F36 + 0F38 + 0F3E-0F47 + 0F49-0F6A + 0F7F + 0F85 + 0F88-0F8B + 0FBE-0FC5 + 0FC7-0FCC + 0FCF + 1000-1021 + 1023-1027 + 1029-102A + 102C + 1031 + 1038 + 1040-1057 + 10A0-10C5 + 10D0-10F8 + 10FB + 1100-1159 + 115F-11A2 + 11A8-11F9 + 1200-1206 + 1208-1246 + 1248 + 124A-124D + 1250-1256 + 1258 + 125A-125D + 1260-1286 + 1288 + 128A-128D + 1290-12AE + 12B0 + 12B2-12B5 + 12B8-12BE + 12C0 + 12C2-12C5 + 12C8-12CE + 12D0-12D6 + 12D8-12EE + 12F0-130E + 1310 + 1312-1315 + 1318-131E + 1320-1346 + 1348-135A + 1361-137C + 13A0-13F4 + 1401-1676 + 1681-169A + 16A0-16F0 + 1700-170C + 170E-1711 + 1720-1731 + 1735-1736 + 1740-1751 + 1760-176C + 176E-1770 + 1780-17B6 + 17BE-17C5 + 17C7-17C8 + 17D4-17DA + 17DC + 17E0-17E9 + 1810-1819 + 1820-1877 + 1880-18A8 + 1E00-1E9B + 1EA0-1EF9 + 1F00-1F15 + 1F18-1F1D + 1F20-1F45 + 1F48-1F4D + 1F50-1F57 + 1F59 + 1F5B + 1F5D + 1F5F-1F7D + 1F80-1FB4 + 1FB6-1FBC + 1FBE + 1FC2-1FC4 + 1FC6-1FCC + 1FD0-1FD3 + 1FD6-1FDB + 1FE0-1FEC + 1FF2-1FF4 + 1FF6-1FFC + 200E + 2071 + 207F + 2102 + 2107 + 210A-2113 + 2115 + 2119-211D + 2124 + 2126 + 2128 + 212A-212D + 212F-2131 + 2133-2139 + 213D-213F + 2145-2149 + 2160-2183 + 2336-237A + 2395 + 249C-24E9 + 3005-3007 + 3021-3029 + 3031-3035 + 3038-303C + 3041-3096 + 309D-309F + 30A1-30FA + 30FC-30FF + 3105-312C + 3131-318E + 3190-31B7 + 31F0-321C + 3220-3243 + 3260-327B + 327F-32B0 + 32C0-32CB + 32D0-32FE + 3300-3376 + 337B-33DD + 33E0-33FE + 3400-4DB5 + 4E00-9FA5 + A000-A48C + AC00-D7A3 + D800-FA2D + FA30-FA6A + FB00-FB06 + FB13-FB17 + FF21-FF3A + FF41-FF5A + FF66-FFBE + FFC2-FFC7 + FFCA-FFCF + FFD2-FFD7 + FFDA-FFDC + 10300-1031E + 10320-10323 + 10330-1034A + 10400-10425 + 10428-1044D + 1D000-1D0F5 + 1D100-1D126 + 1D12A-1D166 + 1D16A-1D172 + 1D183-1D184 + 1D18C-1D1A9 + 1D1AE-1D1DD + 1D400-1D454 + 1D456-1D49C + 1D49E-1D49F + 1D4A2 + 1D4A5-1D4A6 + 1D4A9-1D4AC + 1D4AE-1D4B9 + 1D4BB + 1D4BD-1D4C0 + 1D4C2-1D4C3 + 1D4C5-1D505 + 1D507-1D50A + 1D50D-1D514 + 1D516-1D51C + 1D51E-1D539 + 1D53B-1D53E + 1D540-1D544 + 1D546 + 1D54A-1D550 + 1D552-1D6A3 + 1D6A8-1D7C9 + 20000-2A6D6 + 2F800-2FA1D + F0000-FFFFD + 100000-10FFFD + ----- End Table D.2 ----- diff --git a/3rdparty/stringprep/stringprep.cpp b/3rdparty/stringprep/stringprep.cpp new file mode 100644 index 00000000..d7f581ba --- /dev/null +++ b/3rdparty/stringprep/stringprep.cpp @@ -0,0 +1,518 @@ +/* stringprep.c --- Core stringprep implementation. + Copyright (C) 2002-2016 Simon Josefsson + + This file is part of GNU Libidn. + + GNU Libidn is free software: you can redistribute it and/or + modify it under the terms of either: + + * the GNU Lesser General Public License as published by the Free + Software Foundation; either version 3 of the License, or (at + your option) any later version. + + or + + * the GNU General Public License as published by the Free + Software Foundation; either version 2 of the License, or (at + your option) any later version. + + or both in parallel, as here. + + GNU Libidn is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received copies of the GNU General Public License and + the GNU Lesser General Public License along with this program. If + not, see . */ + +#include +#include + +#include +#include + +#include "qstringprep.h" + +static int _compare_table_element(const uint32_t *c, const Stringprep_table_element *e) +{ + if (*c < e->start) + return -1; + if (*c > e->end) + return 1; + return 0; +} + +static std::ptrdiff_t stringprep_find_character_in_table(uint32_t ucs4, const Stringprep_table_element *table, + size_t table_size) +{ + /* This is where typical uses of Libidn spends very close to all CPU + time and causes most cache misses. One could easily do a binary + search instead. Before rewriting this, I want hard evidence this + slowness is at all relevant in typical applications. (I don't + dispute optimization may improve matters significantly, I'm + mostly interested in having someone give real-world benchmark on + the impact of libidn.) + * + * Answer (Tim Rühsen rockdaboot@gmx.de): + * Testing the fuzz corpora just once via make check takes ~54 billion CPU cycles. + * That is almost 20s on my Intel i3 3.1GHz !!! + * That even makes fuzzing almost useless, eating up CPU cycles for nothing. + * + * The bsearch() approach takes ~3 billion CPU cycles. + * Almost a factor of 20 faster (but still pretty slow). + * There are still ~2 million calls to bsearch() which make ~30% of CPU time used. + * Most time is spent in _g_utf8_normalize_wc(). + + std::ptrdiff_t i; + + for (i = 0; table[i].start || table[i].end; i++) + if (ucs4 >= table[i].start && + ucs4 <= (table[i].end ? table[i].end : table[i].start)) + return i; + */ + + const Stringprep_table_element *p = static_cast( + bsearch(&ucs4, table, table_size, sizeof(Stringprep_table_element), + (int (*)(const void *, const void *))_compare_table_element)); + + return p ? (p - table) : -1; +} + +static std::ptrdiff_t stringprep_find_string_in_table(uint *ucs4, size_t len, size_t *tablepos, + const Stringprep_table_element *table, size_t table_size) +{ + size_t j; + std::ptrdiff_t pos; + + for (j = 0; j < len; j++) + if ((pos = stringprep_find_character_in_table(ucs4[j], table, table_size)) != -1) { + if (tablepos) + *tablepos = pos; + return j; + } + + return -1; +} + +static int stringprep_apply_table_to_string(QVector &ucs4, const Stringprep_table_element *table, + size_t table_size) +{ + std::ptrdiff_t pos; + size_t i, maplen; + uint32_t *src = ucs4.data(); /* points to unprocessed data */ + size_t srclen = ucs4.size(); /* length of unprocessed data */ + + while ((pos = stringprep_find_string_in_table(src, srclen, &i, table, table_size)) != -1) { + for (maplen = STRINGPREP_MAX_MAP_CHARS; maplen > 0 && table[i].map[maplen - 1] == 0; maplen--) + ; + + if (maplen > 1) { + ucs4.resize(ucs4.size() + int(maplen) - 1); + } + + memmove(src + pos + maplen, src + pos + 1, sizeof(uint32_t) * (srclen - pos - 1)); + memcpy(src + pos, table[i].map, sizeof(uint32_t) * maplen); + src += pos + maplen; + srclen -= pos + 1; + } + + return STRINGPREP_OK; +} + +#define INVERTED(x) ((x) & ((~0UL) >> 1)) +#define UNAPPLICAPLEFLAGS(flags, profileflags) \ + ((!INVERTED(profileflags) && !(profileflags & flags) && profileflags) \ + || (INVERTED(profileflags) && (profileflags & flags))) + +/** + * stringprep_4i: + * @ucs4: input/output array with string to prepare. + * @len: on input, length of input array with Unicode code points, + * on exit, length of output array with Unicode code points. + * @maxucs4len: maximum length of input/output array. + * @flags: a #Stringprep_profile_flags value, or 0. + * @profile: pointer to #Stringprep_profile to use. + * + * Prepare the input UCS-4 string according to the stringprep profile, + * and write back the result to the input string. + * + * The input is not required to be zero terminated (@ucs4[@len] = 0). + * The output will not be zero terminated unless @ucs4[@len] = 0. + * Instead, see stringprep_4zi() if your input is zero terminated or + * if you want the output to be. + * + * Since the stringprep operation can expand the string, @maxucs4len + * indicate how large the buffer holding the string is. This function + * will not read or write to code points outside that size. + * + * The @flags are one of #Stringprep_profile_flags values, or 0. + * + * The @profile contain the #Stringprep_profile instructions to + * perform. Your application can define new profiles, possibly + * re-using the generic stringprep tables that always will be part of + * the library, or use one of the currently supported profiles. + * + * Return value: Returns %STRINGPREP_OK iff successful, or an + * #Stringprep_rc error code. + **/ +int stringprep_4i(QString &input, Stringprep_profile_flags flags, const Stringprep_profile *profile) +{ + size_t i, j; + std::ptrdiff_t k; + QVector ucs4vector = input.toUcs4(); + // size_t ucs4len = ucs4vector.size(); + ucs4vector.reserve(ucs4vector.size() * 2); // to pass nfkc + // uint32_t *ucs4 = ucs4vector.data(); + int rc; + + for (i = 0; profile[i].operation; i++) { + switch (profile[i].operation) { + case STRINGPREP_NFKC: + if (UNAPPLICAPLEFLAGS(flags, profile[i].flags)) + break; + + if (flags & STRINGPREP_NO_NFKC && !profile[i].flags) + /* Profile requires NFKC, but callee asked for no NFKC. */ + return STRINGPREP_FLAG_ERROR; + + static_assert(sizeof(char32_t) == sizeof(uint)); + ucs4vector = QString::fromUcs4(reinterpret_cast(ucs4vector.data()), ucs4vector.length()) + .normalized(QString::NormalizationForm_KC) + .toUcs4(); + break; + + case STRINGPREP_PROHIBIT_TABLE: + k = stringprep_find_string_in_table(ucs4vector.data(), ucs4vector.length(), NULL, profile[i].table, + profile[i].table_size); + if (k != -1) + return STRINGPREP_CONTAINS_PROHIBITED; + break; + + case STRINGPREP_UNASSIGNED_TABLE: + if (UNAPPLICAPLEFLAGS(flags, profile[i].flags)) + break; + if (flags & STRINGPREP_NO_UNASSIGNED) { + k = stringprep_find_string_in_table(ucs4vector.data(), ucs4vector.length(), NULL, profile[i].table, + profile[i].table_size); + if (k != -1) + return STRINGPREP_CONTAINS_UNASSIGNED; + } + break; + + case STRINGPREP_MAP_TABLE: + if (UNAPPLICAPLEFLAGS(flags, profile[i].flags)) + break; + rc = stringprep_apply_table_to_string(ucs4vector, profile[i].table, profile[i].table_size); + if (rc != STRINGPREP_OK) + return rc; + break; + + case STRINGPREP_BIDI_PROHIBIT_TABLE: + case STRINGPREP_BIDI_RAL_TABLE: + case STRINGPREP_BIDI_L_TABLE: + break; + + case STRINGPREP_BIDI: { + int done_prohibited = 0; + int done_ral = 0; + int done_l = 0; + size_t contains_ral = SIZE_MAX; + size_t contains_l = SIZE_MAX; + + for (j = 0; profile[j].operation; j++) + if (profile[j].operation == STRINGPREP_BIDI_PROHIBIT_TABLE) { + done_prohibited = 1; + k = stringprep_find_string_in_table(ucs4vector.data(), ucs4vector.length(), NULL, profile[j].table, + profile[j].table_size); + if (k != -1) + return STRINGPREP_BIDI_CONTAINS_PROHIBITED; + } else if (profile[j].operation == STRINGPREP_BIDI_RAL_TABLE) { + done_ral = 1; + if (stringprep_find_string_in_table(ucs4vector.data(), ucs4vector.length(), NULL, profile[j].table, + profile[j].table_size) + != -1) + contains_ral = j; + } else if (profile[j].operation == STRINGPREP_BIDI_L_TABLE) { + done_l = 1; + if (stringprep_find_string_in_table(ucs4vector.data(), ucs4vector.length(), NULL, profile[j].table, + profile[j].table_size) + != -1) + contains_l = j; + } + + if (!done_prohibited || !done_ral || !done_l) + return STRINGPREP_PROFILE_ERROR; + + if (contains_ral != SIZE_MAX && contains_l != SIZE_MAX) + return STRINGPREP_BIDI_BOTH_L_AND_RAL; + + if (contains_ral != SIZE_MAX) { + if (!(stringprep_find_character_in_table(ucs4vector.first(), profile[contains_ral].table, + profile[contains_ral].table_size) + != -1 + && stringprep_find_character_in_table(ucs4vector.last(), profile[contains_ral].table, + profile[contains_ral].table_size) + != -1)) + return STRINGPREP_BIDI_LEADTRAIL_NOT_RAL; + } + } break; + + default: + return STRINGPREP_PROFILE_ERROR; + break; + } + } + + static_assert(sizeof(char32_t) == sizeof(uint)); + input = QString::fromUcs4(reinterpret_cast(ucs4vector.data()), ucs4vector.size()); + + return STRINGPREP_OK; +} + +/** + * stringprep: + * @in: input/ouput array with string to prepare. + * @maxlen: maximum length of input/output array. + * @flags: a #Stringprep_profile_flags value, or 0. + * @profile: pointer to #Stringprep_profile to use. + * + * Prepare the input zero terminated UTF-8 string according to the + * stringprep profile, and write back the result to the input string. + * + * Note that you must convert strings entered in the systems locale + * into UTF-8 before using this function, see + * stringprep_locale_to_utf8(). + * + * Since the stringprep operation can expand the string, @maxlen + * indicate how large the buffer holding the string is. This function + * will not read or write to characters outside that size. + * + * The @flags are one of #Stringprep_profile_flags values, or 0. + * + * The @profile contain the #Stringprep_profile instructions to + * perform. Your application can define new profiles, possibly + * re-using the generic stringprep tables that always will be part of + * the library, or use one of the currently supported profiles. + * + * Return value: Returns %STRINGPREP_OK iff successful, or an error code. + **/ +int stringprep(QString &input, Stringprep_profile_flags flags, const Stringprep_profile *profile) +{ + int rc = stringprep_4i(input, flags, profile); + if (rc != STRINGPREP_OK) { + return rc; + } + + return STRINGPREP_OK; +} + +/*! \mainpage GNU Internationalized Domain Name Library + * + * \section intro Introduction + * + * GNU Libidn is an implementation of the Stringprep, Punycode and IDNA + * specifications defined by the IETF Internationalized Domain Names + * (IDN) working group, used for internationalized domain names. The + * package is available under the GNU Lesser General Public License. + * + * The library contains a generic Stringprep implementation that does + * Unicode 3.2 NFKC normalization, mapping and prohibitation of + * characters, and bidirectional character handling. Profiles for + * Nameprep, iSCSI, SASL and XMPP are included. Punycode and ASCII + * Compatible Encoding (ACE) via IDNA are supported. A mechanism to + * define Top-Level Domain (TLD) specific validation tables, and to + * compare strings against those tables, is included. Default tables + * for some TLDs are also included. + * + * The Stringprep API consists of two main functions, one for + * converting data from the system's native representation into UTF-8, + * and one function to perform the Stringprep processing. Adding a + * new Stringprep profile for your application within the API is + * straightforward. The Punycode API consists of one encoding + * function and one decoding function. The IDNA API consists of the + * ToASCII and ToUnicode functions, as well as an high-level interface + * for converting entire domain names to and from the ACE encoded + * form. The TLD API consists of one set of functions to extract the + * TLD name from a domain string, one set of functions to locate the + * proper TLD table to use based on the TLD name, and core functions + * to validate a string against a TLD table, and some utility wrappers + * to perform all the steps in one call. + * + * The library is used by, e.g., GNU SASL and Shishi to process user + * names and passwords. Libidn can be built into GNU Libc to enable a + * new system-wide getaddrinfo() flag for IDN processing. + * + * Libidn is developed for the GNU/Linux system, but runs on over 20 Unix + * platforms (including Solaris, IRIX, AIX, and Tru64) and Windows. + * Libidn is written in C and (parts of) the API is accessible from C, + * C++, Emacs Lisp, Python and Java. + * + * The project web page:\n + * http://www.gnu.org/software/libidn/ + * + * The software archive:\n + * ftp://alpha.gnu.org/pub/gnu/libidn/ + * + * For more information see:\n + * http://www.ietf.org/html.charters/idn-charter.html\n + * http://www.ietf.org/rfc/rfc3454.txt (stringprep specification)\n + * http://www.ietf.org/rfc/rfc3490.txt (idna specification)\n + * http://www.ietf.org/rfc/rfc3491.txt (nameprep specification)\n + * http://www.ietf.org/rfc/rfc3492.txt (punycode specification)\n + * http://www.ietf.org/internet-drafts/draft-ietf-ips-iscsi-string-prep-04.txt\n + * http://www.ietf.org/internet-drafts/draft-ietf-krb-wg-utf8-profile-01.txt\n + * http://www.ietf.org/internet-drafts/draft-ietf-sasl-anon-00.txt\n + * http://www.ietf.org/internet-drafts/draft-ietf-sasl-saslprep-00.txt\n + * http://www.ietf.org/internet-drafts/draft-ietf-xmpp-nodeprep-01.txt\n + * http://www.ietf.org/internet-drafts/draft-ietf-xmpp-resourceprep-01.txt\n + * + * Further information and paid contract development:\n + * Simon Josefsson + * + * \section examples Examples + * + * \include example.c + * \include example3.c + * \include example4.c + * \include example5.c + */ + +/** + * STRINGPREP_VERSION + * + * String defined via CPP denoting the header file version number. + * Used together with stringprep_check_version() to verify header file + * and run-time library consistency. + */ + +/** + * STRINGPREP_MAX_MAP_CHARS + * + * Maximum number of code points that can replace a single code point, + * during stringprep mapping. + */ + +/** + * Stringprep_rc: + * @STRINGPREP_OK: Successful operation. This value is guaranteed to + * always be zero, the remaining ones are only guaranteed to hold + * non-zero values, for logical comparison purposes. + * @STRINGPREP_CONTAINS_UNASSIGNED: String contain unassigned Unicode + * code points, which is forbidden by the profile. + * @STRINGPREP_CONTAINS_PROHIBITED: String contain code points + * prohibited by the profile. + * @STRINGPREP_BIDI_BOTH_L_AND_RAL: String contain code points with + * conflicting bidirection category. + * @STRINGPREP_BIDI_LEADTRAIL_NOT_RAL: Leading and trailing character + * in string not of proper bidirectional category. + * @STRINGPREP_BIDI_CONTAINS_PROHIBITED: Contains prohibited code + * points detected by bidirectional code. + * @STRINGPREP_TOO_SMALL_BUFFER: Buffer handed to function was too + * small. This usually indicate a problem in the calling + * application. + * @STRINGPREP_PROFILE_ERROR: The stringprep profile was inconsistent. + * This usually indicate an internal error in the library. + * @STRINGPREP_FLAG_ERROR: The supplied flag conflicted with profile. + * This usually indicate a problem in the calling application. + * @STRINGPREP_UNKNOWN_PROFILE: The supplied profile name was not + * known to the library. + * @STRINGPREP_ICONV_ERROR: Could not convert string in locale encoding. + * @STRINGPREP_NFKC_FAILED: The Unicode NFKC operation failed. This + * usually indicate an internal error in the library. + * @STRINGPREP_MALLOC_ERROR: The malloc() was out of memory. This is + * usually a fatal error. + * + * Enumerated return codes of stringprep(), stringprep_profile() + * functions (and macros using those functions). The value 0 is + * guaranteed to always correspond to success. + */ + +/** + * Stringprep_profile_flags: + * @STRINGPREP_NO_NFKC: Disable the NFKC normalization, as well as + * selecting the non-NFKC case folding tables. Usually the profile + * specifies BIDI and NFKC settings, and applications should not + * override it unless in special situations. + * @STRINGPREP_NO_BIDI: Disable the BIDI step. Usually the profile + * specifies BIDI and NFKC settings, and applications should not + * override it unless in special situations. + * @STRINGPREP_NO_UNASSIGNED: Make the library return with an error if + * string contains unassigned characters according to profile. + * + * Stringprep profile flags. + */ + +/** + * Stringprep_profile_steps: + * @STRINGPREP_NFKC: The NFKC step. + * @STRINGPREP_BIDI: The BIDI step. + * @STRINGPREP_MAP_TABLE: The MAP step. + * @STRINGPREP_UNASSIGNED_TABLE: The Unassigned step. + * @STRINGPREP_PROHIBIT_TABLE: The Prohibited step. + * @STRINGPREP_BIDI_PROHIBIT_TABLE: The BIDI-Prohibited step. + * @STRINGPREP_BIDI_RAL_TABLE: The BIDI-RAL step. + * @STRINGPREP_BIDI_L_TABLE: The BIDI-L step. + * + * Various steps in the stringprep algorithm. You really want to + * study the source code to understand this one. Only useful if you + * want to add another profile. + */ + +/** + * stringprep_nameprep: + * @in: input/ouput array with string to prepare. + * @maxlen: maximum length of input/output array. + * + * Prepare the input UTF-8 string according to the nameprep profile. + * The AllowUnassigned flag is true, use + * stringprep_nameprep_no_unassigned() if you want a false + * AllowUnassigned. Returns 0 iff successful, or an error code. + **/ + +/** + * stringprep_nameprep_no_unassigned: + * @in: input/ouput array with string to prepare. + * @maxlen: maximum length of input/output array. + * + * Prepare the input UTF-8 string according to the nameprep profile. + * The AllowUnassigned flag is false, use stringprep_nameprep() for + * true AllowUnassigned. Returns 0 iff successful, or an error code. + **/ + +/** + * stringprep_iscsi: + * @in: input/ouput array with string to prepare. + * @maxlen: maximum length of input/output array. + * + * Prepare the input UTF-8 string according to the draft iSCSI + * stringprep profile. Returns 0 iff successful, or an error code. + **/ + +/** + * stringprep_plain: + * @in: input/ouput array with string to prepare. + * @maxlen: maximum length of input/output array. + * + * Prepare the input UTF-8 string according to the draft SASL + * ANONYMOUS profile. Returns 0 iff successful, or an error code. + **/ + +/** + * stringprep_xmpp_nodeprep: + * @in: input/ouput array with string to prepare. + * @maxlen: maximum length of input/output array. + * + * Prepare the input UTF-8 string according to the draft XMPP node + * identifier profile. Returns 0 iff successful, or an error code. + **/ + +/** + * stringprep_xmpp_resourceprep: + * @in: input/ouput array with string to prepare. + * @maxlen: maximum length of input/output array. + * + * Prepare the input UTF-8 string according to the draft XMPP resource + * identifier profile. Returns 0 iff successful, or an error code. + **/ diff --git a/CMakeLists.txt b/CMakeLists.txt index f716cf4c..1aab0947 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,125 +1,162 @@ +cmake_minimum_required(VERSION 3.10.0) + +project(iris + DESCRIPTION "XMPP network library" + LANGUAGES C CXX +) + if(NOT CMAKE_BUILD_TYPE) set(CMAKE_BUILD_TYPE "Release") endif() -project(iris) -cmake_minimum_required(VERSION 3.1.0) - -if(POLICY CMP0071) - cmake_policy(SET CMP0071 OLD) -endif() -if(POLICY CMP0074) - cmake_policy(SET CMP0074 NEW) +# Set 'd' prefix for Windows OS debug builds +if(CMAKE_BUILD_TYPE STREQUAL "Debug") + if(WIN32) + set(D "d") + elseif(APPLE) + set(D "_debug") + endif() endif() -set( IRIS_LIB_VERSION_MAJOR 1 ) -set( IRIS_LIB_VERSION_MINOR 0 ) -set( IRIS_LIB_VERSION_PATCH 0 ) -set( IRIS_LIB_VERSION_STRING +# Check if Iris is a subproject +get_directory_property(IS_SUBPROJECT PARENT_DIRECTORY) + +set(IRIS_LIB_VERSION_MAJOR 1) +set(IRIS_LIB_VERSION_MINOR 0) +set(IRIS_LIB_VERSION_PATCH 0) +set(IRIS_LIB_VERSION_STRING ${IRIS_LIB_VERSION_MAJOR}.${IRIS_LIB_VERSION_MINOR}.${IRIS_LIB_VERSION_PATCH} - ) +) -set( CMAKE_MODULE_PATH - "${CMAKE_MODULE_PATH}" +list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules" - "${CMAKE_CURRENT_SOURCE_DIR}/../cmake/modules" - "${PROJECT_SOURCE_DIR}/cmake/modules" - "${PROJECT_SOURCE_DIR}/../cmake/modules" - ) + "${CMAKE_SOURCE_DIR}/cmake/modules" +) -option( USE_QJDNS "Use qjdns/jdns library. Disabled by default for Qt5" OFF ) -option( SEPARATE_QJDNS "Build qjdns with iris library" OFF ) +include(policyRules) -set(CMAKE_CXX_STANDARD 14) +set(CMAKE_AUTOMOC ON) +set(CMAKE_INCLUDE_CURRENT_DIR ON) +set(CMAKE_CXX_STANDARD 11) + +# Detect MXE cross-compilation +set(IRIS_DEFAULT_BUNDLED_USRSCTP OFF) +if(NOT DEFINED USE_MXE AND (EXISTS "${CMAKE_TOOLCHAIN_FILE}")) + option(USE_MXE "Use MXE (cross-compilation build environment for MS Windows)" OFF) + string(TOLOWER ${CMAKE_TOOLCHAIN_FILE} TOOLCHAIN_FILE) + string(REGEX MATCH "mxe-conf" MXE_DETECTED "${TOOLCHAIN_FILE}") + if(MXE_DETECTED) + message(STATUS "MXE environment detected") + message(STATUS "MXE toolchain: ${CMAKE_TOOLCHAIN_FILE}") + message(STATUS "MXE root path: ${CMAKE_PREFIX_PATH}") + set(USE_MXE ON) + set(IRIS_DEFAULT_BUNDLED_USRSCTP ON) + endif() +endif() -include_directories(${PROJECT_SOURCE_DIR} ${PROJECT_BINARY_DIR}) -include_directories(include/iris) +set(IRIS_DEFAULT_BUNDLED_QCA OFF) +if(APPLE OR (MSVC OR USE_MXE)) + set(IRIS_DEFAULT_BUNDLED_QCA ON) +endif() -set(CMAKE_AUTOMOC ON) +option(IRIS_ENABLE_INSTALL "Enable installation" ON) +option(IRIS_ENABLE_JINGLE_SCTP "Enable SCTP over ICE Jingle transport / data channels" ON) +option(IRIS_BUNDLED_QCA "Adds: DTLS, Blake2b and other useful for XMPP crypto-stuff" ${IRIS_DEFAULT_BUNDLED_QCA}) +option(IRIS_BUNDLED_USRSCTP "Compile compatible UsrSCTP lib (required for datachannel Jingle transport)" ${IRIS_DEFAULT_BUNDLED_USRSCTP}) +option(IRIS_BUILD_TOOLS "Build tools and examples" OFF) +option(IRIS_ENABLE_DEBUG "Enable debugging code paths" OFF) -find_package(Qt5 COMPONENTS Core Gui Xml Network REQUIRED) -if(USE_QJDNS) - message(WARNING "USE_QJDNS flag is enabled with Qt5. If you have problems with connection please disable this flag") +set(IRIS_INSTALL_INCLUDEDIR ${CMAKE_INSTALL_INCLUDEDIR}/xmpp/iris) + +set(CMAKE_CXX_STANDARD 17) + +if("${CMAKE_BUILD_TYPE}" STREQUAL "Debug" OR ("${CMAKE_BUILD_TYPE}" STREQUAL "RelWithDebInfo")) + include(debug-definitions) endif() -if(USE_QJDNS) - add_definitions(-DNEED_JDNS) +if("${QT_DEFAULT_MAJOR_VERSION}" STREQUAL "") + set(QT_DEFAULT_MAJOR_VERSION 5) endif() -find_package(Qca REQUIRED) -set(QCA_INCLUDES ${Qca_INCLUDE_DIR}) -set(qca_LIB ${Qca_LIBRARY}) +if(QT_DEFAULT_MAJOR_VERSION LESS 6) + find_package(Qt5 5.10 REQUIRED COMPONENTS Core Gui Xml Network) +else() + find_package(Qt6 REQUIRED COMPONENTS Core Gui Xml Network) +endif() +message(STATUS "Qt${QT_DEFAULT_MAJOR_VERSION} found, version ${Qt${QT_DEFAULT_MAJOR_VERSION}Core_VERSION}") -include_directories( - ${QCA_INCLUDES} -) +# Find QCA package or use bundled if enabled +include(IrisQCA) -if(NOT USE_QJDNS AND SEPARATE_QJDNS) - message(WARNING "SEPARATE_QJDNS flag enabled, but USE_QJDNS flag disabled. Both flags will be disabled") - set(SEPARATE_QJDNS OFF) -endif() +set(STDINT_FOUND OFF) +find_file(STDINT_INCLUDE stdint.h) -if(USE_QJDNS AND (NOT SEPARATE_QJDNS)) - add_definitions(-DJDNS_STATIC) - set(QJDns_LIBRARY qjdns) - include_directories( - src/jdns/include/jdns - ) - set(jdns_SRCS - src/jdns/src/jdns/jdns.c - src/jdns/src/jdns/jdns_mdnsd.c - src/jdns/src/jdns/jdns_packet.c - src/jdns/src/jdns/jdns_sys.c - src/jdns/src/jdns/jdns_util.c - ) - set(jdns_PUBLIC_HEADERS - src/jdns/include/jdns/jdns.h - src/jdns/include/jdns/jdns_export.h - ) - set(jdns_HEADERS - src/jdns/src/jdns/jdns_packet.h - src/jdns/src/jdns/jdns_mdnsd.h - src/jdns/src/jdns/jdns_p.h - ) - add_library(jdns STATIC ${jdns_SRCS} ${jdns_HEADERS} ${jdns_PUBLIC_HEADERS}) - if(WIN32) - target_link_libraries(jdns ws2_32 advapi32) - endif() - set(qjdns_MOC_HDRS - src/jdns/include/jdns/qjdns.h - src/jdns/include/jdns/qjdnsshared.h - src/jdns/src/qjdns/qjdns_p.h - src/jdns/src/qjdns/qjdnsshared_p.h - ) - - qt_wrap_cpp(qjdns_MOC_SRCS ${qjdns_MOC_HDRS}) - - set(qjdns_SRCS - src/jdns/src/qjdns/qjdns.cpp - src/jdns/src/qjdns/qjdns_sock.cpp - src/jdns/src/qjdns/qjdnsshared.cpp - ) - - set(qjdns_PUBLIC_HEADERS - src/jdns/include/jdns/qjdns.h - src/jdns/include/jdns/qjdnsshared.h - ) - set(qjdns_HEADERS - src/jdns/src/qjdns/qjdns_sock.h - ) - add_library(${QJDns_LIBRARY} STATIC ${qjdns_SRCS} ${qjdns_MOC_SRCS} ${qjdns_MOC_HDRS} ${qjdns_PUBLIC_HEADERS}) - target_link_libraries(${QJDns_LIBRARY} Qt5::Core Qt5::Network) - target_link_libraries(${QJDns_LIBRARY} jdns) -elseif(USE_QJDNS) - set(QJDns_SUFFIX -qt5) - find_package(QJDns REQUIRED) - set(QJDns_LIBRARY ${QJDns_LIBRARY} PARENT_SCOPE) - include_directories( ${QJDns_INCLUDE_DIR} ) +if(NOT ${STDINT_INCLUDE} STREQUAL "STDINT_INCLUDE-NOTFOUND") + message(STATUS "StdInt include found: ${STDINT_INCLUDE}") + set(STDINT_FOUND ON) endif() -find_package(IDN REQUIRED) +# Find SCTP package or use bundled if enabled +if(IRIS_ENABLE_JINGLE_SCTP) + include(IrisSCTP) +endif() -add_definitions(-DIRISNET_STATIC) +if(NOT IRIS_BUNDLED_QCA) + find_package(B2 QUIET) + if(B2_FOUND) + message(STATUS "Found B2: ${B2_LIBRARY}") + endif() +endif() +add_subdirectory(3rdparty/stringprep) add_subdirectory(src/irisnet) add_subdirectory(src/xmpp) + +if(IRIS_BUILD_TOOLS) + if(NOT IRIS_BUNDLED_QCA) + message(FATAL_ERROR "Bundled Qca is needed to build tools") + endif() + add_subdirectory(tools) +endif() + +if(NOT IS_SUBPROJECT) + include(fix-codestyle) +endif() + +if(IRIS_ENABLE_INSTALL) + include(CMakePackageConfigHelpers) + include(GNUInstallDirs) + + write_basic_package_version_file( + ${CMAKE_CURRENT_BINARY_DIR}/IrisConfigVersion.cmake + VERSION ${IRIS_LIB_VERSION_STRING} + COMPATIBILITY SameMajorVersion + ) + configure_file( + iris.pc.in + ${CMAKE_CURRENT_BINARY_DIR}/iris.pc + @ONLY + ) + + install(TARGETS iris + EXPORT iris + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} + ) + install(EXPORT iris + DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/Iris + NAMESPACE iris:: + FILE IrisConfig.cmake + ) + install(FILES ${CMAKE_CURRENT_BINARY_DIR}/IrisConfigVersion.cmake + DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/Iris + ) + install(FILES ${CMAKE_CURRENT_BINARY_DIR}/iris.pc + DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig + ) + + install(DIRECTORY include/iris + DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/xmpp + FILES_MATCHING PATTERN "*.h" + ) +endif() diff --git a/COPYING b/COPYING index 5ab7695a..732811e4 100644 --- a/COPYING +++ b/COPYING @@ -500,5 +500,3 @@ necessary. Here is a sample; alter the names: Ty Coon, President of Vice That's all there is to it! - - diff --git a/IrisConfigVersion.cmake.in b/IrisConfigVersion.cmake.in index 2a2c2990..f7ec235a 100644 --- a/IrisConfigVersion.cmake.in +++ b/IrisConfigVersion.cmake.in @@ -1,9 +1,9 @@ set(PACKAGE_VERSION "@IRIS_LIB_VERSION_STRING@") if(PACKAGE_FIND_VERSION VERSION_EQUAL PACKAGE_VERSION) set(PACKAGE_VERSION_EXACT TRUE) -endif(PACKAGE_FIND_VERSION VERSION_EQUAL PACKAGE_VERSION) +endif() if(NOT PACKAGE_FIND_VERSION VERSION_GREATER PACKAGE_VERSION) set(PACKAGE_VERSION_COMPATIBLE TRUE) -else(NOT PACKAGE_FIND_VERSION VERSION_GREATER PACKAGE_VERSION) +else() set(PACKAGE_VERSION_UNSUITABLE TRUE) -endif(NOT PACKAGE_FIND_VERSION VERSION_GREATER PACKAGE_VERSION) +endif() diff --git a/README.md b/README.md index 2fc3ed76..fd19227e 100644 --- a/README.md +++ b/README.md @@ -31,13 +31,13 @@ Iris depends on Qt and QCA. * Roster management * Subscriptions -* JEP extensions: +* XEP extensions: * Version/Time requests * Service Discovery (disco, browse, and older ‘agents’ modes) * Account registration * Password changing * Agent/Transport registration - * VCards + * vCards * Basic Groupchat * OpenPGP capable * S5B Direct Connections diff --git a/TODO b/TODO index 49de0c4d..6d522fd9 100644 --- a/TODO +++ b/TODO @@ -43,4 +43,3 @@ questions: goal: support xmpp-core for tcp/httpbind xmpp-core should be "just another protocol" - diff --git a/cmake/modules/FindIDN.cmake b/cmake/modules/FindB2.cmake similarity index 65% rename from cmake/modules/FindIDN.cmake rename to cmake/modules/FindB2.cmake index ea3a9133..d712e9b2 100644 --- a/cmake/modules/FindIDN.cmake +++ b/cmake/modules/FindB2.cmake @@ -1,5 +1,5 @@ #============================================================================= -# Copyright 2016-2017 Psi+ Project, Vitaly Tonkacheyev +# Copyright (C) 2016-2020 Psi+ Project, Vitaly Tonkacheyev # All rights reserved. # # Redistribution and use in source and binary forms, with or without @@ -28,53 +28,48 @@ if(CMAKE_BUILD_TYPE STREQUAL "Debug" AND WIN32) set(D "d") endif() -if( IDN_INCLUDE_DIR AND IDN_LIBRARY ) +if( B2_INCLUDE_DIR AND B2_LIBRARY ) # in cache already - set(IDN_FIND_QUIETLY TRUE) -endif( IDN_INCLUDE_DIR AND IDN_LIBRARY ) + set(B2_FIND_QUIETLY TRUE) +endif() if( UNIX AND NOT( APPLE OR CYGWIN ) ) find_package( PkgConfig QUIET ) - pkg_check_modules( PC_IDN QUIET libidn ) - if( PC_IDN_FOUND ) - set( IDN_DEFINITIONS ${PC_IDN_CFLAGS} ${PC_IDN_CFLAGS_OTHER} ) - endif( PC_IDN_FOUND ) -endif( UNIX AND NOT( APPLE OR CYGWIN ) ) - -set( IDN_ROOT "" CACHE STRING "Path to libidn library" ) + pkg_check_modules( PC_B2 QUIET libb2 ) + if( PC_B2_FOUND ) + set( B2_DEFINITIONS ${PC_B2_CFLAGS} ${PC_B2_CFLAGS_OTHER} ) + endif() +endif() find_path( - IDN_INCLUDE_DIR idna.h + B2_INCLUDE_DIR blake2.h HINTS - ${IDN_ROOT}/include - ${PC_IDN_INCLUDEDIR} - ${PC_IDN_INCLUDE_DIRS} + ${B2_ROOT}/include + ${PC_B2_INCLUDEDIR} + ${PC_B2_INCLUDE_DIRS} ) -set(IDN_NAMES - idn${D} - libidn${D} - idn-11${D} - libidn-11${D} +set(B2_NAMES + b2${D} ) find_library( - IDN_LIBRARY - NAMES ${IDN_NAMES} + B2_LIBRARY + NAMES ${B2_NAMES} HINTS - ${PC_IDN_LIBDIR} - ${PC_IDN_LIBRARY_DIRS} - ${IDN_ROOT}/lib - ${IDN_ROOT}/bin + ${PC_B2_LIBDIR} + ${PC_B2_LIBRARY_DIRS} + ${B2_ROOT}/lib + ${B2_ROOT}/bin ) include(FindPackageHandleStandardArgs) find_package_handle_standard_args( - IDN + B2 DEFAULT_MSG - IDN_LIBRARY - IDN_INCLUDE_DIR + B2_LIBRARY + B2_INCLUDE_DIR ) -if( IDN_FOUND ) - set( IDN_LIBRARIES ${IDN_LIBRARY} ) - set( IDN_INCLUDE_DIRS ${IDN_INCLUDE_DIR} ) -endif( IDN_FOUND ) +if( B2_FOUND ) + set( B2_LIBRARIES ${B2_LIBRARY} ) + set( B2_INCLUDE_DIRS ${B2_INCLUDE_DIR} ) +endif() -mark_as_advanced( IDN_INCLUDE_DIR IDN_LIBRARY ) +mark_as_advanced( B2_INCLUDE_DIR B2_LIBRARY ) diff --git a/cmake/modules/FindQJDns.cmake b/cmake/modules/FindQJDns.cmake index 1b2359fd..941e9ece 100644 --- a/cmake/modules/FindQJDns.cmake +++ b/cmake/modules/FindQJDns.cmake @@ -1,5 +1,5 @@ #============================================================================= -# Copyright 2016-2017 Psi+ Project +# Copyright (C) 2016-2017 Psi+ Project # All rights reserved. # # Redistribution and use in source and binary forms, with or without @@ -36,13 +36,13 @@ endif () if ( UNIX AND NOT( APPLE OR CYGWIN ) ) find_package( PkgConfig QUIET ) pkg_check_modules( PC_QJDns QUIET jdns ) - set ( QJDns_DEFINITIONS + set ( QJDns_DEFINITIONS ${PC_QJDns_CFLAGS} ${PC_QJDns_CFLAGS_OTHER} ) endif ( UNIX AND NOT( APPLE OR CYGWIN ) ) -set ( LIBINCS +set ( LIBINCS qjdns.h ) @@ -69,7 +69,7 @@ set(QJDns_NAMES find_library( QJDns_LIBRARY NAMES ${QJDns_NAMES} - HINTS + HINTS ${PC_QJDns_LIBDIR} ${PC_QJDns_LIBRARY_DIRS} ${QJDNS_DIR}/lib diff --git a/cmake/modules/FindQca.cmake b/cmake/modules/FindQca.cmake index a82ada09..55ac4932 100644 --- a/cmake/modules/FindQca.cmake +++ b/cmake/modules/FindQca.cmake @@ -1,5 +1,5 @@ #============================================================================= -# Copyright 2016-2017 Psi+ Project, Vitaly Tonkacheyev +# Copyright 2016-2020 Psi+ Project, Vitaly Tonkacheyev # All rights reserved. # # Redistribution and use in source and binary forms, with or without @@ -31,13 +31,14 @@ endif() if (Qca_INCLUDE_DIR AND Qca_LIBRARY) # in cache already set(Qca_FIND_QUIETLY TRUE) -endif () +endif() set(EXTRA_PATH_SUFFIXES - qt5/Qca-qt5/QtCrypto - Qca-qt5/QtCrypto + qt5/Qca-qt${QT_DEFAULT_MAJOR_VERSION}/QtCrypto + Qca-qt${QT_DEFAULT_MAJOR_VERSION}/QtCrypto qt5/QtCrypto - qt/Qca-qt5/QtCrypto + qt/Qca-qt${QT_DEFAULT_MAJOR_VERSION}/QtCrypto + lib/qca-qt${QT_DEFAULT_MAJOR_VERSION}.framework/Versions/2/Headers ) find_path( @@ -51,11 +52,12 @@ find_path( find_library( Qca_LIBRARY - NAMES qca-qt5${D} - HINTS + NAMES qca-qt${QT_DEFAULT_MAJOR_VERSION}${D} + HINTS ${QCA_DIR}/lib ${QCA_DIR}/bin ) + include(FindPackageHandleStandardArgs) find_package_handle_standard_args( Qca @@ -63,10 +65,10 @@ find_package_handle_standard_args( Qca_LIBRARY Qca_INCLUDE_DIR ) + if (Qca_FOUND) set ( Qca_LIBRARIES ${Qca_LIBRARY} ) set ( Qca_INCLUDE_DIRS ${Qca_INCLUDE_DIR} ) -endif(Qca_FOUND) +endif() mark_as_advanced( Qca_INCLUDE_DIR Qca_LIBRARY ) - diff --git a/cmake/modules/FindUsrSCTP.cmake b/cmake/modules/FindUsrSCTP.cmake new file mode 100644 index 00000000..efc77ab6 --- /dev/null +++ b/cmake/modules/FindUsrSCTP.cmake @@ -0,0 +1,24 @@ +# Simple libnice cmake find + +if (NOT TARGET SctpLab::UsrSCTP) + #set(USRSCTP_DEFINITIONS INET INET6) + find_path(USRSCTP_INCLUDE usrsctp.h HINTS ${USRSCTP_INCLUDE_DIR} PATH_SUFFIXES usrsctp) + find_library(USRSCTP_LIBRARY NAMES usrsctp libusrsctp HINTS ${USRSCTP_LIB_DIR}) + + include(FindPackageHandleStandardArgs) + find_package_handle_standard_args(UsrSCTP DEFAULT_MSG USRSCTP_LIBRARY USRSCTP_INCLUDE) + + mark_as_advanced(USRSCTP_INCLUDE USRSCTP_LIBRARY) + + set(USRSCTP_LIBRARIES ${USRSCTP_LIBRARY}) + set(USRSCTP_INCLUDES ${USRSCTP_INCLUDE}) + + if (UsrSCTP_FOUND) + add_library(SctpLab::UsrSCTP UNKNOWN IMPORTED) + set_target_properties(SctpLab::UsrSCTP PROPERTIES + IMPORTED_LOCATION "${USRSCTP_LIBRARY}" + INTERFACE_COMPILE_DEFINITIONS "${USRSCTP_DEFINITIONS}" + INTERFACE_INCLUDE_DIRECTORIES "${USRSCTP_INCLUDES}" + IMPORTED_LINK_INTERFACE_LANGUAGES "C") + endif () +endif () diff --git a/cmake/modules/IrisQCA.cmake b/cmake/modules/IrisQCA.cmake new file mode 100644 index 00000000..e43b80fd --- /dev/null +++ b/cmake/modules/IrisQCA.cmake @@ -0,0 +1,77 @@ +cmake_minimum_required(VERSION 3.10.0) + +set(IrisQCAGitRepo "https://github.com/psi-im/qca.git") + +if(IRIS_BUNDLED_QCA) + message(STATUS "QCA: using bundled") + set(QCA_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/3rdparty/qca) + set(QCA_PREFIX ${CMAKE_CURRENT_BINARY_DIR}/qca) + set(QCA_BUILD_DIR ${QCA_PREFIX}/build) + set(Qca_INCLUDE_DIR ${QCA_BUILD_DIR}) + if(NOT EXISTS ${QCA_SOURCE_DIR}) + list(APPEND Qca_INCLUDE_DIR ${QCA_PREFIX}/src/QcaProject/include/QtCrypto) + else() + list(APPEND Qca_INCLUDE_DIR ${QCA_SOURCE_DIR}/include/QtCrypto) + endif() + set(Qca_CORE_LIB ${QCA_BUILD_DIR}/lib/${CMAKE_STATIC_LIBRARY_PREFIX}qca-qt${QT_DEFAULT_MAJOR_VERSION}${D}${CMAKE_STATIC_LIBRARY_SUFFIX}) + set(Qca_OSSL_LIB ${QCA_BUILD_DIR}/lib/qca-qt${QT_DEFAULT_MAJOR_VERSION}/crypto/${CMAKE_STATIC_LIBRARY_PREFIX}qca-ossl${D}${CMAKE_STATIC_LIBRARY_SUFFIX}) + set(Qca_LIBRARY ${Qca_OSSL_LIB} ${Qca_CORE_LIB}) + if(APPLE) + set(COREFOUNDATION_LIBRARY "-framework CoreFoundation") + set(COREFOUNDATION_LIBRARY_SECURITY "-framework Security") + set(Qca_LIBRARY ${Qca_LIBRARY} ${COREFOUNDATION_LIBRARY} ${COREFOUNDATION_LIBRARY_SECURITY}) + endif() + if(IS_SUBPROJECT) + set(Qca_LIBRARY_EXPORT ${Qca_LIBRARY} PARENT_SCOPE) + set(Qca_INCLUDE_DIR_EXPORT ${Qca_INCLUDE_DIR} PARENT_SCOPE) + endif() + + if ("${OPENSSL_ROOT_DIR}" STREQUAL "" AND APPLE) + set(OPENSSL_ROOT_DIR /usr/local/opt/openssl) + endif() + find_package(OpenSSL REQUIRED) + + include(ExternalProject) + #set CMake options and transfer the environment to an external project + set(QCA_BUILD_OPTIONS + -DBUILD_SHARED_LIBS=OFF -DBUILD_PLUGINS=ossl -DLOAD_SHARED_PLUGINS=OFF + -DBUILD_TESTS=OFF -DBUILD_TOOLS=OFF -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} + -DCMAKE_INSTALL_PREFIX=${QCA_PREFIX} -DCMAKE_PREFIX_PATH=${CMAKE_PREFIX_PATH} + -DOPENSSL_ROOT_DIR=${OPENSSL_ROOT_DIR} -DCMAKE_TOOLCHAIN_FILE=${CMAKE_TOOLCHAIN_FILE} + -DCMAKE_MAKE_PROGRAM=${CMAKE_MAKE_PROGRAM} -DCMAKE_C_COMPILER=${CMAKE_C_COMPILER} + -DOSX_FRAMEWORK=OFF) + if(QT_DEFAULT_MAJOR_VERSION LESS 6) + list(APPEND QCA_BUILD_OPTIONS -DBUILD_WITH_QT6=OFF) + else() + list(APPEND QCA_BUILD_OPTIONS -DBUILD_WITH_QT6=ON) + endif() + if (EXISTS ${QCA_SOURCE_DIR}) + message(STATUS "QCA: found bundled sources") + ExternalProject_Add(QcaProject + PREFIX ${QCA_PREFIX} + BINARY_DIR ${QCA_BUILD_DIR} + SOURCE_DIR ${QCA_SOURCE_DIR} + CMAKE_ARGS ${QCA_BUILD_OPTIONS} + BUILD_BYPRODUCTS ${Qca_LIBRARY} + INSTALL_COMMAND "" + ) + else() + include(FindGit) + find_package(Git) + if(NOT Git_FOUND) + message(FATAL_ERROR "Git not found! Bundled Qca needs Git utility.\nPlease set GIT_EXECUTABLE variable or add git to PATH") + endif() + ExternalProject_Add(QcaProject + PREFIX ${QCA_PREFIX} + BINARY_DIR ${QCA_BUILD_DIR} + GIT_REPOSITORY ${IrisQCAGitRepo} + CMAKE_ARGS ${QCA_BUILD_OPTIONS} + BUILD_BYPRODUCTS ${Qca_LIBRARY} + INSTALL_COMMAND "" + ) + endif() +else() + message(WARNING "Disabling IRIS_BUNDLED_QCA option makes impossible to use DTLS and PsiMedia") + message(STATUS "QCA: using system") + find_package(Qca REQUIRED) +endif() diff --git a/cmake/modules/IrisSCTP.cmake b/cmake/modules/IrisSCTP.cmake new file mode 100644 index 00000000..42226968 --- /dev/null +++ b/cmake/modules/IrisSCTP.cmake @@ -0,0 +1,75 @@ +cmake_minimum_required(VERSION 3.10.0) + +set(IrisSCTPGitRepo "https://github.com/sctplab/usrsctp.git") + +if(USE_MXE AND STDINT_FOUND) + # Add SCTP_STDINT_INCLUDE definition to compile irisnet with usrsctp with MinGW + add_definitions( + -DSCTP_STDINT_INCLUDE="${STDINT_INCLUDE}" + ) +endif() + +if(NOT IRIS_BUNDLED_USRSCTP) + find_package(UsrSCTP) + if(NOT UsrSCTP_FOUND) + message(FATAL_ERROR "UsrSCTP library not found. Try to install usrsctp library or enable IRIS_BUNDLED_USRSCTP flag") + endif() +else() + message(STATUS "USRSCTP: using bundled") + set(USRSCTP_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/3rdparty/usrsctp) + set(USRSCTP_PREFIX ${CMAKE_CURRENT_BINARY_DIR}/usrsctp) + set(USRSCTP_BUILD_DIR ${USRSCTP_PREFIX}/build) + if(NOT EXISTS ${USRSCTP_SOURCE_DIR}) + set(USRSCTP_INCLUDES ${USRSCTP_PREFIX}/src/UsrSCTPProject/usrsctplib) + else() + set(USRSCTP_INCLUDES ${USRSCTP_SOURCE_DIR}/usrsctplib) + endif() + set(USRSCTP_LIBRARY ${USRSCTP_BUILD_DIR}/usrsctplib/${CMAKE_STATIC_LIBRARY_PREFIX}usrsctp${CMAKE_STATIC_LIBRARY_SUFFIX}) + if(WIN32 AND MSVC) + add_definitions(-DWIN32_LEAN_AND_MEAN) + endif() + + include(ExternalProject) + #set CMake options and transfer the environment to an external project + set(USRSCTP_BUILD_OPTIONS + -DBUILD_SHARED_LIBS=OFF -Dsctp_build_programs=OFF -Dsctp_build_shared_lib=OFF -Dsctp_debug=OFF + -Dsctp_inet=OFF -Dsctp_inet6=OFF -DCMAKE_INSTALL_PREFIX=${USRSCTP_PREFIX} + -DCMAKE_PREFIX_PATH=${CMAKE_PREFIX_PATH} -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} + -DCMAKE_TOOLCHAIN_FILE=${CMAKE_TOOLCHAIN_FILE} -DCMAKE_MAKE_PROGRAM=${CMAKE_MAKE_PROGRAM} + -DCMAKE_C_COMPILER=${CMAKE_C_COMPILER}) + if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU") + list(APPEND USRSCTP_BUILD_OPTIONS -DCMAKE_C_FLAGS="-Wno-maybe-uninitialized") + endif() + if (EXISTS ${USRSCTP_SOURCE_DIR}) + message(STATUS "USRSCTP: found bundled sources") + ExternalProject_Add(UsrSCTPProject + PREFIX ${USRSCTP_PREFIX} + BINARY_DIR ${USRSCTP_BUILD_DIR} + SOURCE_DIR ${USRSCTP_SOURCE_DIR} + CMAKE_ARGS ${USRSCTP_BUILD_OPTIONS} + BUILD_BYPRODUCTS ${USRSCTP_LIBRARY} + INSTALL_COMMAND "" + ) + else() + include(FindGit) + find_package(Git) + if(NOT Git_FOUND) + message(FATAL_ERROR "Git not found! Bundled UsrSCTP needs Git utility.\nPlease set GIT_EXECUTABLE variable or add git to PATH") + endif() + ExternalProject_Add(UsrSCTPProject + PREFIX ${USRSCTP_PREFIX} + BINARY_DIR ${USRSCTP_BUILD_DIR} + GIT_REPOSITORY ${IrisSCTPGitRepo} + GIT_TAG a17109528c75d01f6372d5c30851a639684c6e99 + CMAKE_ARGS ${USRSCTP_BUILD_OPTIONS} + BUILD_BYPRODUCTS ${USRSCTP_LIBRARY} + INSTALL_COMMAND "" + ) + endif() + add_library(SctpLab::UsrSCTP UNKNOWN IMPORTED) + set_target_properties(SctpLab::UsrSCTP PROPERTIES + IMPORTED_LOCATION "${USRSCTP_LIBRARY}" + INTERFACE_COMPILE_DEFINITIONS "${USRSCTP_DEFINITIONS}" + IMPORTED_LINK_INTERFACE_LANGUAGES "C") + add_dependencies(SctpLab::UsrSCTP ${USRSCTP_LIBRARY}) +endif() diff --git a/cmake/modules/debug-definitions.cmake b/cmake/modules/debug-definitions.cmake new file mode 100644 index 00000000..21061873 --- /dev/null +++ b/cmake/modules/debug-definitions.cmake @@ -0,0 +1,41 @@ +cmake_minimum_required(VERSION 3.10.0) +option(IRIS_NETNAMES_DEBUG "Add -DNETNAMES_DEBUG definition" OFF) +option(IRIS_PSI_ENABLE_SCTP_DEBUG "Add -DPSI_ENABLE_SCTP_DEBUG definition" OFF) +option(IRIS_ICE_DEBUG "Add -DICE_DEBUG definition" OFF) +option(IRIS_PROX_DEBUG "Add -DPROX_DEBUG definition" OFF) +option(IRIS_BS_DEBUG "Add -DBS_DEBUG definition" OFF) +option(IRIS_S5B_DEBUG "Add -DS5B_DEBUG definition" OFF) +option(IRIS_IBB_DEBUG "Add -DIBB_DEBUG definition" OFF) +option(IRIS_SM_DEBUG "Add -DIRIS_SM_DEBUG definition" OFF) +option(IRIS_XMPP_DEBUG "Add -DXMPP_DEBUG definition" OFF) +option(IRIS_TESTDEBUG_H "Add -DTESTDEBUG_H definition" OFF) +if(IRIS_NETNAMES_DEBUG) + add_definitions(-DNETNAMES_DEBUG) +endif() +if(IRIS_PSI_ENABLE_SCTP_DEBUG) + add_definitions(-DPSI_ENABLE_SCTP_DEBUG) +endif() +if(IRIS_ICE_DEBUG) + add_definitions(-DICE_DEBUG) +endif() +if(IRIS_PROX_DEBUG) + add_definitions(-DPROX_DEBUG) +endif() +if(IRIS_BS_DEBUG) + add_definitions(-DBS_DEBUG) +endif() +if(IRIS_S5B_DEBUG) + add_definitions(-DS5B_DEBUG) +endif() +if(IRIS_IBB_DEBUG) + add_definitions(-DIBB_DEBUG) +endif() +if(IRIS_SM_DEBUG) + add_definitions(-DIRIS_SM_DEBUG) +endif() +if(IRIS_XMPP_DEBUG) + add_definitions(-DXMPP_DEBUG) +endif() +if(IRIS_TESTDEBUG_H) + add_definitions(-DTESTDEBUG_H) +endif() diff --git a/cmake/modules/fix-codestyle.cmake b/cmake/modules/fix-codestyle.cmake new file mode 100644 index 00000000..cff163ac --- /dev/null +++ b/cmake/modules/fix-codestyle.cmake @@ -0,0 +1,31 @@ +cmake_minimum_required( VERSION 3.10.0 ) + +#Find clang-format binary +find_program(CLF_BIN clang-format DOC "Path to clang-format binary") +if(CLF_BIN) + #Obtain list of source files + file(GLOB_RECURSE SRC_LIST + *.c + *.cc + *.cpp + *.hpp + *.h + *.mm + qcm/*.qcm + ) + foreach(src_file ${SRC_LIST}) + #Exclude libpsi + if("${src_file}" MATCHES ".*/jdns/.*") + list(REMOVE_ITEM SRC_LIST ${src_file}) + endif() + endforeach() + add_custom_target(fix-codestyle + COMMAND ${CLF_BIN} + --verbose + -style=file + -i ${SRC_LIST} + WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} + COMMENT "Fix codestyle with clang-format" + VERBATIM + ) +endif() diff --git a/cmake/modules/policyRules.cmake b/cmake/modules/policyRules.cmake new file mode 100644 index 00000000..081ab755 --- /dev/null +++ b/cmake/modules/policyRules.cmake @@ -0,0 +1,13 @@ +cmake_minimum_required( VERSION 3.10 ) +#Set automoc and autouic policy +if(NOT POLICY_SET) + if(POLICY CMP0071) + cmake_policy(SET CMP0071 NEW) + message(STATUS "CMP0071 policy set to NEW") + endif() + if(POLICY CMP0074) + cmake_policy(SET CMP0074 NEW) + message(STATUS "CMP0074 policy set to NEW") + endif() + set(POLICY_SET ON) +endif() diff --git a/cmake_uninstall.cmake.in b/cmake_uninstall.cmake.in index be7fbb2e..0585625e 100644 --- a/cmake_uninstall.cmake.in +++ b/cmake_uninstall.cmake.in @@ -1,6 +1,6 @@ if(NOT EXISTS "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt") message(FATAL_ERROR "Cannot find install manifest: \"@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt\"") -endif(NOT EXISTS "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt") +endif() file(READ "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt" files) string(REGEX REPLACE "\n" ";" files "${files}") @@ -14,4 +14,4 @@ foreach(file ${files}) if(NOT "${rm_retval}" STREQUAL 0) message(FATAL_ERROR "Problem when removing \"$ENV{DESTDIR}${file}\"") endif(NOT "${rm_retval}" STREQUAL 0) -endforeach(file) +endforeach() diff --git a/common.pri b/common.pri deleted file mode 100644 index 99da6d0a..00000000 --- a/common.pri +++ /dev/null @@ -1,19 +0,0 @@ -# common stuff for iris.pro and iris.pri - -CONFIG -= c++11 -CONFIG += c++14 - -# default build configuration -!iris_build_pri { - # build appledns on mac - mac:CONFIG += appledns - - # bundle appledns inside of irisnetcore on mac - mac:CONFIG += appledns_bundle - - # bundle irisnetcore inside of iris - CONFIG += irisnetcore_bundle - - # don't build iris, app will include iris.pri - #CONFIG += iris_bundle -} diff --git a/conf_win.pri.example b/conf_win.pri.example deleted file mode 100644 index e80c1b35..00000000 --- a/conf_win.pri.example +++ /dev/null @@ -1,17 +0,0 @@ -load(winlocal.prf) - -# qca -CONFIG += crypto - -# zlib -INCLUDEPATH += $$WINLOCAL_PREFIX/include -LIBS += -L$$WINLOCAL_PREFIX/lib - -# zlib may have a different lib name depending on how it was compiled -win32-g++ { - LIBS += -lz -} -else { - LIBS += -lzlib # static - #LIBS += -lzdll # dll -} diff --git a/confapp.pri b/confapp.pri deleted file mode 100644 index 9febe572..00000000 --- a/confapp.pri +++ /dev/null @@ -1,6 +0,0 @@ -unix:exists(confapp_unix.pri):include(confapp_unix.pri) -windows:exists(confapp_win.pri):include(confapp_win.pri) - -include(common.pri) - -mac:QMAKE_MACOSX_DEPLOYMENT_TARGET = 10.9 diff --git a/confapp_win.pri.example b/confapp_win.pri.example deleted file mode 100644 index e80c1b35..00000000 --- a/confapp_win.pri.example +++ /dev/null @@ -1,17 +0,0 @@ -load(winlocal.prf) - -# qca -CONFIG += crypto - -# zlib -INCLUDEPATH += $$WINLOCAL_PREFIX/include -LIBS += -L$$WINLOCAL_PREFIX/lib - -# zlib may have a different lib name depending on how it was compiled -win32-g++ { - LIBS += -lz -} -else { - LIBS += -lzlib # static - #LIBS += -lzdll # dll -} diff --git a/configure b/configure deleted file mode 100755 index af0fcd87..00000000 --- a/configure +++ /dev/null @@ -1,2595 +0,0 @@ -#!/bin/sh -# -# Generated by qconf 2.0 ( http://delta.affinix.com/qconf/ ) -# - -show_usage() { -cat </dev/null` - if echo $WHICH | grep 'shell built-in command' >/dev/null 2>&1; then - WHICH=which - elif [ -z "$WHICH" ]; then - if which which >/dev/null 2>&1; then - WHICH=which - else - for a in /usr/ucb /usr/bin /bin /usr/local/bin; do - if [ -x $a/which ]; then - WHICH=$a/which - break - fi - done - fi - fi - - RET_CODE=1 - if [ -z "$WHICH" ]; then - OLD_IFS=$IFS - IFS=: - for a in $PATH; do - if [ -x $a/$1 ]; then - echo "$a/$1" - RET_CODE=0 - [ -z "$ALL_MATCHES" ] && break - fi - done - IFS=$OLD_IFS - export IFS - else - a=`"$WHICH" "$ALL_MATCHES" "$1" 2>/dev/null` - if [ ! -z "$a" -a -x "$a" ]; then - echo "$a" - RET_CODE=0 - fi - fi - HOME=$OLD_HOME - export HOME - return $RET_CODE -} -WHICH=which_command - -# find a make command -if [ -z "$MAKE" ]; then - MAKE= - for mk in gmake make; do - if $WHICH $mk >/dev/null 2>&1; then - MAKE=`$WHICH $mk` - break - fi - done - if [ -z "$MAKE" ]; then - echo "You don't seem to have 'make' or 'gmake' in your PATH." - echo "Cannot proceed." - exit 1 - fi -fi - -show_qt_info() { - printf "Be sure you have a proper Qt 4.0+ build environment set up. This means not\n" - printf "just Qt, but also a C++ compiler, a make tool, and any other packages\n" - printf "necessary for compiling C++ programs.\n" - printf "\n" - printf "If you are certain everything is installed, then it could be that Qt is not\n" - printf "being recognized or that a different version of Qt is being detected by\n" - printf "mistake (for example, this could happen if \$QTDIR is pointing to a Qt 3\n" - printf "installation). At least one of the following conditions must be satisfied:\n" - printf "\n" - printf " 1) --qtdir is set to the location of Qt\n" - printf " 2) \$QTDIR is set to the location of Qt\n" - printf " 3) QtCore is in the pkg-config database\n" - printf " 4) qmake is in the \$PATH\n" - printf "\n" - printf "This script will use the first one it finds to be true, checked in the above\n" - printf "order. #3 and #4 are the recommended options. #1 and #2 are mainly for\n" - printf "overriding the system configuration.\n" - printf "\n" -} - -while [ $# -gt 0 ]; do - optarg=`expr "x$1" : 'x[^=]*=\(.*\)'` - case "$1" in - --qtdir=*) - EX_QTDIR=$optarg - shift - ;; - - --static) - QC_STATIC="Y" - shift - ;; - - --extraconf=*) - QC_EXTRACONF=$optarg - shift - ;; - - --release) - QC_RELEASE="Y" - shift - ;; - - --debug) - QC_DEBUG="Y" - shift - ;; - - --debug-and-release) - QC_DEBUG_AND_RELEASE="Y" - shift - ;; - - --no-separate-debug-info) - QC_NO_SEPARATE_DEBUG_INFO="Y" - shift - ;; - - --separate-debug-info) - QC_SEPARATE_DEBUG_INFO="Y" - shift - ;; - - --no-framework) - QC_NO_FRAMEWORK="Y" - shift - ;; - - --framework) - QC_FRAMEWORK="Y" - shift - ;; - - --universal) - QC_UNIVERSAL="Y" - shift - ;; - - --mac-sdk=*) - QC_MAC_SDK=$optarg - shift - ;; - - --enable-tests) - QC_ENABLE_TESTS="Y" - shift - ;; - - --with-idn-inc=*) - QC_WITH_IDN_INC=$optarg - shift - ;; - - --with-idn-lib=*) - QC_WITH_IDN_LIB=$optarg - shift - ;; - - --with-qca-inc=*) - QC_WITH_QCA_INC=$optarg - shift - ;; - - --with-qca-lib=*) - QC_WITH_QCA_LIB=$optarg - shift - ;; - - --with-zlib-inc=*) - QC_WITH_ZLIB_INC=$optarg - shift - ;; - - --with-zlib-lib=*) - QC_WITH_ZLIB_LIB=$optarg - shift - ;; - - --with-qjdns-inc=*) - QC_WITH_QJDNS_INC=$optarg - shift - ;; - - --with-qjdns-lib=*) - QC_WITH_QJDNS_LIB=$optarg - shift - ;; - - --verbose) - QC_VERBOSE="Y" - shift - ;; - --qtselect=*) - QC_QTSELECT="${optarg}" - shift - ;; - --help) show_usage; exit ;; - *) echo "configure: WARNING: unrecognized options: $1" >&2; shift; ;; - esac -done - - -echo "Configuring Iris ..." - -if [ "$QC_VERBOSE" = "Y" ]; then -echo -echo EX_QTDIR=$EX_QTDIR -echo QC_STATIC=$QC_STATIC -echo QC_EXTRACONF=$QC_EXTRACONF -echo QC_RELEASE=$QC_RELEASE -echo QC_DEBUG=$QC_DEBUG -echo QC_DEBUG_AND_RELEASE=$QC_DEBUG_AND_RELEASE -echo QC_NO_SEPARATE_DEBUG_INFO=$QC_NO_SEPARATE_DEBUG_INFO -echo QC_SEPARATE_DEBUG_INFO=$QC_SEPARATE_DEBUG_INFO -echo QC_NO_FRAMEWORK=$QC_NO_FRAMEWORK -echo QC_FRAMEWORK=$QC_FRAMEWORK -echo QC_UNIVERSAL=$QC_UNIVERSAL -echo QC_MAC_SDK=$QC_MAC_SDK -echo QC_ENABLE_TESTS=$QC_ENABLE_TESTS -echo QC_WITH_IDN_INC=$QC_WITH_IDN_INC -echo QC_WITH_IDN_LIB=$QC_WITH_IDN_LIB -echo QC_WITH_QCA_INC=$QC_WITH_QCA_INC -echo QC_WITH_QCA_LIB=$QC_WITH_QCA_LIB -echo QC_WITH_ZLIB_INC=$QC_WITH_ZLIB_INC -echo QC_WITH_ZLIB_LIB=$QC_WITH_ZLIB_LIB -echo QC_WITH_QJDNS_INC=$QC_WITH_QJDNS_INC -echo QC_WITH_QJDNS_LIB=$QC_WITH_QJDNS_LIB -echo -fi - -printf "Verifying Qt build environment ... " - -if [ -z "$QC_QTSELECT" ]; then - QC_QTSELECT="$(echo $QT_SELECT | tr -d "qt")" -fi - -if [ ! -z "$QC_QTSELECT" ]; then - QTSEARCHTEXT="$QC_QTSELECT" -else - QTSEARCHTEXT="4 or 5" -fi - -# run qmake and check version -qmake_check() { - if [ -x "$1" ]; then - cmd="\"$1\" -query QT_VERSION" - if [ "$QC_VERBOSE" = "Y" ]; then - echo "running: $cmd" - fi - vout=`/bin/sh -c "$cmd" 2>&1` - case "${vout}" in - *.*.*) - vmaj="${vout%%.*}" - if [ ! -z "$QC_QTSELECT" ]; then - if [ "$vmaj" = "$QC_QTSELECT" ]; then - return 0 - fi - else - if [ "$vmaj" = "4" ] || [ "$vmaj" = "5" ]; then - return 0 - fi - fi - ;; - esac - if [ "$QC_VERBOSE" = "Y" ]; then - echo "Warning: $1 not for Qt ${QTSEARCHTEXT}" - fi - fi - return 1 -} - -if [ "$QC_VERBOSE" = "Y" ]; then - echo -fi - -qm="" -qt4_names="qmake-qt4 qmake4" -qt5_names="qmake-qt5 qmake5" -names="qmake" -if [ -z "$QC_QTSELECT" ]; then - names="${qt5_names} ${qt4_names} $names" -else - if [ "$QC_QTSELECT" = "4" ]; then - names="${qt4_names} $names" - elif [ "$QC_QTSELECT" -ge "5" ]; then - names="${qt5_names} $names" - fi -fi - -if [ -z "$qm" ] && [ ! -z "$EX_QTDIR" ]; then - # qt4 check: --qtdir - for n in $names; do - qstr=$EX_QTDIR/bin/$n - if qmake_check "$qstr"; then - qm=$qstr - break - fi - done - if [ -z "$qm" ] && [ "$QC_VERBOSE" = "Y" ]; then - echo "Warning: qmake not found via --qtdir" - fi - -elif [ -z "$qm" ] && [ ! -z "$QTDIR" ]; then - # qt4 check: QTDIR - for n in $names; do - qstr=$QTDIR/bin/$n - if qmake_check "$qstr"; then - qm=$qstr - break - fi - done - if [ -z "$qm" ] && [ "$QC_VERBOSE" = "Y" ]; then - echo "Warning: qmake not found via \$QTDIR" - fi - -else - # Try all other implicit checks - - # qtchooser - if [ -z "$qm" ]; then - qtchooser=$($WHICH qtchooser 2>/dev/null) - if [ ! -z "$qtchooser" ]; then - if [ ! -z "$QC_QTSELECT" ]; then - versions="$QC_QTSELECT" - else - cmd="$qtchooser --list-versions" - if [ "$QC_VERBOSE" = "Y" ]; then - echo "running: $cmd" - fi - versions=`$cmd` - fi - for version in $versions; do - cmd="$qtchooser -run-tool=qmake -qt=${version} -query QT_INSTALL_BINS" - if [ "$QC_VERBOSE" = "Y" ]; then - echo "running: $cmd" - fi - qtbins=`$cmd 2>/dev/null` - if [ ! -z "$qtbins" ] && qmake_check "$qtbins/qmake"; then - qm="$qtbins/qmake" - break - fi - done - fi - fi - if [ -z "$qm" ] && [ "$QC_VERBOSE" = "Y" ]; then - echo "Warning: qmake not found via qtchooser" - fi - - # qt4: pkg-config - if [ -z "$qm" ]; then - cmd="pkg-config QtCore --variable=exec_prefix" - if [ "$QC_VERBOSE" = "Y" ]; then - echo "running: $cmd" - fi - str=`$cmd 2>/dev/null` - if [ ! -z "$str" ]; then - for n in $names; do - qstr=$str/bin/$n - if qmake_check "$qstr"; then - qm=$qstr - break - fi - done - fi - fi - if [ -z "$qm" ] && [ "$QC_VERBOSE" = "Y" ]; then - echo "Warning: qmake not found via pkg-config" - fi - - # qmake in PATH - if [ -z "$qm" ]; then - for n in $names; do - qstr=`$WHICH -a $n 2>/dev/null` - for q in $qstr; do - if qmake_check "$q"; then - qm="$q" - break - fi - done - if [ ! -z "$qm" ]; then - break - fi - done - fi - if [ -z "$qm" ] && [ "$QC_VERBOSE" = "Y" ]; then - echo "Warning: qmake not found via \$PATH" - fi - - # end of implicit checks -fi - -if [ -z "$qm" ]; then - if [ "$QC_VERBOSE" = "Y" ]; then - echo " -> fail" - else - echo "fail" - fi - printf "\n" - printf "Reason: Unable to find the 'qmake' tool for Qt ${QTSEARCHTEXT}.\n" - printf "\n" - show_qt_info - exit 1; -fi -if [ "$QC_VERBOSE" = "Y" ]; then - echo qmake found in "$qm" -fi - -# try to determine the active makespec -defmakespec=$QMAKESPEC -if [ -z "$defmakespec" ]; then - if $WHICH readlink >/dev/null 2>&1; then - READLINK=`$WHICH readlink` - fi - if [ ! -z "$READLINK" ]; then - qt_mkspecsdir=`"$qm" -query QT_INSTALL_DATA`/mkspecs - if [ -d "$qt_mkspecsdir" ] && [ -h "$qt_mkspecsdir/default" ]; then - defmakespec=`$READLINK $qt_mkspecsdir/default` - fi - fi -fi - -if [ "$QC_VERBOSE" = "Y" ]; then - echo makespec is $defmakespec -fi - -qm_spec="" -# if the makespec is macx-xcode, force macx-g++ -if [ "$defmakespec" = "macx-xcode" ]; then - qm_spec=macx-g++ - QMAKESPEC=$qm_spec - export QMAKESPEC - if [ "$QC_VERBOSE" = "Y" ]; then - echo overriding makespec to $qm_spec - fi -fi - -gen_files() { -cat >"$1/modules.cpp" <= 4.2 ------END QCMOD----- -*/ -class qc_qt42 : public ConfObj -{ -public: - qc_qt42(Conf *c) : ConfObj(c) {} - QString name() const { return "Qt >= 4.2"; } - QString shortname() const { return "qt42"; } - bool exec() - { - conf->debug(QString("QT_VERSION = 0x%1").arg(QString::number(QT_VERSION, 16))); - if(QT_VERSION >= 0x040200) - return true; - else - return false; - } -}; -#line 1 "buildmode.qcm" -/* ------BEGIN QCMOD----- -name: buildmode -section: project -arg: release,Build with debugging turned off (default). -arg: debug,Build with debugging turned on. -arg: debug-and-release,Build two versions, with and without debugging turned on (mac only). -arg: no-separate-debug-info,Do not store debug information in a separate file (default for mac). -arg: separate-debug-info,Strip debug information into a separate .debug file (default for non-mac). -arg: no-framework,Do not build as a Mac framework. -arg: framework,Build as a Mac framework (default). ------END QCMOD----- -*/ - -#define QC_BUILDMODE -bool qc_buildmode_release = false; -bool qc_buildmode_debug = false; -bool qc_buildmode_framework = false; -bool qc_buildmode_separate_debug_info = false; - -class qc_buildmode : public ConfObj -{ -public: - qc_buildmode(Conf *c) : ConfObj(c) {} - QString name() const { return "buildmode"; } - QString shortname() const { return "buildmode"; } - - // no output - QString checkString() const { return QString(); } - - bool exec() - { - // first, parse out the options - bool opt_release = false; - bool opt_debug = false; - bool opt_debug_and_release = false; - bool opt_no_framework = false; - bool opt_framework = false; - bool opt_no_separate_debug_info = false; - bool opt_separate_debug_info = false; - - if(conf->getenv("QC_RELEASE") == "Y") - opt_release = true; - if(conf->getenv("QC_DEBUG") == "Y") - opt_debug = true; - if(conf->getenv("QC_DEBUG_AND_RELEASE") == "Y") - opt_debug_and_release = true; - if(conf->getenv("QC_NO_FRAMEWORK") == "Y") - opt_no_framework = true; - if(conf->getenv("QC_FRAMEWORK") == "Y") - opt_framework = true; - if(conf->getenv("QC_NO_SEPARATE_DEBUG_INFO") == "Y") - opt_no_separate_debug_info = true; - if(conf->getenv("QC_SEPARATE_DEBUG_INFO") == "Y") - opt_separate_debug_info = true; - - bool staticmode = false; - if(conf->getenv("QC_STATIC") == "Y") - staticmode = true; - -#ifndef Q_OS_MAC - if(opt_debug_and_release) - { - printf("\\nError: The --debug-and-release option is for mac only.\\n"); - exit(1); - } - - if(opt_framework) - { - printf("\\nError: The --framework option is for mac only.\\n"); - exit(1); - } -#endif - - if(opt_framework && opt_debug) - { - printf("\\nError: Cannot use both --framework and --debug.\\n"); - exit(1); - } - - // sanity check exclusive options - int x; - - // build mode - x = 0; - if(opt_release) - ++x; - if(opt_debug) - ++x; - if(opt_debug_and_release) - ++x; - if(x > 1) - { - printf("\\nError: Use only one of --release, --debug, or --debug-and-release.\\n"); - exit(1); - } - - // framework - if(opt_framework && staticmode) - { - printf("\\nError: Cannot use both --framework and --static.\\n"); - exit(1); - } - - x = 0; - if(opt_no_framework) - ++x; - if(opt_framework) - ++x; - if(x > 1) - { - printf("\\nError: Use only one of --framework or --no-framework.\\n"); - exit(1); - } - - // debug info - x = 0; - if(opt_no_separate_debug_info) - ++x; - if(opt_separate_debug_info) - ++x; - if(x > 1) - { - printf("\\nError: Use only one of --separate-debug-info or --no-separate-debug-info\\n"); - exit(1); - } - - // now process the options - - if(opt_release) - qc_buildmode_release = true; - else if(opt_debug) - qc_buildmode_debug = true; - else if(opt_debug_and_release) - { - qc_buildmode_release = true; - qc_buildmode_debug = true; - } - else // default - qc_buildmode_release = true; - - if(opt_framework) - qc_buildmode_framework = true; - else if(opt_no_framework) - { - // nothing to do - } - else // default - { - if(!staticmode && !opt_debug) - qc_buildmode_framework = true; - } - - if(opt_separate_debug_info) - qc_buildmode_separate_debug_info = true; - else if(opt_no_separate_debug_info) - { - // nothing to do - } - else // default - { -#ifndef Q_OS_MAC - qc_buildmode_separate_debug_info = true; -#endif - } - - // make the string - QStringList opts; - QString other; - - if(qc_buildmode_release && qc_buildmode_debug) - { - opts += "debug_and_release"; - opts += "build_all"; - } - else if(qc_buildmode_release) - opts += "release"; - else // qc_buildmode_debug - opts += "debug"; - -#ifdef Q_OS_MAC - if(qc_buildmode_framework) - opts += "lib_bundle"; -#endif - - if(qc_buildmode_separate_debug_info) - { - opts += "separate_debug_info"; - other += "QMAKE_CFLAGS += -g\\n"; - other += "QMAKE_CXXFLAGS += -g\\n"; - } - - QString str = QString("CONFIG += ") + opts.join(" ") + '\\n'; - conf->addExtra(str); - - if(!other.isEmpty()) - conf->addExtra(other); - - return true; - } -}; -#line 1 "universal.qcm" -/* ------BEGIN QCMOD----- -name: Mac universal binary support -section: project -arg: universal,Build with Mac universal binary support. -arg: mac-sdk=[path],Path to Mac universal SDK (PPC host only). ------END QCMOD----- -*/ - -#define QC_UNIVERSAL -bool qc_universal_enabled = false; -QString qc_universal_sdk; - -//---------------------------------------------------------------------------- -// qc_universal -//---------------------------------------------------------------------------- -class qc_universal : public ConfObj -{ -public: - qc_universal(Conf *c) : ConfObj(c) {} - QString name() const { return "Mac universal binary support"; } - QString shortname() const { return "universal"; } - QString checkString() const { return QString(); } - - bool exec() - { -#ifdef Q_OS_MAC - if(qc_getenv("QC_UNIVERSAL") == "Y") - { - qc_universal_enabled = true; - - QString str = - "contains(QT_CONFIG,x86):contains(QT_CONFIG,ppc) {\\n" - " CONFIG += x86 ppc\\n" - "}\\n"; - - QString sdk = qc_getenv("QC_MAC_SDK"); - if(!sdk.isEmpty()) - { - str += QString("QMAKE_MAC_SDK = %1\\n").arg(sdk); - qc_universal_sdk = sdk; - } - - conf->addExtra(str); - } -#endif - return true; - } -}; -#line 1 "idn.qcm" -/* ------BEGIN QCMOD----- -name: libidn -arg: with-idn-inc=[path],Path to libidn include files -arg: with-idn-lib=[path],Path to libidn library or framework files ------END QCMOD----- -*/ - -//---------------------------------------------------------------------------- -// qc_idn -//---------------------------------------------------------------------------- -class qc_idn : public ConfObj -{ -public: - qc_idn(Conf *c) : ConfObj(c) {} - QString name() const { return "LibIDN"; } - QString shortname() const { return "libidn"; } - bool exec() - { - QString idn_incdir, idn_libdir; - idn_incdir = conf->getenv("QC_WITH_IDN_INC"); - idn_libdir = conf->getenv("QC_WITH_IDN_LIB"); - - if (!idn_incdir.isEmpty() || !idn_libdir.isEmpty()) { // prefer this if given - if ((!idn_incdir.isEmpty() && conf->checkHeader(idn_incdir, "stringprep.h")) || - (idn_incdir.isEmpty() && conf->findHeader("stringprep.h", QStringList(), &idn_incdir))) { - conf->addIncludePath(idn_incdir); - } else { - printf("Headers not found!\\n"); - return false; - } - - if((!idn_libdir.isEmpty() && conf->checkLibrary(idn_libdir, "idn")) || - (idn_libdir.isEmpty() && conf->findLibrary("idn", &idn_libdir))) { - conf->addLib(idn_libdir.isEmpty()? "-lidn" : QString("-L%1 -lidn").arg(idn_libdir)); - } else { - printf("Libraries not found!\\n"); - return false; - } - return true; - } - - QStringList incs; - QString version, libs, other; - if(conf->findPkgConfig("libidn", VersionAny, QString::null, &version, &incs, &libs, &other)) - { - for(int n = 0; n < incs.count(); ++n) - conf->addIncludePath(incs[n]); - if(!libs.isEmpty()) - conf->addLib(libs); - return true; - } - - return false; - } -}; -#line 1 "qca.qcm" -/* ------BEGIN QCMOD----- -name: QCA >= 2.0 -arg: with-qca-inc=[path],Path to QCA include files -arg: with-qca-lib=[path],Path to QCA library or framework files ------END QCMOD----- -*/ - -// adapted from crypto.prf -static QString internal_crypto_prf(const QString &incdir, const QString &libdir, const QString &frameworkdir) -{ - QString out = QString( -"QCA_INCDIR = %1\\n" -"QCA_LIBDIR = %2\\n" -"QMAKE_RPATHDIR = %2\\n" -"QCA_FRAMEWORKDIR = %3\\n" -"\\n" -"CONFIG *= qt\\n" -"\\n" -"LINKAGE =\\n" -"QCA_NAME = qca-qt5\\n" -"\\n" -"!isEmpty(QCA_FRAMEWORKDIR): {\\n" -" framework_dir = \$\$QCA_FRAMEWORKDIR\\n" -" exists(\$\$framework_dir/\$\${QCA_NAME}.framework) {\\n" -" #QMAKE_FRAMEWORKPATH *= \$\$framework_dir\\n" -" LIBS *= -F\$\$framework_dir\\n" -" INCLUDEPATH += \$\$framework_dir/\$\${QCA_NAME}.framework/Headers\\n" -" LINKAGE = -framework \$\${QCA_NAME}\\n" -" }\\n" -"}\\n" -"\\n" -"# else, link normally\\n" -"isEmpty(LINKAGE) {\\n" -" !isEmpty(QCA_INCDIR):INCLUDEPATH += \$\$QCA_INCDIR/QtCrypto\\n" -" !isEmpty(QCA_LIBDIR):LIBS += -L\$\$QCA_LIBDIR\\n" -" LINKAGE = -l\$\${QCA_NAME}\\n" -" CONFIG(debug, debug|release) {\\n" -" windows:LINKAGE = -l\$\${QCA_NAME}d\\n" -" mac:LINKAGE = -l\$\${QCA_NAME}_debug\\n" -" }\\n" -"}\\n" -"\\n" -"LIBS += \$\$LINKAGE\\n" - ).arg(incdir, libdir, frameworkdir); - return out; -} - -// set either libdir or frameworkdir, but not both -static bool qca_try(Conf *conf, const QString &incdir, const QString &libdir, const QString &frameworkdir, bool release, bool debug, QString *_prf) -{ - QString proextra; - QString prf; - if (!incdir.isEmpty() || !libdir.isEmpty() || !frameworkdir.isEmpty()) { - prf = internal_crypto_prf(conf->escapePath(incdir), conf->escapePath(libdir), frameworkdir); - } else { - prf = "CONFIG += crypto\\n"; - } - proextra = - "CONFIG += qt\\n" - "CONFIG -= debug_and_release debug release\\n" - "QT -= gui\\n"; - proextra += prf; - - QString str = - "#include \\n" - "\\n" - "int main()\\n" - "{\\n" - " unsigned long x = QCA_VERSION;\\n" - " if(x >= 0x020000 && x < 0x030000) return 0; else return 1;\\n" - "}\\n"; - - // test desired versions, potentially both release and debug - - if(release) - { - int ret; - if(!conf->doCompileAndLink(str, QStringList(), QString(), proextra + "CONFIG += release\\n", &ret) || ret != 0) - return false; - } - - if(debug) - { - int ret; - if(!conf->doCompileAndLink(str, QStringList(), QString(), proextra + "CONFIG += debug\\n", &ret) || ret != 0) - return false; - } - - *_prf = prf; - return true; -} - -static bool qca_try_lib(Conf *conf, const QString &incdir, const QString &libdir, bool release, bool debug, QString *prf) -{ - return qca_try(conf, incdir, libdir, QString(), release, debug, prf) || - qca_try(conf, incdir + "/Qca-qt5", libdir, QString(), release, debug, prf); -} - -static bool qca_try_framework(Conf *conf, const QString &frameworkdir, bool release, bool debug, QString *prf) -{ - return qca_try(conf, QString(), QString(), frameworkdir, release, debug, prf); -} - -static bool qca_try_ext_prf(Conf *conf, bool release, bool debug, QString *prf) -{ - return qca_try(conf, QString(), QString(), QString(), release, debug, prf); -} - -//---------------------------------------------------------------------------- -// qc_qca -//---------------------------------------------------------------------------- -class qc_qca : public ConfObj -{ -public: - qc_qca(Conf *c) : ConfObj(c) {} - QString name() const { return "QCA >= 2.0"; } - QString shortname() const { return "qca"; } - bool exec() - { - // get the build mode -#ifdef QC_BUILDMODE - bool release = qc_buildmode_release; - bool debug = qc_buildmode_debug; -#else - // else, default to just release mode - bool release = true; - bool debug = false; -#endif - - QString qca_incdir, qca_libdir, qca_crypto_prf; - qca_incdir = conf->getenv("QC_WITH_QCA_INC"); - qca_libdir = conf->getenv("QC_WITH_QCA_LIB"); - -#if defined(Q_OS_MAC) - if(!qca_libdir.isEmpty() && qca_try_framework(conf, qca_libdir, release, debug, &qca_crypto_prf)) - { - conf->addExtra(qca_crypto_prf); - return true; - } -#endif - - if(!qca_incdir.isEmpty() && !qca_libdir.isEmpty() && qca_try_lib(conf, qca_incdir, qca_libdir, release, debug, &qca_crypto_prf)) - { - conf->addExtra(qca_crypto_prf); - return true; - } - - if (qca_try_ext_prf(conf, release, debug, &qca_crypto_prf)) - { - conf->addExtra(qca_crypto_prf); - return true; - } - - QStringList incs; - QString version, libs, other; - - if(conf->findPkgConfig("qca2-qt5", VersionMin, "2.0.0", &version, &incs, &libs, &other)) - { - for(int n = 0; n < incs.count(); ++n) - conf->addIncludePath(incs[n]); - if(!libs.isEmpty()) - conf->addLib(libs); - return true; - } - - QStringList prefixes; -#ifndef Q_OS_WIN - prefixes += "/usr"; - prefixes += "/usr/local"; -#endif - QString prefix = conf->getenv("PREFIX"); - if (!prefix.isEmpty()) { - prefixes += prefix; - } - - for(int n = 0; n < prefixes.count(); ++n) - { - const QString &prefix = prefixes[n]; - if(qca_try_lib(conf, prefix + "/include", prefix + "/lib", release, debug, &qca_crypto_prf)) - { - conf->addExtra(qca_crypto_prf); - return true; - } - } - - return false; - } -}; -#line 1 "zlib.qcm" -/* ------BEGIN QCMOD----- -name: zlib -arg: with-zlib-inc=[path],Path to zlib include files -arg: with-zlib-lib=[path],Path to zlib library files ------END QCMOD----- -*/ - -//---------------------------------------------------------------------------- -// qc_zlib -//---------------------------------------------------------------------------- -class qc_zlib : public ConfObj -{ -public: - qc_zlib(Conf *c) : ConfObj(c) {} - QString name() const { return "zlib"; } - QString shortname() const { return "zlib"; } - bool exec() - { - QString inc, lib; - QString s; - - s = conf->getenv("QC_WITH_ZLIB_INC"); - if(!s.isEmpty()) { - if(!conf->checkHeader(s, "zlib.h")) - return false; - inc = s; - } - else { - if(!conf->findHeader("zlib.h", QStringList(), &s)) - return false; - inc = s; - } - - s = conf->getenv("QC_WITH_ZLIB_LIB"); - if(!s.isEmpty()) { - if(!conf->checkLibrary(s, "z")) - return false; - lib = s; - } - else { - if(!conf->findLibrary("z", &s)) - return false; - lib = s; - } - - if(!inc.isEmpty()) - conf->addIncludePath(inc); - if(!lib.isEmpty()) - conf->addLib(QString("-L") + s); - conf->addLib("-lz"); - - return true; - } -}; -#line 1 "extra.qcm" -/* ------BEGIN QCMOD----- -name: extra -section: project -arg: enable-tests,Build examples and unittests. ------END QCMOD----- -*/ - -class qc_extra : public ConfObj -{ -public: - qc_extra(Conf *c) : ConfObj(c) {} - QString name() const { return "extra"; } - QString shortname() const { return "extra"; } - - // no output - QString checkString() const { return QString(); } - - bool exec() - { - QString str; - QFile f; - - if(conf->getenv("QC_ENABLE_TESTS") == "Y") - str += "CONFIG += iris_tests\\n"; - - conf->addExtra(str); - - bool release = true; - bool debug = false; - bool debug_info = false; - bool universal = false; - QString sdk; - -#ifdef QC_BUILDMODE - release = qc_buildmode_release; - debug = qc_buildmode_debug; - debug_info = qc_buildmode_separate_debug_info; -#endif - -#ifdef QC_UNIVERSAL - universal = qc_universal_enabled; - sdk = qc_universal_sdk; -#endif - - // write confapp_unix.pri - str = QString(); - QString var = conf->getenv("BINDIR"); - if(!var.isEmpty()) - str += QString("BINDIR = %1\\n").arg(var); - if(debug) // debug or debug-and-release - str += QString("CONFIG += debug\\n"); - else // release - str += QString("CONFIG += release\\n"); - if(debug_info) - { - str += QString("CONFIG += separate_debug_info\\n"); - str += "QMAKE_CFLAGS += -g\\n"; - str += "QMAKE_CXXFLAGS += -g\\n"; - } - if(universal) - { - str += - "contains(QT_CONFIG,x86):contains(QT_CONFIG,ppc) {\\n" - " CONFIG += x86 ppc\\n" - "}\\n"; - - if(!sdk.isEmpty()) - str += QString("QMAKE_MAC_SDK = %1\\n").arg(sdk); - } -#ifdef QC_QCA - if(!qc_qca_procode.isEmpty()) - str += qc_qca_procode; -#endif - f.setFileName("confapp_unix.pri"); - if(f.open(QFile::WriteOnly | QFile::Truncate)) - f.write(str.toLatin1()); - f.close(); - - return true; - } -}; -#line 1 "qjdns.qcm" -/* ------BEGIN QCMOD----- -name: jdns -arg: with-qjdns-inc=[path],Path to QJDns include files -arg: with-qjdns-lib=[path],Path to QJDns library files ------END QCMOD----- -*/ - -//---------------------------------------------------------------------------- -// qc_qjdns -//---------------------------------------------------------------------------- -class qc_qjdns : public ConfObj -{ -public: - qc_qjdns(Conf *c) : ConfObj(c) {} - QString name() const { return "QJDns"; } - QString shortname() const { return "qjdns"; } - QString resultString() const - { -#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) - return "Disabled for Qt5 and above"; -#else - return ConfObj::resultString(); -#endif - } - bool exec() - { -#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) - return true; // hack. TODO: figure out how to force jdns -#endif - conf->addExtra("CONFIG += need_jdns"); -#if defined Q_OS_WIN || defined Q_OS_MAC - // HACK: on Windows and Mac OS X, always use psi's bundled qjdns - conf->addExtra("CONFIG += iris-qjdns"); - return true; -#else - QStringList incs; - QString version, libs, other; - QString s; - -#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) - bool found = conf->findPkgConfig("qjdns-qt5", VersionMin, "2.0.3", &version, &incs, &libs, &other); -#else - bool found = conf->findPkgConfig("qjdns-qt4", VersionMin, "2.0.3", &version, &incs, &libs, &other); -#endif - if(!found && !conf->findPkgConfig("qjdns", VersionMin, "2.0.0", &version, &incs, &libs, &other)) { - s = conf->getenv("QC_WITH_QJDNS_INC"); - if ((!s.isEmpty() && conf->checkHeader(s, "qjdns.h")) || - (s.isEmpty() && conf->findHeader("qjdns.h", QStringList(), &s))) { - incs.append(s); - } - - s = conf->getenv("QC_WITH_QJDNS_LIB"); - if((!s.isEmpty() && conf->checkLibrary(s, "qjdns")) || - (s.isEmpty() && conf->findLibrary("qjdns", &s))) { - libs = s.isEmpty()? "-lqjdns -ljdns" : QString("-L%1 -lqjdns -ljdns").arg(s); - } - } - - if (!incs.isEmpty() && !libs.isEmpty()) { - foreach(const QString &inc, incs) { - conf->addIncludePath(inc); - } - conf->addLib(libs); - conf->addExtra("CONFIG += ext-qjdns"); - } - - return true; -#endif - } -}; - -EOT -cat >"$1/modules_new.cpp" <required = true; - o->disabled = false; - o = new qc_buildmode(conf); - o->required = true; - o->disabled = false; - o = new qc_universal(conf); - o->required = true; - o->disabled = false; - o = new qc_idn(conf); - o->required = true; - o->disabled = false; - o = new qc_qca(conf); - o->required = true; - o->disabled = false; - o = new qc_zlib(conf); - o->required = true; - o->disabled = false; - o = new qc_extra(conf); - o->required = true; - o->disabled = false; - o = new qc_qjdns(conf); - o->required = true; - o->disabled = false; - -EOT -cat >"$1/conf4.h" < - -class Conf; - -enum VersionMode { VersionMin, VersionExact, VersionMax, VersionAny }; - -// ConfObj -// -// Subclass ConfObj to create a new configuration module. -class ConfObj -{ -public: - Conf *conf; - bool required; - bool disabled; - bool success; - - ConfObj(Conf *c); - virtual ~ConfObj(); - - // long or descriptive name of what is being checked/performed - // example: "KDE >= 3.3" - virtual QString name() const = 0; - - // short name - // example: "kde" - virtual QString shortname() const = 0; - - // string to display during check - // default: "Checking for [name] ..." - virtual QString checkString() const; - - // string to display after check - // default: "yes" or "no", based on result of exec() - virtual QString resultString() const; - - // this is where the checking code goes - virtual bool exec() = 0; -}; - -// Conf -// -// Interact with this class from your ConfObj to perform detection -// operations and to output configuration parameters. -class Conf -{ -public: - bool debug_enabled; - QString qmake_path; - QString qmakespec; - QString maketool; - - QString DEFINES; - QStringList INCLUDEPATH; - QStringList LIBS; - QString extra; - - QList list; - QMap vars; - - Conf(); - ~Conf(); - - QString getenv(const QString &var); - QString qvar(const QString &s); - QString normalizePath(const QString &s) const; - QString escapeQmakeVar(const QString &s) const; - inline QString escapePath(const QString &s) /* prepare fs path for qmake file */ - { return escapeQmakeVar(normalizePath(s)); } - QString escapedIncludes() const; - QString escapedLibs() const; - - bool exec(); - - void debug(const QString &s); - - QString expandIncludes(const QString &inc); - QString expandLibs(const QString &lib); - - int doCommand(const QString &s, QByteArray *out = 0); - int doCommand(const QString &prog, const QStringList &args, QByteArray *out = 0); - - bool doCompileAndLink(const QString &filedata, const QStringList &incs, const QString &libs, const QString &proextra, int *retcode = 0); - bool checkHeader(const QString &path, const QString &h); - bool findHeader(const QString &h, const QStringList &ext, QString *inc); - bool checkLibrary(const QString &path, const QString &name); - bool findLibrary(const QString &name, QString *lib); - QString findProgram(const QString &prog); - bool findSimpleLibrary(const QString &incvar, const QString &libvar, const QString &incname, const QString &libname, QString *incpath, QString *libs); - bool findFooConfig(const QString &path, QString *version, QStringList *incs, QString *libs, QString *otherflags); - bool findPkgConfig(const QString &name, VersionMode mode, const QString &req_version, QString *version, QStringList *incs, QString *libs, QString *otherflags); - - void addDefine(const QString &str); - void addLib(const QString &str); - void addIncludePath(const QString &str); - void addExtra(const QString &str); - -private: - bool first_debug; - - friend class ConfObj; - void added(ConfObj *o); -}; - -#endif - -EOT -cat >"$1/conf4.cpp" < -#include -#include -#ifndef PATH_MAX -# ifdef Q_OS_WIN -# define PATH_MAX 260 -# endif -#endif - -class MocTestObject : public QObject -{ - Q_OBJECT -public: - MocTestObject() {} -}; - -QString qc_getenv(const QString &var) -{ - char *p = ::getenv(var.toLatin1().data()); - if(!p) - return QString(); - return QString(p); -} - -QStringList qc_pathlist() -{ - QStringList list; - QString path = qc_getenv("PATH"); - if(!path.isEmpty()) - { -#ifdef Q_OS_WIN - list = path.split(';', QString::SkipEmptyParts); -#else - list = path.split(':', QString::SkipEmptyParts); -#endif - } -#ifdef Q_OS_WIN - list.prepend("."); -#endif - return list; -} - -QString qc_findprogram(const QString &prog) -{ - QString out; - QStringList list = qc_pathlist(); - for(int n = 0; n < list.count(); ++n) - { - QFileInfo fi(list[n] + '/' + prog); - if(fi.exists() && fi.isExecutable()) - { - out = fi.filePath(); - break; - } - -#ifdef Q_OS_WIN - // on windows, be sure to look for .exe - if(prog.right(4).toLower() != ".exe") - { - fi = QFileInfo(list[n] + '/' + prog + ".exe"); - if(fi.exists() && fi.isExecutable()) - { - out = fi.filePath(); - break; - } - } -#endif - } - return out; -} - -QString qc_findself(const QString &argv0) -{ -#ifdef Q_OS_WIN - if(argv0.contains('\\\\')) -#else - if(argv0.contains('/')) -#endif - return argv0; - else - return qc_findprogram(argv0); -} - -int qc_run_program_or_command(const QString &prog, const QStringList &args, const QString &command, QByteArray *out, bool showOutput) -{ - if(out) - out->clear(); - - QProcess process; - process.setReadChannel(QProcess::StandardOutput); - - if(!prog.isEmpty()) - process.start(prog, args); - else if(!command.isEmpty()) - process.start(command); - else - return -1; - - if(!process.waitForStarted(-1)) - return -1; - - QByteArray buf; - - while(process.waitForReadyRead(-1)) - { - buf = process.readAllStandardOutput(); - if(out) - out->append(buf); - if(showOutput) - fprintf(stdout, "%s", buf.data()); - - buf = process.readAllStandardError(); - if(showOutput) - fprintf(stderr, "%s", buf.data()); - } - - buf = process.readAllStandardError(); - if(showOutput) - fprintf(stderr, "%s", buf.data()); - - // calling waitForReadyRead will cause the process to eventually be - // marked as finished, so we should not need to separately call - // waitForFinished. however, we will do it anyway just to be safe. - // we won't check the return value since false could still mean - // success (if the process had already been marked as finished). - process.waitForFinished(-1); - - if(process.exitStatus() != QProcess::NormalExit) - return -1; - - return process.exitCode(); -} - -int qc_runcommand(const QString &command, QByteArray *out, bool showOutput) -{ - return qc_run_program_or_command(QString(), QStringList(), command, out, showOutput); -} - -int qc_runprogram(const QString &prog, const QStringList &args, QByteArray *out, bool showOutput) -{ - return qc_run_program_or_command(prog, args, QString(), out, showOutput); -} - -bool qc_removedir(const QString &dirPath) -{ - QDir dir(dirPath); - if(!dir.exists()) - return false; - QStringList list = dir.entryList(); - foreach(QString s, list) - { - if(s == "." || s == "..") - continue; - QFileInfo fi(dir.filePath(s)); - if(fi.isDir()) - { - if(!qc_removedir(fi.filePath())) - return false; - } - else - { - if(!dir.remove(s)) - return false; - } - } - QString dirName = dir.dirName(); - if(!dir.cdUp()) - return false; - if(!dir.rmdir(dirName)) - return false; - return true; -} - -// simple command line arguemnts splitter able to understand quoted args. -// the splitter removes quotes and unescapes symbols as well. -QStringList qc_splitflags(const QString &flags) -{ - QStringList ret; - bool searchStart = true; - bool inQuotes = false; - bool escaped = false; - QChar quote, backslash = QLatin1Char('\\\\'); - QString buf; -#ifdef PATH_MAX - buf.reserve(PATH_MAX); -#endif - for (int i=0; i < flags.length(); i++) { - if (searchStart && flags[i].isSpace()) { - continue; - } - if (searchStart) { - searchStart = false; - buf.clear(); - } - if (escaped) { - buf += flags[i]; - escaped = false; - continue; - } - //buf += flags[i]; - if (inQuotes) { - if (quote == QLatin1Char('\\'')) { - if (flags[i] == quote) { - inQuotes = false; - continue; - } - } else { // we are in double quoetes - if (flags[i] == backslash && i < flags.length() - 1 && - (flags[i+1] == QLatin1Char('"') || flags[i+1] == backslash)) - { - // if next symbol is one of in parentheses ("\\) - escaped = true; - continue; - } - } - } else { - if (flags[i].isSpace()) { - ret.append(buf); - searchStart = true; - buf.clear(); - continue; -#ifndef Q_OS_WIN /* on windows backslash is just a path separator */ - } else if (flags[i] == backslash) { - escaped = true; - continue; // just add next symbol -#endif - } else if (flags[i] == QLatin1Char('\\'') || flags[i] == QLatin1Char('"')) { - inQuotes = true; - quote = flags[i]; - continue; - } - } - buf += flags[i]; - } - if (buf.size()) { - ret.append(buf); - } - return ret; -} - -void qc_splitcflags(const QString &cflags, QStringList *incs, QStringList *otherflags) -{ - incs->clear(); - otherflags->clear(); - - QStringList cflagsList = qc_splitflags(cflags); - for(int n = 0; n < cflagsList.count(); ++n) - { - QString str = cflagsList[n]; - if(str.startsWith("-I")) - { - // we want everything except the leading "-I" - incs->append(str.remove(0, 2)); - } - else - { - // we want whatever is left - otherflags->append(str); - } - } -} - -QString qc_escapeArg(const QString &str) -{ - QString out; - for(int n = 0; n < (int)str.length(); ++n) { - if(str[n] == '-') - out += '_'; - else - out += str[n]; - } - return out; -} - - -QString qc_trim_char(const QString &s, const QChar &ch) -{ - if (s.startsWith(ch) && s.endsWith(ch)) { - return s.mid(1, s.size() - 2); - } - return s; -} - -// removes surrounding quotes, removes trailing slashes, converts to native separators. -// accepts unescaped but possible quoted path -QString qc_normalize_path(const QString &str) -{ - QString path = str.trimmed(); - path = qc_trim_char(path, QLatin1Char('"')); - path = qc_trim_char(path, QLatin1Char('\\'')); - - // It's OK to use unix style'/' pathes on windows Qt handles this without any problems. - // Using Windows-style '\\\\' can leads strange compilation error with MSYS which uses - // unix style. - QLatin1Char nativeSep('/'); -#ifdef Q_OS_WIN - path.replace(QLatin1Char('\\\\'), QLatin1Char('/')); -#endif - // trim trailing slashes - while (path.length() && path[path.length() - 1] == nativeSep) { - path.resize(path.length() - 1); - } - return path; -} - -// escape filesystem path to be added to qmake pro/pri file. -QString qc_escape_string_var(const QString &str) -{ - QString path = str; - path.replace(QLatin1Char('\\\\'), QLatin1String("\\\\\\\\")) - .replace(QLatin1Char('"'), QLatin1String("\\\\\\"")); - if (path.indexOf(QLatin1Char(' ')) != -1) { // has spaces - return QLatin1Char('"') + path + QLatin1Char('"'); - } - return path; -} - -// escapes each path in incs and join into single string suiable for INCLUDEPATH var -QString qc_prepare_includepath(const QStringList &incs) -{ - if (incs.empty()) { - return QString(); - } - QStringList ret; - foreach (const QString &path, incs) { - ret.append(qc_escape_string_var(path)); - } - return ret.join(QLatin1String(" ")); -} - -// escapes each path in libs and to make it suiable for LIBS var -// notice, entries of libs are every single arg for linker. -QString qc_prepare_libs(const QStringList &libs) -{ - if (libs.isEmpty()) { - return QString(); - } - QSet pathSet; - QStringList paths; - QStringList ordered; - foreach (const QString &arg, libs) { - if (arg.startsWith(QLatin1String("-L"))) { - QString path = qc_escape_string_var(arg.mid(2)); - if (!pathSet.contains(path)) { - pathSet.insert(path); - paths.append(path); - } - } else if (arg.startsWith(QLatin1String("-l"))) { - ordered.append(arg); - } else { - ordered.append(qc_escape_string_var(arg)); - } - } - QString ret; - if (paths.size()) { - ret += (QLatin1String(" -L") + paths.join(QLatin1String(" -L")) + QLatin1Char(' ')); - } - return ret + ordered.join(QLatin1String(" ")); -} - -//---------------------------------------------------------------------------- -// ConfObj -//---------------------------------------------------------------------------- -ConfObj::ConfObj(Conf *c) -{ - conf = c; - conf->added(this); - required = false; - disabled = false; - success = false; -} - -ConfObj::~ConfObj() -{ -} - -QString ConfObj::checkString() const -{ - return QString("Checking for %1 ...").arg(name()); -} - -QString ConfObj::resultString() const -{ - if(success) - return "yes"; - else - return "no"; -} - -//---------------------------------------------------------------------------- -// qc_internal_pkgconfig -//---------------------------------------------------------------------------- -class qc_internal_pkgconfig : public ConfObj -{ -public: - QString pkgname, desc; - VersionMode mode; - QString req_ver; - - qc_internal_pkgconfig(Conf *c, const QString &_name, const QString &_desc, VersionMode _mode, const QString &_req_ver) : ConfObj(c) - { - pkgname = _name; - desc = _desc; - mode = _mode; - req_ver = _req_ver; - } - - QString name() const { return desc; } - QString shortname() const { return pkgname; } - - bool exec() - { - QStringList incs; - QString version, libs, other; - if(!conf->findPkgConfig(pkgname, mode, req_ver, &version, &incs, &libs, &other)) - return false; - - for(int n = 0; n < incs.count(); ++n) - conf->addIncludePath(incs[n]); - if(!libs.isEmpty()) - conf->addLib(libs); - //if(!other.isEmpty()) - // conf->addExtra(QString("QMAKE_CFLAGS += %1\\n").arg(other)); - - if(!required) - conf->addDefine("HAVE_PKG_" + qc_escapeArg(pkgname).toUpper()); - - return true; - } -}; - -//---------------------------------------------------------------------------- -// Conf -//---------------------------------------------------------------------------- -Conf::Conf() -{ - // TODO: no more vars? - //vars.insert("QMAKE_INCDIR_X11", new QString(X11_INC)); - //vars.insert("QMAKE_LIBDIR_X11", new QString(X11_LIBDIR)); - //vars.insert("QMAKE_LIBS_X11", new QString(X11_LIB)); - //vars.insert("QMAKE_CC", CC); - - debug_enabled = false; -} - -Conf::~Conf() -{ - qDeleteAll(list); -} - -void Conf::added(ConfObj *o) -{ - list.append(o); -} - -QString Conf::getenv(const QString &var) -{ - return qc_getenv(var); -} - -void Conf::debug(const QString &s) -{ - if(debug_enabled) - { - if(first_debug) - printf("\\n"); - first_debug = false; - printf(" * %s\\n", qPrintable(s)); - } -} - -bool Conf::exec() -{ - for(int n = 0; n < list.count(); ++n) - { - ConfObj *o = list[n]; - - // if this was a disabled-by-default option, check if it was enabled - if(o->disabled) - { - QString v = QString("QC_ENABLE_") + qc_escapeArg(o->shortname()); - if(getenv(v) != "Y") - continue; - } - // and the opposite? - else - { - QString v = QString("QC_DISABLE_") + qc_escapeArg(o->shortname()); - if(getenv(v) == "Y") - continue; - } - - bool output = true; - QString check = o->checkString(); - if(check.isEmpty()) - output = false; - - if(output) - { - printf("%s", check.toLatin1().data()); - fflush(stdout); - } - - first_debug = true; - bool ok = o->exec(); - o->success = ok; - - if(output) - { - QString result = o->resultString(); - if(!first_debug) - printf(" -> %s\\n", result.toLatin1().data()); - else - printf(" %s\\n", result.toLatin1().data()); - } - - if(!ok && o->required) - { - printf("\\nError: need %s!\\n", o->name().toLatin1().data()); - return false; - } - } - return true; -} - -QString Conf::qvar(const QString &s) -{ - return vars.value(s); -} - -QString Conf::normalizePath(const QString &s) const -{ - return qc_normalize_path(s); -} - -QString Conf::escapeQmakeVar(const QString &s) const -{ - return qc_escape_string_var(s); -} - -QString Conf::escapedIncludes() const -{ - return qc_prepare_includepath(INCLUDEPATH); -} - -QString Conf::escapedLibs() const -{ - return qc_prepare_libs(LIBS); -} - -QString Conf::expandIncludes(const QString &inc) -{ - return QLatin1String("-I") + inc; -} - -QString Conf::expandLibs(const QString &lib) -{ - return QLatin1String("-L") + lib; -} - -int Conf::doCommand(const QString &s, QByteArray *out) -{ - debug(QString("[%1]").arg(s)); - int r = qc_runcommand(s, out, debug_enabled); - debug(QString("returned: %1").arg(r)); - return r; -} - -int Conf::doCommand(const QString &prog, const QStringList &args, QByteArray *out) -{ - QString fullcmd = prog; - QString argstr = args.join(QLatin1String(" ")); - if(!argstr.isEmpty()) - fullcmd += QString(" ") + argstr; - debug(QString("[%1]").arg(fullcmd)); - int r = qc_runprogram(prog, args, out, debug_enabled); - debug(QString("returned: %1").arg(r)); - return r; -} - -bool Conf::doCompileAndLink(const QString &filedata, const QStringList &incs, const QString &libs, const QString &proextra, int *retcode) -{ -#ifdef Q_OS_WIN - QDir tmp("qconftemp"); -#else - QDir tmp(".qconftemp"); -#endif - QStringList normalizedLibs; - foreach (const QString &l, qc_splitflags(libs)) { - normalizedLibs.append(qc_normalize_path(l)); - } - - if(!tmp.mkdir("atest")) - { - debug(QString("unable to create atest dir: %1").arg(tmp.absoluteFilePath("atest"))); - return false; - } - QDir dir(tmp.filePath("atest")); - if(!dir.exists()) - { - debug("atest dir does not exist"); - return false; - } - - QString fname = dir.filePath("atest.cpp"); - QString out = "atest"; - QFile f(fname); - if(!f.open(QFile::WriteOnly | QFile::Truncate)) - { - debug("unable to open atest.cpp for writing"); - return false; - } - if(f.write(filedata.toLatin1()) == -1) - { - debug("error writing to atest.cpp"); - return false; - } - f.close(); - - debug(QString("Wrote atest.cpp:\\n%1").arg(filedata)); - - QString pro = QString( - "CONFIG += console\\n" - "CONFIG -= qt app_bundle\\n" - "DESTDIR = \$\$PWD\\n" - "SOURCES += atest.cpp\\n"); - QString inc = qc_prepare_includepath(incs); - if(!inc.isEmpty()) - pro += "INCLUDEPATH += " + inc + '\\n'; - QString escaped_libs = qc_prepare_libs(normalizedLibs); - if(!escaped_libs.isEmpty()) - pro += "LIBS += " + escaped_libs + '\\n'; - pro += proextra; - - fname = dir.filePath("atest.pro"); - f.setFileName(fname); - if(!f.open(QFile::WriteOnly | QFile::Truncate)) - { - debug("unable to open atest.pro for writing"); - return false; - } - if(f.write(pro.toLatin1()) == -1) - { - debug("error writing to atest.pro"); - return false; - } - f.close(); - - debug(QString("Wrote atest.pro:\\n%1").arg(pro)); - - QString oldpath = QDir::currentPath(); - QDir::setCurrent(dir.path()); - - bool ok = false; - int r = doCommand(qmake_path, QStringList() << "atest.pro"); - if(r == 0) - { - r = doCommand(maketool, QStringList()); - if(r == 0) - { - ok = true; - if(retcode) - { - QString runatest = out; -#ifdef Q_OS_UNIX - runatest.prepend("./"); -#endif - *retcode = doCommand(runatest, QStringList()); - } - } - r = doCommand(maketool, QStringList() << "distclean"); - if(r != 0) - debug("error during atest distclean"); - } - - QDir::setCurrent(oldpath); - - // cleanup - //dir.remove("atest.pro"); - //dir.remove("atest.cpp"); - //tmp.rmdir("atest"); - - // remove whole dir since distclean doesn't always work - qc_removedir(tmp.filePath("atest")); - - if(!ok) - return false; - return true; -} - -bool Conf::checkHeader(const QString &path, const QString &h) -{ - return QDir(path).exists(h); -} - -bool Conf::findHeader(const QString &h, const QStringList &ext, QString *inc) -{ - if(checkHeader("/usr/include", h)) - { - *inc = ""; - return true; - } - QStringList dirs; - dirs += "/usr/local/include"; - dirs += ext; - - QString prefix = qc_getenv("PREFIX"); - if (!prefix.isEmpty()) { - prefix += "/include"; - prefix = qc_normalize_path(prefix); - if (!dirs.contains(prefix)) - dirs << prefix; - } - - for(QStringList::ConstIterator it = dirs.begin(); it != dirs.end(); ++it) - { - if(checkHeader(*it, h)) - { - *inc = *it; - return true; - } - } - return false; -} - -bool Conf::checkLibrary(const QString &path, const QString &name) -{ - QString str = - //"#include \\n" - "int main()\\n" - "{\\n" - //" printf(\\"library checker running\\\\\\\\n\\");\\n" - " return 0;\\n" - "}\\n"; - - QString libs; - if(!path.isEmpty()) - libs += QString("-L") + path + ' '; - libs += QString("-l") + name; - if(!doCompileAndLink(str, QStringList(), libs, QString())) - return false; - return true; -} - -bool Conf::findLibrary(const QString &name, QString *lib) -{ - if(checkLibrary("", name)) - { - *lib = ""; - return true; - } - if(checkLibrary("/usr/local/lib", name)) - { - *lib = "/usr/local/lib"; - return true; - } - - QString prefix = qc_getenv("PREFIX"); - if (!prefix.isEmpty()) { - prefix += "/lib"; - prefix = qc_normalize_path(prefix); - if(checkLibrary(prefix, name)) - { - *lib = prefix; - return true; - } - } - - return false; -} - -QString Conf::findProgram(const QString &prog) -{ - return qc_findprogram(prog); -} - -bool Conf::findSimpleLibrary(const QString &incvar, const QString &libvar, const QString &incname, const QString &libname, QString *incpath, QString *libs) -{ - QString inc, lib; - QString s; - - s = getenv(incvar).trimmed(); - if(!s.isEmpty()) { - if(!checkHeader(s, incname)) { - if(debug_enabled) - printf("%s is not found in \\"%s\\"\\n", qPrintable(incname), qPrintable(s)); - return false; - } - inc = s; - } - else { - if(!findHeader(incname, QStringList(), &s)) { - if(debug_enabled) - printf("%s is not found anywhere\\n", qPrintable(incname)); - return false; - } - inc = s; - } - - s = getenv(libvar).trimmed(); - if(!s.isEmpty()) { - if(!checkLibrary(s, libname)) { - if(debug_enabled) - printf("%s is not found in \\"%s\\"\\n", qPrintable(libname), qPrintable(s)); - return false; - } - lib = s; - } - else { - if(!findLibrary(libname, &s)) { - if(debug_enabled) - printf("%s is not found anywhere\\n", qPrintable(libname)); - return false; - } - lib = s; - } - - QString lib_out; - if(!lib.isEmpty()) - lib_out += QString("-L") + s + " "; - lib_out += QString("-l") + libname; - - *incpath = inc; - *libs = lib_out; - return true; -} - -bool Conf::findFooConfig(const QString &path, QString *version, QStringList *incs, QString *libs, QString *otherflags) -{ - QStringList args; - QByteArray out; - int ret; - - args += "--version"; - ret = doCommand(path, args, &out); - if(ret != 0) - return false; - - QString version_out = QString::fromLatin1(out).trimmed(); - - args.clear(); - args += "--libs"; - ret = doCommand(path, args, &out); - if(ret != 0) - return false; - - QString libs_out = QString::fromLatin1(out).trimmed(); - - args.clear(); - args += "--cflags"; - ret = doCommand(path, args, &out); - if(ret != 0) - return false; - - QString cflags = QString::fromLatin1(out).trimmed(); - - QStringList incs_out, otherflags_out; - qc_splitcflags(cflags, &incs_out, &otherflags_out); - - *version = version_out; - *incs = incs_out; - *libs = libs_out; - *otherflags = otherflags_out.join(QLatin1String(" ")); - return true; -} - -bool Conf::findPkgConfig(const QString &name, VersionMode mode, const QString &req_version, QString *version, QStringList *incs, QString *libs, QString *otherflags) -{ - QStringList args; - QByteArray out; - int ret; - - args += name; - args += "--exists"; - ret = doCommand("pkg-config", args, &out); - if(ret != 0) - return false; - - if(mode != VersionAny) - { - args.clear(); - args += name; - if(mode == VersionMin) - args += QString("--atleast-version=%1").arg(req_version); - else if(mode == VersionMax) - args += QString("--max-version=%1").arg(req_version); - else - args += QString("--exact-version=%1").arg(req_version); - ret = doCommand("pkg-config", args, &out); - if(ret != 0) - return false; - } - - args.clear(); - args += name; - args += "--modversion"; - ret = doCommand("pkg-config", args, &out); - if(ret != 0) - return false; - - QString version_out = QString::fromLatin1(out).trimmed(); - - args.clear(); - args += name; - args += "--libs"; - ret = doCommand("pkg-config", args, &out); - if(ret != 0) - return false; - - QString libs_out = QString::fromLatin1(out).trimmed(); - - args.clear(); - args += name; - args += "--cflags"; - ret = doCommand("pkg-config", args, &out); - if(ret != 0) - return false; - - QString cflags = QString::fromLatin1(out).trimmed(); - - QStringList incs_out, otherflags_out; - qc_splitcflags(cflags, &incs_out, &otherflags_out); - - *version = version_out; - *incs = incs_out; - *libs = libs_out; - *otherflags = otherflags_out.join(QLatin1String(" ")); - return true; -} - -void Conf::addDefine(const QString &str) -{ - if(DEFINES.isEmpty()) - DEFINES = str; - else - DEFINES += QString(" ") + str; - debug(QString("DEFINES += %1").arg(str)); -} - -void Conf::addLib(const QString &str) -{ - QStringList libs = qc_splitflags(str); - foreach (const QString &lib, libs) { - if (lib.startsWith("-l")) { - LIBS.append(lib); - } else { - LIBS.append(qc_normalize_path(lib)); // we don't care about -L prefix since normalier does not touch it. - } - } - debug(QString("LIBS += %1").arg(str)); -} - -void Conf::addIncludePath(const QString &str) -{ - INCLUDEPATH.append(qc_normalize_path(str)); - debug(QString("INCLUDEPATH += %1").arg(str)); -} - -void Conf::addExtra(const QString &str) -{ - extra += str + '\\n'; - debug(QString("extra += %1").arg(str)); -} - -//---------------------------------------------------------------------------- -// main -//---------------------------------------------------------------------------- -#include "conf4.moc" - -#ifdef HAVE_MODULES -# include"modules.cpp" -#endif - -int main(int argc, char ** argv) -{ - QCoreApplication app(argc, argv); - Conf *conf = new Conf; - ConfObj *o = 0; - Q_UNUSED(o); -#ifdef HAVE_MODULES -# include"modules_new.cpp" -#endif - - conf->debug_enabled = (qc_getenv("QC_VERBOSE") == "Y") ? true: false; - if(conf->debug_enabled) - printf(" -> ok\\n"); - else - printf("ok\\n"); - - QString confCommand = qc_getenv("QC_COMMAND"); - QString proName = qc_getenv("QC_PROFILE"); - conf->qmake_path = qc_getenv("QC_QMAKE"); - conf->qmakespec = qc_getenv("QC_QMAKESPEC"); - conf->maketool = qc_getenv("QC_MAKETOOL"); - - if(conf->debug_enabled) - printf("conf command: [%s]\\n", qPrintable(confCommand)); - - QString confPath = qc_findself(confCommand); - if(confPath.isEmpty()) - { - printf("Error: cannot find myself; rerun with an absolute path\\n"); - return 1; - } - - QString srcdir = QFileInfo(confPath).absolutePath(); - QString builddir = QDir::current().absolutePath(); - QString proPath = QDir(srcdir).filePath(proName); - - if(conf->debug_enabled) - { - printf("conf path: [%s]\\n", qPrintable(confPath)); - printf("srcdir: [%s]\\n", qPrintable(srcdir)); - printf("builddir: [%s]\\n", qPrintable(builddir)); - printf("profile: [%s]\\n", qPrintable(proPath)); - printf("qmake path: [%s]\\n", qPrintable(conf->qmake_path)); - printf("qmakespec: [%s]\\n", qPrintable(conf->qmakespec)); - printf("make tool: [%s]\\n", qPrintable(conf->maketool)); - printf("\\n"); - } - - bool success = false; - if(conf->exec()) - { - QFile f("conf.pri"); - if(!f.open(QFile::WriteOnly | QFile::Truncate)) - { - printf("Error writing %s\\n", qPrintable(f.fileName())); - return 1; - } - - QString str; - str += "# qconf\\n\\n"; - str += "greaterThan(QT_MAJOR_VERSION, 4):CONFIG += c++11\\n"; - - QString var; - var = qc_normalize_path(qc_getenv("PREFIX")); - if(!var.isEmpty()) - str += QString("PREFIX = %1\\n").arg(var); - var = qc_normalize_path(qc_getenv("BINDIR")); - if(!var.isEmpty()) - str += QString("BINDIR = %1\\n").arg(var); - var = qc_normalize_path(qc_getenv("INCDIR")); - if(!var.isEmpty()) - str += QString("INCDIR = %1\\n").arg(var); - var = qc_normalize_path(qc_getenv("LIBDIR")); - if(!var.isEmpty()) - str += QString("LIBDIR = %1\\n").arg(var); - var = qc_normalize_path(qc_getenv("DATADIR")); - if(!var.isEmpty()) - str += QString("DATADIR = %1\\n").arg(var); - str += '\\n'; - - if(qc_getenv("QC_STATIC") == "Y") - str += "CONFIG += staticlib\\n"; - - // TODO: don't need this? - //str += "QT_PATH_PLUGINS = " + QString(qInstallPathPlugins()) + '\\n'; - - if(!conf->DEFINES.isEmpty()) - str += "DEFINES += " + conf->DEFINES + '\\n'; - if(!conf->INCLUDEPATH.isEmpty()) - str += "INCLUDEPATH += " + qc_prepare_includepath(conf->INCLUDEPATH) + '\\n'; - if(!conf->LIBS.isEmpty()) - str += "LIBS += " + qc_prepare_libs(conf->LIBS) + '\\n'; - if(!conf->extra.isEmpty()) - str += conf->extra; - str += '\\n'; - - var = qc_getenv("QC_EXTRACONF"); - if (!var.isEmpty()) - str += ("\\n# Extra conf from command line\\n" + var + "\\n"); - - QByteArray cs = str.toLatin1(); - f.write(cs); - f.close(); - success = true; - } - QString qmake_path = conf->qmake_path; - QString qmakespec = conf->qmakespec; - delete conf; - - if(!success) - return 1; - - // run qmake on the project file - QStringList args; - if(!qmakespec.isEmpty()) - { - args += "-spec"; - args += qmakespec; - } - args += proPath; - int ret = qc_runprogram(qmake_path, args, 0, true); - if(ret != 0) - return 1; - - return 0; -} - -EOT -cat >"$1/conf4.pro" </dev/null - else - "$qm" conf4.pro >/dev/null - fi - $MAKE clean >/dev/null 2>&1 - $MAKE >../conf.log 2>&1 -) - -if [ "$?" != "0" ]; then - rm -rf ".qconftemp" - if [ "$QC_VERBOSE" = "Y" ]; then - echo " -> fail" - else - echo "fail" - fi - printf "\n" - printf "Reason: There was an error compiling 'conf'. See conf.log for details.\n" - printf "\n" - show_qt_info - if [ "$QC_VERBOSE" = "Y" ]; then - echo "conf.log:" - cat conf.log - fi - exit 1; -fi - -QC_COMMAND=$0 -export QC_COMMAND -QC_PROFILE=iris.pro -export QC_PROFILE -QC_QMAKE="$qm" -export QC_QMAKE -QC_QMAKESPEC=$qm_spec -export QC_QMAKESPEC -QC_MAKETOOL=$MAKE -export QC_MAKETOOL -".qconftemp/conf" -ret="$?" -if [ "$ret" = "1" ]; then - rm -rf ".qconftemp" - echo - exit 1; -else - if [ "$ret" != "0" ]; then - rm -rf ".qconftemp" - if [ "$QC_VERBOSE" = "Y" ]; then - echo " -> fail" - else - echo "fail" - fi - echo - echo "Reason: Unexpected error launching 'conf'" - echo - exit 1; - fi -fi -rm -rf ".qconftemp" - -echo -echo "Good, your configure finished. Now run $MAKE." -echo diff --git a/configure.exe b/configure.exe deleted file mode 100644 index 139fbc1c..00000000 Binary files a/configure.exe and /dev/null differ diff --git a/include/iris/addressresolver.h b/include/iris/addressresolver.h index 4e7e4455..6d771ec4 100644 --- a/include/iris/addressresolver.h +++ b/include/iris/addressresolver.h @@ -1 +1 @@ -#include "../../src/irisnet/corelib/addressresolver.h" +#include "irisnet/corelib/addressresolver.h" diff --git a/include/iris/bsocket.h b/include/iris/bsocket.h index 5c7c906d..dc480185 100644 --- a/include/iris/bsocket.h +++ b/include/iris/bsocket.h @@ -1 +1 @@ -#include "../../src/irisnet/noncore/cutestuff/bsocket.h" +#include "irisnet/noncore/cutestuff/bsocket.h" diff --git a/include/iris/bytestream.h b/include/iris/bytestream.h index cae0e2e9..5d276979 100644 --- a/include/iris/bytestream.h +++ b/include/iris/bytestream.h @@ -1 +1 @@ -#include "../../src/irisnet/noncore/cutestuff/bytestream.h" +#include "irisnet/noncore/cutestuff/bytestream.h" diff --git a/include/iris/dtls.h b/include/iris/dtls.h new file mode 100644 index 00000000..7bfafddc --- /dev/null +++ b/include/iris/dtls.h @@ -0,0 +1 @@ +#include "irisnet/noncore/dtls.h" diff --git a/include/iris/filetransfer.h b/include/iris/filetransfer.h index e17e8ab0..c9e5d59d 100644 --- a/include/iris/filetransfer.h +++ b/include/iris/filetransfer.h @@ -1 +1 @@ -#include "../../src/xmpp/xmpp-im/filetransfer.h" +#include "xmpp/xmpp-im/filetransfer.h" diff --git a/include/iris/httpconnect.h b/include/iris/httpconnect.h index 91306af6..b0840552 100644 --- a/include/iris/httpconnect.h +++ b/include/iris/httpconnect.h @@ -1 +1 @@ -#include "../../src/irisnet/noncore/cutestuff/httpconnect.h" +#include "irisnet/noncore/cutestuff/httpconnect.h" diff --git a/include/iris/httpfileupload.h b/include/iris/httpfileupload.h index 381b857d..efe0bb11 100644 --- a/include/iris/httpfileupload.h +++ b/include/iris/httpfileupload.h @@ -1 +1 @@ -#include "../../src/xmpp/xmpp-im/httpfileupload.h" +#include "xmpp/xmpp-im/httpfileupload.h" diff --git a/include/iris/httppoll.h b/include/iris/httppoll.h index 5ac52eb5..162d67c0 100644 --- a/include/iris/httppoll.h +++ b/include/iris/httppoll.h @@ -1 +1 @@ -#include "../../src/irisnet/noncore/cutestuff/httppoll.h" +#include "irisnet/noncore/cutestuff/httppoll.h" diff --git a/include/iris/ice176.h b/include/iris/ice176.h index 5a6360a5..adc42e8c 100644 --- a/include/iris/ice176.h +++ b/include/iris/ice176.h @@ -1 +1 @@ -#include "../../src/irisnet/noncore/ice176.h" +#include "irisnet/noncore/ice176.h" diff --git a/include/iris/iceabstractstundisco.h b/include/iris/iceabstractstundisco.h new file mode 100644 index 00000000..9f3f6a8a --- /dev/null +++ b/include/iris/iceabstractstundisco.h @@ -0,0 +1 @@ +#include "irisnet/noncore/iceabstractstundisco.h" diff --git a/include/iris/iceagent.h b/include/iris/iceagent.h new file mode 100644 index 00000000..c8739eda --- /dev/null +++ b/include/iris/iceagent.h @@ -0,0 +1 @@ +#include "irisnet/noncore/iceagent.h" diff --git a/include/iris/im.h b/include/iris/im.h index beb8c104..ee83ff9a 100644 --- a/include/iris/im.h +++ b/include/iris/im.h @@ -1 +1 @@ -#include "../../src/xmpp/xmpp-im/im.h" +#include "xmpp/xmpp-im/im.h" diff --git a/include/iris/irisnetexport.h b/include/iris/irisnetexport.h index d169d1ed..9cb9d147 100644 --- a/include/iris/irisnetexport.h +++ b/include/iris/irisnetexport.h @@ -1 +1 @@ -#include "../../src/irisnet/corelib/irisnetexport.h" +#include "irisnet/corelib/irisnetexport.h" diff --git a/include/iris/irisnetglobal.h b/include/iris/irisnetglobal.h index 28a55c73..552439d9 100644 --- a/include/iris/irisnetglobal.h +++ b/include/iris/irisnetglobal.h @@ -1 +1 @@ -#include "../../src/irisnet/corelib/irisnetglobal.h" +#include "irisnet/corelib/irisnetglobal.h" diff --git a/include/iris/irisnetplugin.h b/include/iris/irisnetplugin.h index 2d4b0a3a..86d21917 100644 --- a/include/iris/irisnetplugin.h +++ b/include/iris/irisnetplugin.h @@ -1 +1 @@ -#include "../../src/irisnet/corelib/irisnetplugin.h" +#include "irisnet/corelib/irisnetplugin.h" diff --git a/include/iris/jingle-application.h b/include/iris/jingle-application.h new file mode 100644 index 00000000..754225c1 --- /dev/null +++ b/include/iris/jingle-application.h @@ -0,0 +1 @@ +#include "xmpp/xmpp-im/jingle-application.h" diff --git a/include/iris/jingle-ft.h b/include/iris/jingle-ft.h index a9880b3f..7caced0e 100644 --- a/include/iris/jingle-ft.h +++ b/include/iris/jingle-ft.h @@ -1 +1 @@ -#include "../../src/xmpp/xmpp-im/jingle-ft.h" +#include "xmpp/xmpp-im/jingle-ft.h" diff --git a/include/iris/jingle-ice.h b/include/iris/jingle-ice.h new file mode 100644 index 00000000..6dd3e033 --- /dev/null +++ b/include/iris/jingle-ice.h @@ -0,0 +1 @@ +#include "xmpp/xmpp-im/jingle-ice.h" diff --git a/include/iris/jingle-nstransportslist.h b/include/iris/jingle-nstransportslist.h new file mode 100644 index 00000000..a1f36859 --- /dev/null +++ b/include/iris/jingle-nstransportslist.h @@ -0,0 +1 @@ +#include "xmpp/xmpp-im/jingle-nstransportslist.h" diff --git a/include/iris/jingle-s5b.h b/include/iris/jingle-s5b.h index 725e0a05..b34fceea 100644 --- a/include/iris/jingle-s5b.h +++ b/include/iris/jingle-s5b.h @@ -1 +1 @@ -#include "../../src/xmpp/xmpp-im/jingle-s5b.h" +#include "xmpp/xmpp-im/jingle-s5b.h" diff --git a/include/iris/jingle-session.h b/include/iris/jingle-session.h new file mode 100644 index 00000000..d8c14eea --- /dev/null +++ b/include/iris/jingle-session.h @@ -0,0 +1 @@ +#include "xmpp/xmpp-im/jingle-session.h" diff --git a/include/iris/jingle-transport.h b/include/iris/jingle-transport.h new file mode 100644 index 00000000..6898bd68 --- /dev/null +++ b/include/iris/jingle-transport.h @@ -0,0 +1 @@ +#include "xmpp/xmpp-im/jingle-transport.h" diff --git a/include/iris/jingle.h b/include/iris/jingle.h index 73551781..c4d32a97 100644 --- a/include/iris/jingle.h +++ b/include/iris/jingle.h @@ -1 +1 @@ -#include "../../src/xmpp/xmpp-im/jingle.h" +#include "xmpp/xmpp-im/jingle.h" diff --git a/include/iris/ndns.h b/include/iris/ndns.h index ea5c8b5f..9c270bba 100644 --- a/include/iris/ndns.h +++ b/include/iris/ndns.h @@ -1 +1 @@ -#include "../../src/irisnet/noncore/legacy/ndns.h" +#include "irisnet/noncore/legacy/ndns.h" diff --git a/include/iris/netavailability.h b/include/iris/netavailability.h index 11ef9114..8079c5f6 100644 --- a/include/iris/netavailability.h +++ b/include/iris/netavailability.h @@ -1 +1 @@ -#include "../../src/irisnet/corelib/netavailability.h" +#include "irisnet/corelib/netavailability.h" diff --git a/include/iris/netinterface.h b/include/iris/netinterface.h index 6e0e2333..4d95300d 100644 --- a/include/iris/netinterface.h +++ b/include/iris/netinterface.h @@ -1 +1 @@ -#include "../../src/irisnet/corelib/netinterface.h" +#include "irisnet/corelib/netinterface.h" diff --git a/include/iris/netnames.h b/include/iris/netnames.h index 5ac6a1ee..804950ca 100644 --- a/include/iris/netnames.h +++ b/include/iris/netnames.h @@ -1 +1 @@ -#include "../../src/irisnet/corelib/netnames.h" +#include "irisnet/corelib/netnames.h" diff --git a/include/iris/objectsession.h b/include/iris/objectsession.h index 31bb9009..e98bef86 100644 --- a/include/iris/objectsession.h +++ b/include/iris/objectsession.h @@ -1 +1 @@ -#include "../../src/irisnet/corelib/objectsession.h" +#include "irisnet/corelib/objectsession.h" diff --git a/include/iris/processquit.h b/include/iris/processquit.h index 0f39f170..c92f192f 100644 --- a/include/iris/processquit.h +++ b/include/iris/processquit.h @@ -1 +1 @@ -#include "../../src/irisnet/noncore/processquit.h" +#include "irisnet/noncore/processquit.h" diff --git a/include/iris/s5b.h b/include/iris/s5b.h index 6658c494..d868c241 100644 --- a/include/iris/s5b.h +++ b/include/iris/s5b.h @@ -1 +1 @@ -#include "../../src/xmpp/xmpp-im/s5b.h" +#include "xmpp/xmpp-im/s5b.h" diff --git a/include/iris/socks.h b/include/iris/socks.h index ff5108ab..08cdb678 100644 --- a/include/iris/socks.h +++ b/include/iris/socks.h @@ -1 +1 @@ -#include "../../src/irisnet/noncore/cutestuff/socks.h" +#include "irisnet/noncore/cutestuff/socks.h" diff --git a/include/iris/srvresolver.h b/include/iris/srvresolver.h index 7ece1aba..13ea4671 100644 --- a/include/iris/srvresolver.h +++ b/include/iris/srvresolver.h @@ -1 +1 @@ -#include "../../src/irisnet/noncore/legacy/srvresolver.h" +#include "irisnet/noncore/legacy/srvresolver.h" diff --git a/include/iris/stunallocate.h b/include/iris/stunallocate.h index a1c9a746..4b5d7aa6 100644 --- a/include/iris/stunallocate.h +++ b/include/iris/stunallocate.h @@ -1 +1 @@ -#include "../../src/irisnet/noncore/stunallocate.h" +#include "irisnet/noncore/stunallocate.h" diff --git a/include/iris/stunbinding.h b/include/iris/stunbinding.h index 86f92df1..5370cc52 100644 --- a/include/iris/stunbinding.h +++ b/include/iris/stunbinding.h @@ -1 +1 @@ -#include "../../src/irisnet/noncore/stunbinding.h" +#include "irisnet/noncore/stunbinding.h" diff --git a/include/iris/stunmessage.h b/include/iris/stunmessage.h index 6025e7b8..40ac0d7c 100644 --- a/include/iris/stunmessage.h +++ b/include/iris/stunmessage.h @@ -1 +1 @@ -#include "../../src/irisnet/noncore/stunmessage.h" +#include "irisnet/noncore/stunmessage.h" diff --git a/include/iris/stuntransaction.h b/include/iris/stuntransaction.h index 05d65576..fef1831c 100644 --- a/include/iris/stuntransaction.h +++ b/include/iris/stuntransaction.h @@ -1 +1 @@ -#include "../../src/irisnet/noncore/stuntransaction.h" +#include "irisnet/noncore/stuntransaction.h" diff --git a/include/iris/tcpportreserver.h b/include/iris/tcpportreserver.h new file mode 100644 index 00000000..5fa38181 --- /dev/null +++ b/include/iris/tcpportreserver.h @@ -0,0 +1 @@ +#include "irisnet/noncore/tcpportreserver.h" diff --git a/include/iris/turnclient.h b/include/iris/turnclient.h index 07de0434..1edf8d39 100644 --- a/include/iris/turnclient.h +++ b/include/iris/turnclient.h @@ -1 +1 @@ -#include "../../src/irisnet/noncore/turnclient.h" +#include "irisnet/noncore/turnclient.h" diff --git a/include/iris/udpportreserver.h b/include/iris/udpportreserver.h index d7e93233..50101a1e 100644 --- a/include/iris/udpportreserver.h +++ b/include/iris/udpportreserver.h @@ -1 +1 @@ -#include "../../src/irisnet/noncore/udpportreserver.h" +#include "irisnet/noncore/udpportreserver.h" diff --git a/include/iris/xmpp.h b/include/iris/xmpp.h index cbeb4bab..1ced3d12 100644 --- a/include/iris/xmpp.h +++ b/include/iris/xmpp.h @@ -1 +1 @@ -#include "../../src/xmpp/xmpp-core/xmpp.h" +#include "xmpp/xmpp-core/xmpp.h" diff --git a/include/iris/xmpp_address.h b/include/iris/xmpp_address.h index a6b8a669..2d87c6db 100644 --- a/include/iris/xmpp_address.h +++ b/include/iris/xmpp_address.h @@ -1 +1 @@ -#include "../../src/xmpp/xmpp-im/xmpp_address.h" +#include "xmpp/xmpp-im/xmpp_address.h" diff --git a/include/iris/xmpp_agentitem.h b/include/iris/xmpp_agentitem.h index 4ac1d4ee..fd477af0 100644 --- a/include/iris/xmpp_agentitem.h +++ b/include/iris/xmpp_agentitem.h @@ -1 +1 @@ -#include "../../src/xmpp/xmpp-im/xmpp_agentitem.h" +#include "xmpp/xmpp-im/xmpp_agentitem.h" diff --git a/include/iris/xmpp_bitsofbinary.h b/include/iris/xmpp_bitsofbinary.h index 01b1bf8d..011ebf4a 100644 --- a/include/iris/xmpp_bitsofbinary.h +++ b/include/iris/xmpp_bitsofbinary.h @@ -1 +1 @@ -#include "../../src/xmpp/xmpp-im/xmpp_bitsofbinary.h" +#include "xmpp/xmpp-im/xmpp_bitsofbinary.h" diff --git a/include/iris/xmpp_bytestream.h b/include/iris/xmpp_bytestream.h new file mode 100644 index 00000000..3afd0e3a --- /dev/null +++ b/include/iris/xmpp_bytestream.h @@ -0,0 +1 @@ +#include "xmpp/xmpp-im/xmpp_bytestream.h" diff --git a/include/iris/xmpp_caps.h b/include/iris/xmpp_caps.h index 4dde12ab..7b758b41 100644 --- a/include/iris/xmpp_caps.h +++ b/include/iris/xmpp_caps.h @@ -1 +1 @@ -#include "../../src/xmpp/xmpp-im/xmpp_caps.h" +#include "xmpp/xmpp-im/xmpp_caps.h" diff --git a/include/iris/xmpp_captcha.h b/include/iris/xmpp_captcha.h index 466b4315..1a00797f 100644 --- a/include/iris/xmpp_captcha.h +++ b/include/iris/xmpp_captcha.h @@ -1 +1 @@ -#include "../../src/xmpp/xmpp-im/xmpp_captcha.h" +#include "xmpp/xmpp-im/xmpp_captcha.h" diff --git a/include/iris/xmpp_chatstate.h b/include/iris/xmpp_chatstate.h index a09c9cc6..a4c29fa2 100644 --- a/include/iris/xmpp_chatstate.h +++ b/include/iris/xmpp_chatstate.h @@ -1 +1 @@ -#include "../../src/xmpp/xmpp-im/xmpp_chatstate.h" +#include "xmpp/xmpp-im/xmpp_chatstate.h" diff --git a/include/iris/xmpp_client.h b/include/iris/xmpp_client.h index 8d7b1ac7..44c3397f 100644 --- a/include/iris/xmpp_client.h +++ b/include/iris/xmpp_client.h @@ -1 +1 @@ -#include "../../src/xmpp/xmpp-im/xmpp_client.h" +#include "xmpp/xmpp-im/xmpp_client.h" diff --git a/include/iris/xmpp_clientstream.h b/include/iris/xmpp_clientstream.h index 090f8548..6f2054ba 100644 --- a/include/iris/xmpp_clientstream.h +++ b/include/iris/xmpp_clientstream.h @@ -1 +1 @@ -#include "../../src/xmpp/xmpp-core/xmpp_clientstream.h" +#include "xmpp/xmpp-core/xmpp_clientstream.h" diff --git a/include/iris/xmpp_discoinfotask.h b/include/iris/xmpp_discoinfotask.h index 2e06c7be..38410b48 100644 --- a/include/iris/xmpp_discoinfotask.h +++ b/include/iris/xmpp_discoinfotask.h @@ -1 +1 @@ -#include "../../src/xmpp/xmpp-im/xmpp_discoinfotask.h" +#include "xmpp/xmpp-im/xmpp_discoinfotask.h" diff --git a/include/iris/xmpp_discoitem.h b/include/iris/xmpp_discoitem.h index 76c777f2..4e7a1fa1 100644 --- a/include/iris/xmpp_discoitem.h +++ b/include/iris/xmpp_discoitem.h @@ -1 +1 @@ -#include "../../src/xmpp/xmpp-im/xmpp_discoitem.h" +#include "xmpp/xmpp-im/xmpp_discoitem.h" diff --git a/include/iris/xmpp_encryption.h b/include/iris/xmpp_encryption.h new file mode 100644 index 00000000..895f43b1 --- /dev/null +++ b/include/iris/xmpp_encryption.h @@ -0,0 +1 @@ +#include "xmpp/xmpp-im/xmpp_encryption.h" diff --git a/include/iris/xmpp_encryptionhandler.h b/include/iris/xmpp_encryptionhandler.h index 26e56549..3c7aecd9 100644 --- a/include/iris/xmpp_encryptionhandler.h +++ b/include/iris/xmpp_encryptionhandler.h @@ -1 +1 @@ -#include "../../src/xmpp/xmpp-im/xmpp_encryptionhandler.h" +#include "xmpp/xmpp-im/xmpp_encryptionhandler.h" diff --git a/include/iris/xmpp_features.h b/include/iris/xmpp_features.h index caf6865d..2a06625a 100644 --- a/include/iris/xmpp_features.h +++ b/include/iris/xmpp_features.h @@ -1 +1 @@ -#include "../../src/xmpp/xmpp-im/xmpp_features.h" +#include "xmpp/xmpp-im/xmpp_features.h" diff --git a/include/iris/xmpp_form.h b/include/iris/xmpp_form.h new file mode 100644 index 00000000..cd320c78 --- /dev/null +++ b/include/iris/xmpp_form.h @@ -0,0 +1 @@ +#include "xmpp/xmpp-im/xmpp_form.h" diff --git a/include/iris/xmpp_hash.h b/include/iris/xmpp_hash.h index b27bbbd1..dc081bed 100644 --- a/include/iris/xmpp_hash.h +++ b/include/iris/xmpp_hash.h @@ -1 +1 @@ -#include "../../src/xmpp/xmpp-im/xmpp_hash.h" +#include "xmpp/xmpp-im/xmpp_hash.h" diff --git a/include/iris/xmpp_htmlelement.h b/include/iris/xmpp_htmlelement.h index 95bab250..fdf91719 100644 --- a/include/iris/xmpp_htmlelement.h +++ b/include/iris/xmpp_htmlelement.h @@ -1 +1 @@ -#include "../../src/xmpp/xmpp-im/xmpp_htmlelement.h" +#include "xmpp/xmpp-im/xmpp_htmlelement.h" diff --git a/include/iris/xmpp_httpauthrequest.h b/include/iris/xmpp_httpauthrequest.h index bec230e9..63646a1e 100644 --- a/include/iris/xmpp_httpauthrequest.h +++ b/include/iris/xmpp_httpauthrequest.h @@ -1 +1 @@ -#include "../../src/xmpp/xmpp-im/xmpp_httpauthrequest.h" +#include "xmpp/xmpp-im/xmpp_httpauthrequest.h" diff --git a/include/iris/xmpp_jid.h b/include/iris/xmpp_jid.h index bdb862da..94dc48d7 100644 --- a/include/iris/xmpp_jid.h +++ b/include/iris/xmpp_jid.h @@ -1 +1 @@ -#include "../../src/xmpp/jid/jid.h" +#include "xmpp/jid/jid.h" diff --git a/include/iris/xmpp_liveroster.h b/include/iris/xmpp_liveroster.h index ab67fcbc..bab57092 100644 --- a/include/iris/xmpp_liveroster.h +++ b/include/iris/xmpp_liveroster.h @@ -1 +1 @@ -#include "../../src/xmpp/xmpp-im/xmpp_liveroster.h" +#include "xmpp/xmpp-im/xmpp_liveroster.h" diff --git a/include/iris/xmpp_liverosteritem.h b/include/iris/xmpp_liverosteritem.h index db5c2488..1a2efc15 100644 --- a/include/iris/xmpp_liverosteritem.h +++ b/include/iris/xmpp_liverosteritem.h @@ -1 +1 @@ -#include "../../src/xmpp/xmpp-im/xmpp_liverosteritem.h" +#include "xmpp/xmpp-im/xmpp_liverosteritem.h" diff --git a/include/iris/xmpp_message.h b/include/iris/xmpp_message.h index a75e34fa..29428d99 100644 --- a/include/iris/xmpp_message.h +++ b/include/iris/xmpp_message.h @@ -1 +1 @@ -#include "../../src/xmpp/xmpp-im/xmpp_message.h" +#include "xmpp/xmpp-im/xmpp_message.h" diff --git a/include/iris/xmpp_muc.h b/include/iris/xmpp_muc.h index 6f6fe741..6f0ae4d4 100644 --- a/include/iris/xmpp_muc.h +++ b/include/iris/xmpp_muc.h @@ -1 +1 @@ -#include "../../src/xmpp/xmpp-im/xmpp_muc.h" +#include "xmpp/xmpp-im/xmpp_muc.h" diff --git a/include/iris/xmpp_pubsubitem.h b/include/iris/xmpp_pubsubitem.h index 59f19b3b..780bff84 100644 --- a/include/iris/xmpp_pubsubitem.h +++ b/include/iris/xmpp_pubsubitem.h @@ -1 +1 @@ -#include "../../src/xmpp/xmpp-im/xmpp_pubsubitem.h" +#include "xmpp/xmpp-im/xmpp_pubsubitem.h" diff --git a/include/iris/xmpp_pubsubretraction.h b/include/iris/xmpp_pubsubretraction.h index d89799d4..49151f85 100644 --- a/include/iris/xmpp_pubsubretraction.h +++ b/include/iris/xmpp_pubsubretraction.h @@ -1 +1 @@ -#include "../../src/xmpp/xmpp-im/xmpp_pubsubretraction.h" +#include "xmpp/xmpp-im/xmpp_pubsubretraction.h" diff --git a/include/iris/xmpp_reference.h b/include/iris/xmpp_reference.h new file mode 100644 index 00000000..fa00c922 --- /dev/null +++ b/include/iris/xmpp_reference.h @@ -0,0 +1 @@ +#include "xmpp/xmpp-im/xmpp_reference.h" diff --git a/include/iris/xmpp_resource.h b/include/iris/xmpp_resource.h index 2f5a9979..b0867cf5 100644 --- a/include/iris/xmpp_resource.h +++ b/include/iris/xmpp_resource.h @@ -1 +1 @@ -#include "../../src/xmpp/xmpp-im/xmpp_resource.h" +#include "xmpp/xmpp-im/xmpp_resource.h" diff --git a/include/iris/xmpp_resourcelist.h b/include/iris/xmpp_resourcelist.h index 5771cdd1..27f5d3ad 100644 --- a/include/iris/xmpp_resourcelist.h +++ b/include/iris/xmpp_resourcelist.h @@ -1 +1 @@ -#include "../../src/xmpp/xmpp-im/xmpp_resourcelist.h" +#include "xmpp/xmpp-im/xmpp_resourcelist.h" diff --git a/include/iris/xmpp_roster.h b/include/iris/xmpp_roster.h index 646f2970..27c9b32c 100644 --- a/include/iris/xmpp_roster.h +++ b/include/iris/xmpp_roster.h @@ -1 +1 @@ -#include "../../src/xmpp/xmpp-im/xmpp_roster.h" +#include "xmpp/xmpp-im/xmpp_roster.h" diff --git a/include/iris/xmpp_rosteritem.h b/include/iris/xmpp_rosteritem.h index f4d4cf7b..94c9d809 100644 --- a/include/iris/xmpp_rosteritem.h +++ b/include/iris/xmpp_rosteritem.h @@ -1 +1 @@ -#include "../../src/xmpp/xmpp-im/xmpp_rosteritem.h" +#include "xmpp/xmpp-im/xmpp_rosteritem.h" diff --git a/include/iris/xmpp_rosterx.h b/include/iris/xmpp_rosterx.h index 325b56a6..2d7a9728 100644 --- a/include/iris/xmpp_rosterx.h +++ b/include/iris/xmpp_rosterx.h @@ -1 +1 @@ -#include "../../src/xmpp/xmpp-im/xmpp_rosterx.h" +#include "xmpp/xmpp-im/xmpp_rosterx.h" diff --git a/include/iris/xmpp_serverinfomanager.h b/include/iris/xmpp_serverinfomanager.h index dd79d801..8c5d7f86 100644 --- a/include/iris/xmpp_serverinfomanager.h +++ b/include/iris/xmpp_serverinfomanager.h @@ -1 +1 @@ -#include "../../src/xmpp/xmpp-im/xmpp_serverinfomanager.h" +#include "xmpp/xmpp-im/xmpp_serverinfomanager.h" diff --git a/include/iris/xmpp_stanza.h b/include/iris/xmpp_stanza.h index 4f0b024d..e8cb64f7 100644 --- a/include/iris/xmpp_stanza.h +++ b/include/iris/xmpp_stanza.h @@ -1 +1 @@ -#include "../../src/xmpp/xmpp-core/xmpp_stanza.h" +#include "xmpp/xmpp-core/xmpp_stanza.h" diff --git a/include/iris/xmpp_status.h b/include/iris/xmpp_status.h index 69511013..2c923b42 100644 --- a/include/iris/xmpp_status.h +++ b/include/iris/xmpp_status.h @@ -1 +1 @@ -#include "../../src/xmpp/xmpp-im/xmpp_status.h" +#include "xmpp/xmpp-im/xmpp_status.h" diff --git a/include/iris/xmpp_stream.h b/include/iris/xmpp_stream.h index 5c996666..5ce83e84 100644 --- a/include/iris/xmpp_stream.h +++ b/include/iris/xmpp_stream.h @@ -1 +1 @@ -#include "../../src/xmpp/xmpp-core/xmpp_stream.h" +#include "xmpp/xmpp-core/xmpp_stream.h" diff --git a/include/iris/xmpp_subsets.h b/include/iris/xmpp_subsets.h index be99daa2..5b54bed1 100644 --- a/include/iris/xmpp_subsets.h +++ b/include/iris/xmpp_subsets.h @@ -1 +1 @@ -#include "../../src/xmpp/xmpp-im/xmpp_subsets.h" +#include "xmpp/xmpp-im/xmpp_subsets.h" diff --git a/include/iris/xmpp_task.h b/include/iris/xmpp_task.h index 0f8f4667..ac65e099 100644 --- a/include/iris/xmpp_task.h +++ b/include/iris/xmpp_task.h @@ -1 +1 @@ -#include "../../src/xmpp/xmpp-im/xmpp_task.h" +#include "xmpp/xmpp-im/xmpp_task.h" diff --git a/include/iris/xmpp_tasks.h b/include/iris/xmpp_tasks.h index 6cb81815..0dc03e84 100644 --- a/include/iris/xmpp_tasks.h +++ b/include/iris/xmpp_tasks.h @@ -1 +1 @@ -#include "../../src/xmpp/xmpp-im/xmpp_tasks.h" +#include "xmpp/xmpp-im/xmpp_tasks.h" diff --git a/include/iris/xmpp_thumbs.h b/include/iris/xmpp_thumbs.h index b24861a0..7715981f 100644 --- a/include/iris/xmpp_thumbs.h +++ b/include/iris/xmpp_thumbs.h @@ -1 +1 @@ -#include "../../src/xmpp/xmpp-im/xmpp_thumbs.h" +#include "xmpp/xmpp-im/xmpp_thumbs.h" diff --git a/include/iris/xmpp_url.h b/include/iris/xmpp_url.h index 16b3ac6a..674eb76d 100644 --- a/include/iris/xmpp_url.h +++ b/include/iris/xmpp_url.h @@ -1 +1 @@ -#include "../../src/xmpp/xmpp-im/xmpp_url.h" +#include "xmpp/xmpp-im/xmpp_url.h" diff --git a/include/iris/xmpp_vcard.h b/include/iris/xmpp_vcard.h index 285e6b5c..1210fdb3 100644 --- a/include/iris/xmpp_vcard.h +++ b/include/iris/xmpp_vcard.h @@ -1 +1 @@ -#include "../../src/xmpp/xmpp-im/xmpp_vcard.h" +#include "xmpp/xmpp-im/xmpp_vcard.h" diff --git a/include/iris/xmpp_xdata.h b/include/iris/xmpp_xdata.h index 417e71f7..8316f4c9 100644 --- a/include/iris/xmpp_xdata.h +++ b/include/iris/xmpp_xdata.h @@ -1 +1 @@ -#include "../../src/xmpp/xmpp-im/xmpp_xdata.h" +#include "xmpp/xmpp-im/xmpp_xdata.h" diff --git a/include/iris/xmpp_xmlcommon.h b/include/iris/xmpp_xmlcommon.h index 779b4b59..2c0d42c0 100644 --- a/include/iris/xmpp_xmlcommon.h +++ b/include/iris/xmpp_xmlcommon.h @@ -1 +1 @@ -#include "../../src/xmpp/xmpp-im/xmpp_xmlcommon.h" +#include "xmpp/xmpp-im/xmpp_xmlcommon.h" diff --git a/iris.pc.in b/iris.pc.in index 676104d0..68f1faaa 100644 --- a/iris.pc.in +++ b/iris.pc.in @@ -1,10 +1,10 @@ -prefix=@CMAKE_INSTALL_PREFIX@ -exec_prefix=${prefix} -libdir=@LIB_INSTALL_DIR@ -includedir=@INCLUDE_INSTALL_DIR@/iris +prefix="@CMAKE_INSTALL_PREFIX@" +exec_prefix="${prefix}" +libdir="${prefix}/@CMAKE_INSTALL_LIBDIR@" +includedir="${prefix}/@CMAKE_INSTALL_INCLUDEDIR@/xmpp" Name: iris Description: Iris is a comprehensive library for working with the XMPP protocol -Version: @IRIS_LIB_MAJOR_VERSION@.@IRIS_LIB_MINOR_VERSION@.@IRIS_LIB_PATCH_VERSION@ +Version: @IRIS_LIB_VERSION_STRING@ Libs: -L${libdir} -liris Cflags: -I${includedir} diff --git a/iris.pri b/iris.pri deleted file mode 100644 index bbdba3b3..00000000 --- a/iris.pri +++ /dev/null @@ -1,48 +0,0 @@ -IRIS_BASE = $$PWD -include(common.pri) - -CONFIG *= link_prl # doesn't seems to work but at least it's documented unlike dependp_prl -unix { # most of devs are on Linux anyway - PRE_TARGETDEPS += $$top_iris_builddir/lib/libiris.a - PRE_TARGETDEPS += $$top_iris_builddir/lib/libirisnet.a -} - -INCLUDEPATH += $$IRIS_BASE/include $$IRIS_BASE/include/iris $$IRIS_BASE/src - -iris_bundle:{ - include(src/xmpp/xmpp.pri) -} -else { - isEmpty(top_iris_builddir):top_iris_builddir = $$PWD - LIBS += -L$$top_iris_builddir/lib -liris -} - -# force on all windows, plus qca ordering workaround -windows { - DEFINES += IRISNET_STATIC # from irisnet - LIBS += -L$$top_iris_builddir/lib -lirisnet # from iris - LIBS += -lWs2_32 -lAdvapi32 # from jdns - contains(LIBS, -lqca) { - LIBS -= -lqca - LIBS += -lqca - } - contains(LIBS, -lqcad) { - LIBS -= -lqcad - LIBS += -lqcad - } - - contains(LIBS, -lidn) { - LIBS -= -lidn - LIBS += -lidn - } - - contains(LIBS, -lz) { - LIBS -= -lz - LIBS += -lz - } - - contains(LIBS, -lzlib) { - LIBS -= -lzlib - LIBS += -lzlib - } -} diff --git a/iris.pro b/iris.pro deleted file mode 100644 index f8ba8ae5..00000000 --- a/iris.pro +++ /dev/null @@ -1,20 +0,0 @@ -TEMPLATE = subdirs - -IRIS_BASE = $$PWD -isEmpty(top_iris_builddir):top_iris_builddir = . -include($$top_iris_builddir/conf.pri) - -include(common.pri) - -# do we have a reason to enter the src dir? -appledns:!appledns_bundle:CONFIG *= build_src -!irisnetcore_bundle:CONFIG *= build_src -!iris_bundle:CONFIG *= build_src - -sub_src.subdir = src -sub_tools.subdir = tools -sub_tools.depends = sub_src - -build_src:SUBDIRS += sub_src - -iris_tests:SUBDIRS += sub_tools diff --git a/iris.qc b/iris.qc deleted file mode 100644 index dee09baf..00000000 --- a/iris.qc +++ /dev/null @@ -1,31 +0,0 @@ - - Iris - iris.pro - - - qcm - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/qcm/README b/qcm/README deleted file mode 100644 index 4ca5ce2a..00000000 --- a/qcm/README +++ /dev/null @@ -1,5 +0,0 @@ -qt42, buildmode, and universal modules are all copied from qca. -do not modify them here. - -the qca module is copied from qca, but modified here. we should consider -pushing the changes back. diff --git a/qcm/buildmode.qcm b/qcm/buildmode.qcm deleted file mode 100644 index 74da89bc..00000000 --- a/qcm/buildmode.qcm +++ /dev/null @@ -1,201 +0,0 @@ -/* ------BEGIN QCMOD----- -name: buildmode -section: project -arg: release,Build with debugging turned off (default). -arg: debug,Build with debugging turned on. -arg: debug-and-release,Build two versions, with and without debugging turned on (mac only). -arg: no-separate-debug-info,Do not store debug information in a separate file (default for mac). -arg: separate-debug-info,Strip debug information into a separate .debug file (default for non-mac). -arg: no-framework,Do not build as a Mac framework. -arg: framework,Build as a Mac framework (default). ------END QCMOD----- -*/ - -#define QC_BUILDMODE -bool qc_buildmode_release = false; -bool qc_buildmode_debug = false; -bool qc_buildmode_framework = false; -bool qc_buildmode_separate_debug_info = false; - -class qc_buildmode : public ConfObj -{ -public: - qc_buildmode(Conf *c) : ConfObj(c) {} - QString name() const { return "buildmode"; } - QString shortname() const { return "buildmode"; } - - // no output - QString checkString() const { return QString(); } - - bool exec() - { - // first, parse out the options - bool opt_release = false; - bool opt_debug = false; - bool opt_debug_and_release = false; - bool opt_no_framework = false; - bool opt_framework = false; - bool opt_no_separate_debug_info = false; - bool opt_separate_debug_info = false; - - if(conf->getenv("QC_RELEASE") == "Y") - opt_release = true; - if(conf->getenv("QC_DEBUG") == "Y") - opt_debug = true; - if(conf->getenv("QC_DEBUG_AND_RELEASE") == "Y") - opt_debug_and_release = true; - if(conf->getenv("QC_NO_FRAMEWORK") == "Y") - opt_no_framework = true; - if(conf->getenv("QC_FRAMEWORK") == "Y") - opt_framework = true; - if(conf->getenv("QC_NO_SEPARATE_DEBUG_INFO") == "Y") - opt_no_separate_debug_info = true; - if(conf->getenv("QC_SEPARATE_DEBUG_INFO") == "Y") - opt_separate_debug_info = true; - - bool staticmode = false; - if(conf->getenv("QC_STATIC") == "Y") - staticmode = true; - -#ifndef Q_OS_MAC - if(opt_debug_and_release) - { - printf("\nError: The --debug-and-release option is for mac only.\n"); - exit(1); - } - - if(opt_framework) - { - printf("\nError: The --framework option is for mac only.\n"); - exit(1); - } -#endif - - if(opt_framework && opt_debug) - { - printf("\nError: Cannot use both --framework and --debug.\n"); - exit(1); - } - - // sanity check exclusive options - int x; - - // build mode - x = 0; - if(opt_release) - ++x; - if(opt_debug) - ++x; - if(opt_debug_and_release) - ++x; - if(x > 1) - { - printf("\nError: Use only one of --release, --debug, or --debug-and-release.\n"); - exit(1); - } - - // framework - if(opt_framework && staticmode) - { - printf("\nError: Cannot use both --framework and --static.\n"); - exit(1); - } - - x = 0; - if(opt_no_framework) - ++x; - if(opt_framework) - ++x; - if(x > 1) - { - printf("\nError: Use only one of --framework or --no-framework.\n"); - exit(1); - } - - // debug info - x = 0; - if(opt_no_separate_debug_info) - ++x; - if(opt_separate_debug_info) - ++x; - if(x > 1) - { - printf("\nError: Use only one of --separate-debug-info or --no-separate-debug-info\n"); - exit(1); - } - - // now process the options - - if(opt_release) - qc_buildmode_release = true; - else if(opt_debug) - qc_buildmode_debug = true; - else if(opt_debug_and_release) - { - qc_buildmode_release = true; - qc_buildmode_debug = true; - } - else // default - qc_buildmode_release = true; - - if(opt_framework) - qc_buildmode_framework = true; - else if(opt_no_framework) - { - // nothing to do - } - else // default - { - if(!staticmode && !opt_debug) - qc_buildmode_framework = true; - } - - if(opt_separate_debug_info) - qc_buildmode_separate_debug_info = true; - else if(opt_no_separate_debug_info) - { - // nothing to do - } - else // default - { -#ifndef Q_OS_MAC - qc_buildmode_separate_debug_info = true; -#endif - } - - // make the string - QStringList opts; - QString other; - - if(qc_buildmode_release && qc_buildmode_debug) - { - opts += "debug_and_release"; - opts += "build_all"; - } - else if(qc_buildmode_release) - opts += "release"; - else // qc_buildmode_debug - opts += "debug"; - -#ifdef Q_OS_MAC - if(qc_buildmode_framework) - opts += "lib_bundle"; -#endif - - if(qc_buildmode_separate_debug_info) - { - opts += "separate_debug_info"; - other += "QMAKE_CFLAGS += -g\n"; - other += "QMAKE_CXXFLAGS += -g\n"; - } - - QString str = QString("CONFIG += ") + opts.join(" ") + '\n'; - conf->addExtra(str); - - if(!other.isEmpty()) - conf->addExtra(other); - - return true; - } -}; diff --git a/qcm/extra.qcm b/qcm/extra.qcm deleted file mode 100644 index 8d7b803b..00000000 --- a/qcm/extra.qcm +++ /dev/null @@ -1,82 +0,0 @@ -/* ------BEGIN QCMOD----- -name: extra -section: project -arg: enable-tests,Build examples and unittests. ------END QCMOD----- -*/ - -class qc_extra : public ConfObj -{ -public: - qc_extra(Conf *c) : ConfObj(c) {} - QString name() const { return "extra"; } - QString shortname() const { return "extra"; } - - // no output - QString checkString() const { return QString(); } - - bool exec() - { - QString str; - QFile f; - - if(conf->getenv("QC_ENABLE_TESTS") == "Y") - str += "CONFIG += iris_tests\n"; - - conf->addExtra(str); - - bool release = true; - bool debug = false; - bool debug_info = false; - bool universal = false; - QString sdk; - -#ifdef QC_BUILDMODE - release = qc_buildmode_release; - debug = qc_buildmode_debug; - debug_info = qc_buildmode_separate_debug_info; -#endif - -#ifdef QC_UNIVERSAL - universal = qc_universal_enabled; - sdk = qc_universal_sdk; -#endif - - // write confapp_unix.pri - str = QString(); - QString var = conf->getenv("BINDIR"); - if(!var.isEmpty()) - str += QString("BINDIR = %1\n").arg(var); - if(debug) // debug or debug-and-release - str += QString("CONFIG += debug\n"); - else // release - str += QString("CONFIG += release\n"); - if(debug_info) - { - str += QString("CONFIG += separate_debug_info\n"); - str += "QMAKE_CFLAGS += -g\n"; - str += "QMAKE_CXXFLAGS += -g\n"; - } - if(universal) - { - str += - "contains(QT_CONFIG,x86):contains(QT_CONFIG,ppc) {\n" - " CONFIG += x86 ppc\n" - "}\n"; - - if(!sdk.isEmpty()) - str += QString("QMAKE_MAC_SDK = %1\n").arg(sdk); - } -#ifdef QC_QCA - if(!qc_qca_procode.isEmpty()) - str += qc_qca_procode; -#endif - f.setFileName("confapp_unix.pri"); - if(f.open(QFile::WriteOnly | QFile::Truncate)) - f.write(str.toLatin1()); - f.close(); - - return true; - } -}; diff --git a/qcm/idn.qcm b/qcm/idn.qcm deleted file mode 100644 index 28151246..00000000 --- a/qcm/idn.qcm +++ /dev/null @@ -1,56 +0,0 @@ -/* ------BEGIN QCMOD----- -name: libidn -arg: with-idn-inc=[path],Path to libidn include files -arg: with-idn-lib=[path],Path to libidn library or framework files ------END QCMOD----- -*/ - -//---------------------------------------------------------------------------- -// qc_idn -//---------------------------------------------------------------------------- -class qc_idn : public ConfObj -{ -public: - qc_idn(Conf *c) : ConfObj(c) {} - QString name() const { return "LibIDN"; } - QString shortname() const { return "libidn"; } - bool exec() - { - QString idn_incdir, idn_libdir; - idn_incdir = conf->getenv("QC_WITH_IDN_INC"); - idn_libdir = conf->getenv("QC_WITH_IDN_LIB"); - - if (!idn_incdir.isEmpty() || !idn_libdir.isEmpty()) { // prefer this if given - if ((!idn_incdir.isEmpty() && conf->checkHeader(idn_incdir, "stringprep.h")) || - (idn_incdir.isEmpty() && conf->findHeader("stringprep.h", QStringList(), &idn_incdir))) { - conf->addIncludePath(idn_incdir); - } else { - printf("Headers not found!\n"); - return false; - } - - if((!idn_libdir.isEmpty() && conf->checkLibrary(idn_libdir, "idn")) || - (idn_libdir.isEmpty() && conf->findLibrary("idn", &idn_libdir))) { - conf->addLib(idn_libdir.isEmpty()? "-lidn" : QString("-L%1 -lidn").arg(idn_libdir)); - } else { - printf("Libraries not found!\n"); - return false; - } - return true; - } - - QStringList incs; - QString version, libs, other; - if(conf->findPkgConfig("libidn", VersionAny, QString::null, &version, &incs, &libs, &other)) - { - for(int n = 0; n < incs.count(); ++n) - conf->addIncludePath(incs[n]); - if(!libs.isEmpty()) - conf->addLib(libs); - return true; - } - - return false; - } -}; diff --git a/qcm/qca.qcm b/qcm/qca.qcm deleted file mode 100644 index 25ede5dd..00000000 --- a/qcm/qca.qcm +++ /dev/null @@ -1,189 +0,0 @@ -/* ------BEGIN QCMOD----- -name: QCA >= 2.0 -arg: with-qca-inc=[path],Path to QCA include files -arg: with-qca-lib=[path],Path to QCA library or framework files ------END QCMOD----- -*/ - -// adapted from crypto.prf -static QString internal_crypto_prf(const QString &incdir, const QString &libdir, const QString &frameworkdir) -{ - QString out = QString( -"QCA_INCDIR = %1\n" -"QCA_LIBDIR = %2\n" -"QMAKE_RPATHDIR = %2\n" -"QCA_FRAMEWORKDIR = %3\n" -"\n" -"CONFIG *= qt\n" -"\n" -"LINKAGE =\n" -"QCA_NAME = qca-qt5\n" -"\n" -"!isEmpty(QCA_FRAMEWORKDIR): {\n" -" framework_dir = $$QCA_FRAMEWORKDIR\n" -" exists($$framework_dir/$${QCA_NAME}.framework) {\n" -" #QMAKE_FRAMEWORKPATH *= $$framework_dir\n" -" LIBS *= -F$$framework_dir\n" -" INCLUDEPATH += $$framework_dir/$${QCA_NAME}.framework/Headers\n" -" LINKAGE = -framework $${QCA_NAME}\n" -" }\n" -"}\n" -"\n" -"# else, link normally\n" -"isEmpty(LINKAGE) {\n" -" !isEmpty(QCA_INCDIR):INCLUDEPATH += $$QCA_INCDIR/QtCrypto\n" -" !isEmpty(QCA_LIBDIR):LIBS += -L$$QCA_LIBDIR\n" -" LINKAGE = -l$${QCA_NAME}\n" -" CONFIG(debug, debug|release) {\n" -" windows:LINKAGE = -l$${QCA_NAME}d\n" -" mac:LINKAGE = -l$${QCA_NAME}_debug\n" -" }\n" -"}\n" -"\n" -"LIBS += $$LINKAGE\n" - ).arg(incdir, libdir, frameworkdir); - return out; -} - -// set either libdir or frameworkdir, but not both -static bool qca_try(Conf *conf, const QString &incdir, const QString &libdir, const QString &frameworkdir, bool release, bool debug, QString *_prf) -{ - QString proextra; - QString prf; - if (!incdir.isEmpty() || !libdir.isEmpty() || !frameworkdir.isEmpty()) { - prf = internal_crypto_prf(conf->escapePath(incdir), conf->escapePath(libdir), frameworkdir); - } else { - prf = "CONFIG += crypto\n"; - } - proextra = - "CONFIG += qt\n" - "CONFIG -= debug_and_release debug release\n" - "QT -= gui\n"; - proextra += prf; - - QString str = - "#include \n" - "\n" - "int main()\n" - "{\n" - " unsigned long x = QCA_VERSION;\n" - " if(x >= 0x020000 && x < 0x030000) return 0; else return 1;\n" - "}\n"; - - // test desired versions, potentially both release and debug - - if(release) - { - int ret; - if(!conf->doCompileAndLink(str, QStringList(), QString(), proextra + "CONFIG += release\n", &ret) || ret != 0) - return false; - } - - if(debug) - { - int ret; - if(!conf->doCompileAndLink(str, QStringList(), QString(), proextra + "CONFIG += debug\n", &ret) || ret != 0) - return false; - } - - *_prf = prf; - return true; -} - -static bool qca_try_lib(Conf *conf, const QString &incdir, const QString &libdir, bool release, bool debug, QString *prf) -{ - return qca_try(conf, incdir, libdir, QString(), release, debug, prf) || - qca_try(conf, incdir + "/Qca-qt5", libdir, QString(), release, debug, prf); -} - -static bool qca_try_framework(Conf *conf, const QString &frameworkdir, bool release, bool debug, QString *prf) -{ - return qca_try(conf, QString(), QString(), frameworkdir, release, debug, prf); -} - -static bool qca_try_ext_prf(Conf *conf, bool release, bool debug, QString *prf) -{ - return qca_try(conf, QString(), QString(), QString(), release, debug, prf); -} - -//---------------------------------------------------------------------------- -// qc_qca -//---------------------------------------------------------------------------- -class qc_qca : public ConfObj -{ -public: - qc_qca(Conf *c) : ConfObj(c) {} - QString name() const { return "QCA >= 2.0"; } - QString shortname() const { return "qca"; } - bool exec() - { - // get the build mode -#ifdef QC_BUILDMODE - bool release = qc_buildmode_release; - bool debug = qc_buildmode_debug; -#else - // else, default to just release mode - bool release = true; - bool debug = false; -#endif - - QString qca_incdir, qca_libdir, qca_crypto_prf; - qca_incdir = conf->getenv("QC_WITH_QCA_INC"); - qca_libdir = conf->getenv("QC_WITH_QCA_LIB"); - -#if defined(Q_OS_MAC) - if(!qca_libdir.isEmpty() && qca_try_framework(conf, qca_libdir, release, debug, &qca_crypto_prf)) - { - conf->addExtra(qca_crypto_prf); - return true; - } -#endif - - if(!qca_incdir.isEmpty() && !qca_libdir.isEmpty() && qca_try_lib(conf, qca_incdir, qca_libdir, release, debug, &qca_crypto_prf)) - { - conf->addExtra(qca_crypto_prf); - return true; - } - - if (qca_try_ext_prf(conf, release, debug, &qca_crypto_prf)) - { - conf->addExtra(qca_crypto_prf); - return true; - } - - QStringList incs; - QString version, libs, other; - - if(conf->findPkgConfig("qca2-qt5", VersionMin, "2.0.0", &version, &incs, &libs, &other)) - { - for(int n = 0; n < incs.count(); ++n) - conf->addIncludePath(incs[n]); - if(!libs.isEmpty()) - conf->addLib(libs); - return true; - } - - QStringList prefixes; -#ifndef Q_OS_WIN - prefixes += "/usr"; - prefixes += "/usr/local"; -#endif - QString prefix = conf->getenv("PREFIX"); - if (!prefix.isEmpty()) { - prefixes += prefix; - } - - for(int n = 0; n < prefixes.count(); ++n) - { - const QString &prefix = prefixes[n]; - if(qca_try_lib(conf, prefix + "/include", prefix + "/lib", release, debug, &qca_crypto_prf)) - { - conf->addExtra(qca_crypto_prf); - return true; - } - } - - return false; - } -}; diff --git a/qcm/qjdns.qcm b/qcm/qjdns.qcm deleted file mode 100644 index 10c9a96d..00000000 --- a/qcm/qjdns.qcm +++ /dev/null @@ -1,71 +0,0 @@ -/* ------BEGIN QCMOD----- -name: jdns -arg: with-qjdns-inc=[path],Path to QJDns include files -arg: with-qjdns-lib=[path],Path to QJDns library files ------END QCMOD----- -*/ - -//---------------------------------------------------------------------------- -// qc_qjdns -//---------------------------------------------------------------------------- -class qc_qjdns : public ConfObj -{ -public: - qc_qjdns(Conf *c) : ConfObj(c) {} - QString name() const { return "QJDns"; } - QString shortname() const { return "qjdns"; } - QString resultString() const - { -#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) - return "Disabled for Qt5 and above"; -#else - return ConfObj::resultString(); -#endif - } - bool exec() - { -#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) - return true; // hack. TODO: figure out how to force jdns -#endif - conf->addExtra("CONFIG += need_jdns"); -#if defined Q_OS_WIN || defined Q_OS_MAC - // HACK: on Windows and Mac OS X, always use psi's bundled qjdns - conf->addExtra("CONFIG += iris-qjdns"); - return true; -#else - QStringList incs; - QString version, libs, other; - QString s; - -#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) - bool found = conf->findPkgConfig("qjdns-qt5", VersionMin, "2.0.3", &version, &incs, &libs, &other); -#else - bool found = conf->findPkgConfig("qjdns-qt4", VersionMin, "2.0.3", &version, &incs, &libs, &other); -#endif - if(!found && !conf->findPkgConfig("qjdns", VersionMin, "2.0.0", &version, &incs, &libs, &other)) { - s = conf->getenv("QC_WITH_QJDNS_INC"); - if ((!s.isEmpty() && conf->checkHeader(s, "qjdns.h")) || - (s.isEmpty() && conf->findHeader("qjdns.h", QStringList(), &s))) { - incs.append(s); - } - - s = conf->getenv("QC_WITH_QJDNS_LIB"); - if((!s.isEmpty() && conf->checkLibrary(s, "qjdns")) || - (s.isEmpty() && conf->findLibrary("qjdns", &s))) { - libs = s.isEmpty()? "-lqjdns -ljdns" : QString("-L%1 -lqjdns -ljdns").arg(s); - } - } - - if (!incs.isEmpty() && !libs.isEmpty()) { - foreach(const QString &inc, incs) { - conf->addIncludePath(inc); - } - conf->addLib(libs); - conf->addExtra("CONFIG += ext-qjdns"); - } - - return true; -#endif - } -}; diff --git a/qcm/qt42.qcm b/qcm/qt42.qcm deleted file mode 100644 index 753de7cf..00000000 --- a/qcm/qt42.qcm +++ /dev/null @@ -1,20 +0,0 @@ -/* ------BEGIN QCMOD----- -name: Qt >= 4.2 ------END QCMOD----- -*/ -class qc_qt42 : public ConfObj -{ -public: - qc_qt42(Conf *c) : ConfObj(c) {} - QString name() const { return "Qt >= 4.2"; } - QString shortname() const { return "qt42"; } - bool exec() - { - conf->debug(QString("QT_VERSION = 0x%1").arg(QString::number(QT_VERSION, 16))); - if(QT_VERSION >= 0x040200) - return true; - else - return false; - } -}; diff --git a/qcm/universal.qcm b/qcm/universal.qcm deleted file mode 100644 index 5b6f7c4d..00000000 --- a/qcm/universal.qcm +++ /dev/null @@ -1,49 +0,0 @@ -/* ------BEGIN QCMOD----- -name: Mac universal binary support -section: project -arg: universal,Build with Mac universal binary support. -arg: mac-sdk=[path],Path to Mac universal SDK (PPC host only). ------END QCMOD----- -*/ - -#define QC_UNIVERSAL -bool qc_universal_enabled = false; -QString qc_universal_sdk; - -//---------------------------------------------------------------------------- -// qc_universal -//---------------------------------------------------------------------------- -class qc_universal : public ConfObj -{ -public: - qc_universal(Conf *c) : ConfObj(c) {} - QString name() const { return "Mac universal binary support"; } - QString shortname() const { return "universal"; } - QString checkString() const { return QString(); } - - bool exec() - { -#ifdef Q_OS_MAC - if(qc_getenv("QC_UNIVERSAL") == "Y") - { - qc_universal_enabled = true; - - QString str = - "contains(QT_CONFIG,x86):contains(QT_CONFIG,ppc) {\n" - " CONFIG += x86 ppc\n" - "}\n"; - - QString sdk = qc_getenv("QC_MAC_SDK"); - if(!sdk.isEmpty()) - { - str += QString("QMAKE_MAC_SDK = %1\n").arg(sdk); - qc_universal_sdk = sdk; - } - - conf->addExtra(str); - } -#endif - return true; - } -}; diff --git a/qcm/zlib.qcm b/qcm/zlib.qcm deleted file mode 100644 index 15b4a5f5..00000000 --- a/qcm/zlib.qcm +++ /dev/null @@ -1,55 +0,0 @@ -/* ------BEGIN QCMOD----- -name: zlib -arg: with-zlib-inc=[path],Path to zlib include files -arg: with-zlib-lib=[path],Path to zlib library files ------END QCMOD----- -*/ - -//---------------------------------------------------------------------------- -// qc_zlib -//---------------------------------------------------------------------------- -class qc_zlib : public ConfObj -{ -public: - qc_zlib(Conf *c) : ConfObj(c) {} - QString name() const { return "zlib"; } - QString shortname() const { return "zlib"; } - bool exec() - { - QString inc, lib; - QString s; - - s = conf->getenv("QC_WITH_ZLIB_INC"); - if(!s.isEmpty()) { - if(!conf->checkHeader(s, "zlib.h")) - return false; - inc = s; - } - else { - if(!conf->findHeader("zlib.h", QStringList(), &s)) - return false; - inc = s; - } - - s = conf->getenv("QC_WITH_ZLIB_LIB"); - if(!s.isEmpty()) { - if(!conf->checkLibrary(s, "z")) - return false; - lib = s; - } - else { - if(!conf->findLibrary("z", &s)) - return false; - lib = s; - } - - if(!inc.isEmpty()) - conf->addIncludePath(inc); - if(!lib.isEmpty()) - conf->addLib(QString("-L") + s); - conf->addLib("-lz"); - - return true; - } -}; diff --git a/src/irisnet/CMakeLists.txt b/src/irisnet/CMakeLists.txt index 662c8541..f1efe7eb 100644 --- a/src/irisnet/CMakeLists.txt +++ b/src/irisnet/CMakeLists.txt @@ -1,20 +1,47 @@ -cmake_minimum_required(VERSION 3.1.0) -include_directories( - ${CMAKE_CURRENT_BINARY_DIR} - corelib - noncore - noncore/cutestuff - noncore/legacy - ${QCA_INCLUDES} -) +cmake_minimum_required(VERSION 3.10.0) -set( CMAKE_MODULE_PATH - "${CMAKE_MODULE_PATH}" +list(APPEND CMAKE_MODULE_PATH + "${CMAKE_SOURCE_DIR}/cmake/modules" "${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules" - "${CMAKE_CURRENT_SOURCE_DIR}/../cmake/modules" ) -set(SOURCES +add_library(irisnet STATIC) + +set(IRISNET_CORELIB_HEADERS + corelib/addressresolver.h + corelib/irisnetexport.h + corelib/irisnetglobal.h + corelib/irisnetplugin.h + corelib/netavailability.h + corelib/netinterface.h + corelib/netnames.h + corelib/objectsession.h +) +set(IRISNET_NONCORE_HEADERS + noncore/cutestuff/bsocket.h + noncore/cutestuff/bytestream.h + noncore/cutestuff/httpconnect.h + noncore/cutestuff/httppoll.h + noncore/cutestuff/socks.h + noncore/dtls.h + noncore/ice176.h + noncore/iceabstractstundisco.h + noncore/iceagent.h + noncore/legacy/ndns.h + noncore/legacy/srvresolver.h + noncore/processquit.h + noncore/stunallocate.h + noncore/stunbinding.h + noncore/stunmessage.h + noncore/stuntransaction.h + noncore/tcpportreserver.h + noncore/turnclient.h + noncore/udpportreserver.h +) + +target_sources(irisnet PRIVATE + ${IRISNET_CORELIB_HEADERS} + ${IRISNET_NONCORE_HEADERS} corelib/irisnetglobal.cpp corelib/irisnetplugin.cpp @@ -34,10 +61,12 @@ set(SOURCES corelib/addressresolver.cpp corelib/netavailability.cpp corelib/netinterface.cpp - corelib/netinterface_qtnet.cpp corelib/netnames.cpp corelib/objectsession.cpp + corelib/netinterface_qtname.cpp + corelib/netinterface_qtnet.cpp + noncore/iceagent.cpp noncore/ice176.cpp noncore/icecomponent.cpp noncore/icelocaltransport.cpp @@ -48,67 +77,70 @@ set(SOURCES noncore/stuntransaction.cpp noncore/turnclient.cpp noncore/udpportreserver.cpp + noncore/tcpportreserver.cpp + noncore/dtls.cpp noncore/cutestuff/bsocket.cpp ) if(UNIX) - list(APPEND SOURCES corelib/netinterface_unix.cpp) + target_sources(irisnet PRIVATE + corelib/netinterface_unix.cpp + ) endif() -if(NOT USE_QJDNS) - list(APPEND SOURCES corelib/netinterface_qtname.cpp) -else() - list(APPEND SOURCES corelib/netnames_jdns.cpp) +if(IRIS_ENABLE_JINGLE_SCTP) + target_sources(irisnet PRIVATE + noncore/sctp/SctpAssociation.cpp + noncore/sctp/DepUsrSCTP.cpp + ) endif() -set(HEADERS - corelib/irisnetexport.h - corelib/irisnetglobal.h - corelib/irisnetglobal_p.h - - noncore/stunmessage.h - noncore/stuntypes.h - noncore/stunutil.h - - corelib/addressresolver.h - corelib/irisnetplugin.h - corelib/netavailability.h - corelib/netinterface.h - corelib/netnames.h - corelib/objectsession.h - - noncore/ice176.h - noncore/icecomponent.h - noncore/icelocaltransport.h - noncore/icetransport.h - noncore/iceturntransport.h - noncore/processquit.h - noncore/stunallocate.h - noncore/stunbinding.h - noncore/stuntransaction.h - noncore/turnclient.h - noncore/udpportreserver.h +if(IRIS_BUNDLED_QCA) + add_dependencies(irisnet QcaProject) +endif() - noncore/cutestuff/bsocket.h - noncore/cutestuff/bytestream.h - noncore/cutestuff/httpconnect.h - noncore/cutestuff/httppoll.h - noncore/cutestuff/socks.h +if(IRIS_ENABLE_JINGLE_SCTP AND IRIS_BUNDLED_USRSCTP) + add_dependencies(irisnet UsrSCTPProject) +endif() - noncore/legacy/ndns.h - noncore/legacy/srvresolver.h +target_include_directories(irisnet + PRIVATE + ${iris_SOURCE_DIR}/include/iris + ${iris_SOURCE_DIR}/src + ${Qca_INCLUDE_DIR} + PUBLIC + ${USRSCTP_INCLUDES} ) -add_library(irisnet STATIC ${SOURCES} ${HEADERS} ) +target_compile_definitions(irisnet PRIVATE IRISNET_STATIC HAVE_QTNET) -if(WIN32 AND (SEPARATE_QJDNS OR (NOT USE_QJDNS))) - set(EXTRA_LDFLAGS ws2_32) +if(WIN32) + set(EXTRA_LDFLAGS ws2_32 iphlpapi) endif() -if(NOT USE_QJDNS) - set(QJDns_LIBRARY "") +# UsrSCTP requires pthread +if(NOT WIN32) + find_package(Threads) + target_link_libraries(irisnet PUBLIC Threads::Threads) endif() -target_link_libraries(irisnet ${QJDns_LIBRARY} ${EXTRA_LDFLAGS}) -target_link_libraries(irisnet Qt5::Core Qt5::Network Qt5::Xml ${qca_LIB}) +target_link_libraries(irisnet PUBLIC + ${Qca_LIBRARY} ${USRSCTP_LIBRARY} +) + +if(QT_DEFAULT_MAJOR_VERSION LESS 6) + target_link_libraries(irisnet PUBLIC Qt5::Core Qt5::Network Qt5::Xml) +else() + target_link_libraries(irisnet PUBLIC Qt6::Core Qt6::Network Qt6::Xml) +endif() +target_link_libraries(irisnet PUBLIC ${EXTRA_LDFLAGS}) + +if(IRIS_ENABLE_INSTALL) + install(FILES ${IRISNET_CORELIB_HEADERS} + DESTINATION ${IRIS_INSTALL_INCLUDEDIR}/irisnet/corelib + ) + install(FILES ${IRISNET_NONCORE_HEADERS} + DESTINATION ${IRIS_INSTALL_INCLUDEDIR}/irisnet/noncore + ) +endif() diff --git a/src/irisnet/appledns/appledns.cpp b/src/irisnet/appledns/appledns.cpp index f0f054ba..80c3cbc0 100644 --- a/src/irisnet/appledns/appledns.cpp +++ b/src/irisnet/appledns/appledns.cpp @@ -17,26 +17,23 @@ */ #include "irisnetplugin.h" +#include "qdnssd.h" #include #include -#include "qdnssd.h" - -// for ntohl -#ifdef Q_OS_WIN -# include +#ifdef Q_OS_WIN // for ntohl +#include #else -# include +#include #endif static QByteArray nameToDottedString(const QByteArray &in) { QByteArray out; - int at = 0; - while(at < in.size()) - { + int at = 0; + while (at < in.size()) { int len = in[at++]; - if(len > 0) + if (len > 0) out += in.mid(at, len); out += '.'; at += len; @@ -44,21 +41,17 @@ static QByteArray nameToDottedString(const QByteArray &in) return out; } -static QMap textsToAttribs(const QList &texts) +static QMap textsToAttribs(const QList &texts) { - QMap out; - foreach(const QByteArray &a, texts) - { - QString key; + QMap out; + for (const QByteArray &a : texts) { + QString key; QByteArray value; - int x = a.indexOf('='); - if(x != -1) - { - key = QString::fromLatin1(a.mid(0, x)); + int x = a.indexOf('='); + if (x != -1) { + key = QString::fromLatin1(a.mid(0, x)); value = a.mid(x + 1); - } - else - { + } else { key = QString::fromLatin1(a); } @@ -67,12 +60,11 @@ static QMap textsToAttribs(const QList &texts) return out; } -static QByteArray attribsToTxtRecord(const QMap &attribs) +static QByteArray attribsToTxtRecord(const QMap &attribs) { - QList texts; - QMapIterator it(attribs); - while(it.hasNext()) - { + QList texts; + QMapIterator it(attribs); + while (it.hasNext()) { it.next(); QByteArray line = it.key().toLatin1() + '=' + it.value(); texts += line; @@ -84,14 +76,14 @@ static QByteArray attribsToTxtRecord(const QMap &attribs) static QList nameToInstanceParts(const QByteArray &name) { // FIXME: improve this parsing... (what about escaping??) - int at = name.indexOf('.'); + int at = name.indexOf('.'); QByteArray sname = name.mid(0, at); ++at; int next = name.indexOf('.', at); ++next; - next = name.indexOf('.', next); - QByteArray stype = name.mid(at, next - at); - at = next + 1; + next = name.indexOf('.', next); + QByteArray stype = name.mid(at, next - at); + at = next + 1; QByteArray sdomain = name.mid(at); QList out; @@ -104,46 +96,40 @@ static QList nameToInstanceParts(const QByteArray &name) static XMPP::NameRecord importQDnsSdRecord(const QDnsSd::Record &in) { XMPP::NameRecord out; - switch(in.rrtype) + switch (in.rrtype) { + case 1: // A { - case 1: // A - { - quint32 *p = (quint32 *)in.rdata.data(); - out.setAddress(QHostAddress(ntohl(*p))); - } - break; + quint32 *p = (quint32 *)in.rdata.data(); + out.setAddress(QHostAddress(ntohl(*p))); + } break; - case 28: // AAAA - { - out.setAddress(QHostAddress((quint8 *)in.rdata.data())); - } - break; - - case 12: // PTR - { - out.setPtr(nameToDottedString(in.rdata)); - } - break; + case 28: // AAAA + { + out.setAddress(QHostAddress((quint8 *)in.rdata.data())); + } break; - case 10: // NULL - { - out.setNull(in.rdata); - } - break; + case 12: // PTR + { + out.setPtr(nameToDottedString(in.rdata)); + } break; - case 16: // TXT - { - QList txtEntries = QDnsSd::parseTxtRecord(in.rdata); - if(txtEntries.isEmpty()) - return out; - out.setTxt(txtEntries); - } - break; + case 10: // NULL + { + out.setNull(in.rdata); + } break; - default: // unsupported - { + case 16: // TXT + { + QList txtEntries = QDnsSd::parseTxtRecord(in.rdata); + if (txtEntries.isEmpty()) return out; - } + out.setTxt(txtEntries); + } break; + + default: // unsupported + { + return out; + } } out.setOwner(in.name); @@ -152,13 +138,9 @@ static XMPP::NameRecord importQDnsSdRecord(const QDnsSd::Record &in) } namespace { - -class QDnsSdDelegate -{ +class QDnsSdDelegate { public: - virtual ~QDnsSdDelegate() - { - } + virtual ~QDnsSdDelegate() { } virtual void dns_queryResult(int id, const QDnsSd::QueryResult &result) { @@ -185,32 +167,26 @@ class QDnsSdDelegate } }; -class IdManager -{ +class IdManager { private: QSet set; - int at; + int at; inline void bump_at() { - if(at == 0x7fffffff) + if (at == 0x7fffffff) at = 0; else ++at; } public: - IdManager() : - at(0) - { - } + IdManager() : at(0) { } int reserveId() { - while(1) - { - if(!set.contains(at)) - { + while (1) { + if (!set.contains(at)) { int id = at; set.insert(id); bump_at(); @@ -221,62 +197,62 @@ class IdManager } } - void releaseId(int id) - { - set.remove(id); - } + void releaseId(int id) { set.remove(id); } }; -} +} // namespace //---------------------------------------------------------------------------- // AppleProvider //---------------------------------------------------------------------------- -class AppleProvider : public XMPP::IrisNetProvider -{ +class AppleProvider : public XMPP::IrisNetProvider { Q_OBJECT Q_INTERFACES(XMPP::IrisNetProvider); + public: - QDnsSd dns; - QHash delegateById; + QDnsSd dns; + QHash delegateById; - AppleProvider() : - dns(this) + AppleProvider() : dns(this) { - connect(&dns, SIGNAL(queryResult(int,QDnsSd::QueryResult)), SLOT(dns_queryResult(int,QDnsSd::QueryResult))); - connect(&dns, SIGNAL(browseResult(int,QDnsSd::BrowseResult)), SLOT(dns_browseResult(int,QDnsSd::BrowseResult))); - connect(&dns, SIGNAL(resolveResult(int,QDnsSd::ResolveResult)), SLOT(dns_resolveResult(int,QDnsSd::ResolveResult))); - connect(&dns, SIGNAL(regResult(int,QDnsSd::RegResult)), SLOT(dns_regResult(int,QDnsSd::RegResult))); + connect(&dns, SIGNAL(queryResult(int, QDnsSd::QueryResult)), SLOT(dns_queryResult(int, QDnsSd::QueryResult))); + connect(&dns, SIGNAL(browseResult(int, QDnsSd::BrowseResult)), + SLOT(dns_browseResult(int, QDnsSd::BrowseResult))); + connect(&dns, SIGNAL(resolveResult(int, QDnsSd::ResolveResult)), + SLOT(dns_resolveResult(int, QDnsSd::ResolveResult))); + connect(&dns, SIGNAL(regResult(int, QDnsSd::RegResult)), SLOT(dns_regResult(int, QDnsSd::RegResult))); } - virtual XMPP::NameProvider *createNameProviderInternet(); - virtual XMPP::NameProvider *createNameProviderLocal(); + virtual XMPP::NameProvider *createNameProviderInternet(); + virtual XMPP::NameProvider *createNameProviderLocal(); virtual XMPP::ServiceProvider *createServiceProvider(); int query(QDnsSdDelegate *p, const QByteArray &name, int qType) { - int id = dns.query(name, qType); + int id = dns.query(name, qType); delegateById[id] = p; return id; } int browse(QDnsSdDelegate *p, const QByteArray &serviceType, const QByteArray &domain) { - int id = dns.browse(serviceType, domain); + int id = dns.browse(serviceType, domain); delegateById[id] = p; return id; } - int resolve(QDnsSdDelegate *p, const QByteArray &serviceName, const QByteArray &serviceType, const QByteArray &domain) + int resolve(QDnsSdDelegate *p, const QByteArray &serviceName, const QByteArray &serviceType, + const QByteArray &domain) { - int id = dns.resolve(serviceName, serviceType, domain); + int id = dns.resolve(serviceName, serviceType, domain); delegateById[id] = p; return id; } - int reg(QDnsSdDelegate *p, const QByteArray &serviceName, const QByteArray &serviceType, const QByteArray &domain, int port, const QByteArray &txtRecord) + int reg(QDnsSdDelegate *p, const QByteArray &serviceName, const QByteArray &serviceType, const QByteArray &domain, + int port, const QByteArray &txtRecord) { - int id = dns.reg(serviceName, serviceType, domain, port, txtRecord); + int id = dns.reg(serviceName, serviceType, domain, port, txtRecord); delegateById[id] = p; return id; } @@ -289,23 +265,19 @@ class AppleProvider : public XMPP::IrisNetProvider void stop_all(QDnsSdDelegate *p) { - QList ids; - QHashIterator it(delegateById); - while(it.hasNext()) - { + QList ids; + QHashIterator it(delegateById); + while (it.hasNext()) { it.next(); - if(it.value() == p) + if (it.value() == p) ids += it.key(); } - foreach(int id, ids) + for (int id : ids) stop(id); } private slots: - void dns_queryResult(int id, const QDnsSd::QueryResult &result) - { - delegateById[id]->dns_queryResult(id, result); - } + void dns_queryResult(int id, const QDnsSd::QueryResult &result) { delegateById[id]->dns_queryResult(id, result); } void dns_browseResult(int id, const QDnsSd::BrowseResult &result) { @@ -317,10 +289,7 @@ private slots: delegateById[id]->dns_resolveResult(id, result); } - void dns_regResult(int id, const QDnsSd::RegResult &result) - { - delegateById[id]->dns_regResult(id, result); - } + void dns_regResult(int id, const QDnsSd::RegResult &result) { delegateById[id]->dns_regResult(id, result); } }; //---------------------------------------------------------------------------- @@ -328,27 +297,20 @@ private slots: //---------------------------------------------------------------------------- // only use this class for a single browse. if you want to browse again, // create a new object. -class AppleBrowse : public QObject, public QDnsSdDelegate -{ +class AppleBrowse : public QObject, public QDnsSdDelegate { Q_OBJECT public: - AppleProvider *global; - int browse_id; + AppleProvider *global; + int browse_id; QList instances; - QHash pendingByQueryId; // waiting for TXT + QHash pendingByQueryId; // waiting for TXT - AppleBrowse(AppleProvider *_global, QObject *parent = 0) : - QObject(parent), - global(_global), - browse_id(-1) + AppleBrowse(AppleProvider *_global, QObject *parent = 0) : QObject(parent), global(_global), browse_id(-1) { connect(this, SIGNAL(unavailable_p(XMPP::ServiceInstance)), SIGNAL(unavailable(XMPP::ServiceInstance))); } - ~AppleBrowse() - { - global->stop_all(this); - } + ~AppleBrowse() { global->stop_all(this); } void browse(const QString &type, const QString &domain) { @@ -368,30 +330,24 @@ class AppleBrowse : public QObject, public QDnsSdDelegate { Q_UNUSED(id); - if(!result.success) - { + if (!result.success) { emit error(); return; } - foreach(const QDnsSd::BrowseEntry &e, result.entries) - { - XMPP::ServiceInstance si(e.serviceName, e.serviceType, e.replyDomain, QMap()); + for (const QDnsSd::BrowseEntry &e : result.entries) { + XMPP::ServiceInstance si(e.serviceName, e.serviceType, e.replyDomain, QMap()); - if(e.added) - { - int query_id = global->query(this, si.name(), 16); // 16 == TXT + if (e.added) { + int query_id = global->query(this, si.name(), 16); // 16 == TXT pendingByQueryId[query_id] = si.name(); - } - else // removed + } else // removed { // emit these queued for SS. no worry of SR since // the browse operation is not cancellable. - for(int n = 0; n < instances.count(); ++n) - { + for (int n = 0; n < instances.count(); ++n) { const XMPP::ServiceInstance &i = instances[n]; - if(i.name() == si.name()) - { + if (i.name() == si.name()) { emit unavailable_p(i); instances.removeAt(n); --n; // adjust position @@ -403,8 +359,7 @@ class AppleBrowse : public QObject, public QDnsSdDelegate virtual void dns_queryResult(int id, const QDnsSd::QueryResult &result) { - if(!result.success) - { + if (!result.success) { // if we get here, then it means we received a browse // entry, but could not fetch its TXT record. if // that happens, cancel the query and drop the @@ -418,14 +373,13 @@ class AppleBrowse : public QObject, public QDnsSdDelegate Q_ASSERT(!result.records.isEmpty()); // only the first entry matters, and it must be an added TXT - if(!result.records[0].added || result.records[0].rrtype != 16) + if (!result.records[0].added || result.records[0].rrtype != 16) return; // we only care about one answer - QByteArray name = pendingByQueryId[id]; + QByteArray name = pendingByQueryId[id]; QList parts = nameToInstanceParts(name); - if(parts.isEmpty()) - { + if (parts.isEmpty()) { // TODO: error Q_ASSERT(0); } @@ -436,19 +390,18 @@ class AppleBrowse : public QObject, public QDnsSdDelegate XMPP::NameRecord rec = importQDnsSdRecord(result.records[0]); // bad answer? - if(rec.isNull()) + if (rec.isNull()) return; - QMap attribs = textsToAttribs(rec.texts()); + QMap attribs = textsToAttribs(rec.texts()); // FIXME: conversion/escaping? - XMPP::ServiceInstance si(QString::fromUtf8(parts[0]), QString::fromUtf8(parts[1]), QString::fromUtf8(parts[2]), attribs); + XMPP::ServiceInstance si(QString::fromUtf8(parts[0]), QString::fromUtf8(parts[1]), QString::fromUtf8(parts[2]), + attribs); // does qdnssd guarantee we won't receive dups? bool found = false; - foreach(const XMPP::ServiceInstance &i, instances) - { - if(i.name() == si.name()) - { + for (const XMPP::ServiceInstance &i : instances) { + if (i.name() == si.name()) { found = true; break; } @@ -465,50 +418,39 @@ class AppleBrowse : public QObject, public QDnsSdDelegate //---------------------------------------------------------------------------- // only use this class for a single lookup. if you want to lookup again, // create a new object. -class AppleBrowseLookup : public QObject, public QDnsSdDelegate -{ +class AppleBrowseLookup : public QObject, public QDnsSdDelegate { Q_OBJECT public: - AppleProvider *global; - int resolve_id; + AppleProvider *global; + int resolve_id; XMPP::NameResolver nameResolverAaaa; XMPP::NameResolver nameResolverA; - bool activeAaaa; - bool activeA; - QTimer waitTimer; - QByteArray host; - QHostAddress addr4; - QHostAddress addr6; - int port; + bool activeAaaa; + bool activeA; + QTimer waitTimer; + QByteArray host; + QHostAddress addr4; + QHostAddress addr6; + int port; AppleBrowseLookup(AppleProvider *_global, QObject *parent = 0) : - QObject(parent), - global(_global), - resolve_id(-1), - nameResolverAaaa(this), - nameResolverA(this), - activeAaaa(false), - activeA(false), - waitTimer(this) + QObject(parent), global(_global), resolve_id(-1), nameResolverAaaa(this), nameResolverA(this), + activeAaaa(false), activeA(false), waitTimer(this) { connect(&nameResolverAaaa, SIGNAL(resultsReady(QList)), - SLOT(nameAaaa_resultsReady(QList))); + SLOT(nameAaaa_resultsReady(QList))); connect(&nameResolverAaaa, SIGNAL(error(XMPP::NameResolver::Error)), - SLOT(nameAaaa_error(XMPP::NameResolver::Error))); + SLOT(nameAaaa_error(XMPP::NameResolver::Error))); connect(&nameResolverA, SIGNAL(resultsReady(QList)), - SLOT(nameA_resultsReady(QList))); - connect(&nameResolverA, SIGNAL(error(XMPP::NameResolver::Error)), - SLOT(nameA_error(XMPP::NameResolver::Error))); + SLOT(nameA_resultsReady(QList))); + connect(&nameResolverA, SIGNAL(error(XMPP::NameResolver::Error)), SLOT(nameA_error(XMPP::NameResolver::Error))); connect(&waitTimer, SIGNAL(timeout()), SLOT(waitTimer_timeout())); waitTimer.setSingleShot(true); } - ~AppleBrowseLookup() - { - global->stop_all(this); - } + ~AppleBrowseLookup() { global->stop_all(this); } void resolve(const QByteArray &instance, const QByteArray &type, const QByteArray &domain) { @@ -526,8 +468,7 @@ class AppleBrowseLookup : public QObject, public QDnsSdDelegate // there is only one response, so deregister global->stop(id); - if(!result.success) - { + if (!result.success) { emit error(); return; } @@ -536,7 +477,7 @@ class AppleBrowseLookup : public QObject, public QDnsSdDelegate port = result.port; activeAaaa = true; - activeA = true; + activeA = true; nameResolverAaaa.start(host, XMPP::NameRecord::Aaaa); nameResolverA.start(host, XMPP::NameRecord::A); waitTimer.start(500); // 500ms cut-off time, take what we have and run @@ -547,7 +488,7 @@ private slots: { // nameresolver guarantees at least one result, and we only // care about the first - addr6 = results[0].address(); + addr6 = results[0].address(); activeAaaa = false; tryDone(); } @@ -563,7 +504,7 @@ private slots: { // nameresolver guarantees at least one result, and we only // care about the first - addr4 = results[0].address(); + addr4 = results[0].address(); activeA = false; tryDone(); } @@ -575,10 +516,7 @@ private slots: tryDone(); } - void waitTimer_timeout() - { - tryDone(); - } + void waitTimer_timeout() { tryDone(); } private: void tryDone() @@ -587,8 +525,7 @@ private slots: // results, or if the wait timer ends and we have at least // one result - if(!activeAaaa && !activeA && addr6.isNull() && addr4.isNull()) - { + if (!activeAaaa && !activeA && addr6.isNull() && addr4.isNull()) { nameResolverAaaa.stop(); nameResolverA.stop(); waitTimer.stop(); @@ -597,15 +534,14 @@ private slots: return; } - if(!waitTimer.isActive() && (!addr6.isNull() || !addr4.isNull())) - { + if (!waitTimer.isActive() && (!addr6.isNull() || !addr4.isNull())) { nameResolverAaaa.stop(); nameResolverA.stop(); QList out; - if(!addr4.isNull()) + if (!addr4.isNull()) out += addr4; - if(!addr6.isNull()) + if (!addr6.isNull()) out += addr6; emit finished(out, port); } @@ -615,27 +551,16 @@ private slots: //---------------------------------------------------------------------------- // AppleNameProvider //---------------------------------------------------------------------------- -class AppleNameProvider : public XMPP::NameProvider, public QDnsSdDelegate -{ +class AppleNameProvider : public XMPP::NameProvider, public QDnsSdDelegate { Q_OBJECT public: AppleProvider *global; - AppleNameProvider(AppleProvider *parent) : - NameProvider(parent), - global(parent) - { - } + AppleNameProvider(AppleProvider *parent) : NameProvider(parent), global(parent) { } - ~AppleNameProvider() - { - global->stop_all(this); - } + ~AppleNameProvider() { global->stop_all(this); } - virtual bool supportsLongLived() const - { - return true; - } + virtual bool supportsLongLived() const { return true; } virtual bool supportsRecordType(int type) const { @@ -650,31 +575,26 @@ class AppleNameProvider : public XMPP::NameProvider, public QDnsSdDelegate return global->query(this, name, qType); } - virtual void resolve_stop(int id) - { - global->stop(id); - } + virtual void resolve_stop(int id) { global->stop(id); } protected: virtual void dns_queryResult(int id, const QDnsSd::QueryResult &result) { - if(!result.success) - { + if (!result.success) { emit resolve_error(id, XMPP::NameResolver::ErrorGeneric); return; } QList results; - foreach(const QDnsSd::Record &rec, result.records) - { + for (const QDnsSd::Record &rec : result.records) { XMPP::NameRecord nr = importQDnsSdRecord(rec); // unsupported type - if(nr.isNull()) + if (nr.isNull()) continue; // if removed, ensure ttl is 0 - if(!rec.added) + if (!rec.added) nr.setTtl(0); results += nr; @@ -687,23 +607,16 @@ class AppleNameProvider : public XMPP::NameProvider, public QDnsSdDelegate //---------------------------------------------------------------------------- // AppleServiceProvider //---------------------------------------------------------------------------- -class AppleServiceProvider : public XMPP::ServiceProvider, public QDnsSdDelegate -{ +class AppleServiceProvider : public XMPP::ServiceProvider, public QDnsSdDelegate { Q_OBJECT public: - class Browse - { + class Browse { public: AppleServiceProvider *parent; - int id; - AppleBrowse *browse; + int id; + AppleBrowse *browse; - Browse(AppleServiceProvider *_parent) : - parent(_parent), - id(-1), - browse(0) - { - } + Browse(AppleServiceProvider *_parent) : parent(_parent), id(-1), browse(0) { } ~Browse() { @@ -712,19 +625,13 @@ class AppleServiceProvider : public XMPP::ServiceProvider, public QDnsSdDelegate } }; - class Resolve - { + class Resolve { public: AppleServiceProvider *parent; - int id; - AppleBrowseLookup *resolve; + int id; + AppleBrowseLookup *resolve; - Resolve(AppleServiceProvider *_parent) : - parent(_parent), - id(-1), - resolve(0) - { - } + Resolve(AppleServiceProvider *_parent) : parent(_parent), id(-1), resolve(0) { } ~Resolve() { @@ -733,16 +640,12 @@ class AppleServiceProvider : public XMPP::ServiceProvider, public QDnsSdDelegate } }; - AppleProvider *global; - QList browseList; - QList resolveList; - IdManager idManager; + AppleProvider *global; + QList browseList; + QList resolveList; + IdManager idManager; - AppleServiceProvider(AppleProvider *parent) : - ServiceProvider(parent), - global(parent) - { - } + AppleServiceProvider(AppleProvider *parent) : ServiceProvider(parent), global(parent) { } ~AppleServiceProvider() { @@ -753,9 +656,8 @@ class AppleServiceProvider : public XMPP::ServiceProvider, public QDnsSdDelegate int indexOfBrowseByBrowse(AppleBrowse *browse) const { - for(int n = 0; n < browseList.count(); ++n) - { - if(browseList[n]->browse == browse) + for (int n = 0; n < browseList.count(); ++n) { + if (browseList[n]->browse == browse) return n; } return -1; @@ -763,9 +665,8 @@ class AppleServiceProvider : public XMPP::ServiceProvider, public QDnsSdDelegate int indexOfBrowseById(int id) const { - for(int n = 0; n < browseList.count(); ++n) - { - if(browseList[n]->id == id) + for (int n = 0; n < browseList.count(); ++n) { + if (browseList[n]->id == id) return n; } return -1; @@ -773,9 +674,8 @@ class AppleServiceProvider : public XMPP::ServiceProvider, public QDnsSdDelegate int indexOfResolveByResolve(AppleBrowseLookup *resolve) const { - for(int n = 0; n < resolveList.count(); ++n) - { - if(resolveList[n]->resolve == resolve) + for (int n = 0; n < resolveList.count(); ++n) { + if (resolveList[n]->resolve == resolve) return n; } return -1; @@ -783,9 +683,8 @@ class AppleServiceProvider : public XMPP::ServiceProvider, public QDnsSdDelegate int indexOfResolveById(int id) const { - for(int n = 0; n < resolveList.count(); ++n) - { - if(resolveList[n]->id == id) + for (int n = 0; n < resolveList.count(); ++n) { + if (resolveList[n]->id == id) return n; } return -1; @@ -794,7 +693,7 @@ class AppleServiceProvider : public XMPP::ServiceProvider, public QDnsSdDelegate virtual int browse_start(const QString &type, const QString &domain) { Browse *b = new Browse(this); - b->id = idManager.reserveId(); + b->id = idManager.reserveId(); b->browse = new AppleBrowse(global, this); connect(b->browse, SIGNAL(available(XMPP::ServiceInstance)), SLOT(browse_available(XMPP::ServiceInstance))); connect(b->browse, SIGNAL(unavailable(XMPP::ServiceInstance)), SLOT(browse_unavailable(XMPP::ServiceInstance))); @@ -807,7 +706,7 @@ class AppleServiceProvider : public XMPP::ServiceProvider, public QDnsSdDelegate virtual void browse_stop(int id) { int at = indexOfBrowseById(id); - if(at == -1) + if (at == -1) return; Browse *b = browseList[at]; @@ -818,14 +717,13 @@ class AppleServiceProvider : public XMPP::ServiceProvider, public QDnsSdDelegate virtual int resolve_start(const QByteArray &name) { QList parts = nameToInstanceParts(name); - if(parts.isEmpty()) - { + if (parts.isEmpty()) { // TODO: signal error rather than die Q_ASSERT(0); } Resolve *r = new Resolve(this); - r->id = idManager.reserveId(); + r->id = idManager.reserveId(); r->resolve = new AppleBrowseLookup(global, this); connect(r->resolve, SIGNAL(finished(QList)), SLOT(resolve_finished(QList))); connect(r->resolve, SIGNAL(error()), SLOT(resolve_error())); @@ -837,7 +735,7 @@ class AppleServiceProvider : public XMPP::ServiceProvider, public QDnsSdDelegate virtual void resolve_stop(int id) { int at = indexOfResolveById(id); - if(at == -1) + if (at == -1) return; Resolve *r = resolveList[at]; @@ -845,11 +743,11 @@ class AppleServiceProvider : public XMPP::ServiceProvider, public QDnsSdDelegate delete r; } - virtual int publish_start(const QString &instance, const QString &type, int port, const QMap &attributes) + virtual int publish_start(const QString &instance, const QString &type, int port, + const QMap &attributes) { QByteArray txtRecord = attribsToTxtRecord(attributes); - if(txtRecord.isEmpty()) - { + if (txtRecord.isEmpty()) { // TODO: signal error rather than die Q_ASSERT(0); } @@ -860,37 +758,30 @@ class AppleServiceProvider : public XMPP::ServiceProvider, public QDnsSdDelegate return global->reg(this, instance.toUtf8(), type.toUtf8(), domain.toUtf8(), port, txtRecord); } - virtual void publish_update(int id, const QMap &attributes) + virtual void publish_update(int id, const QMap &attributes) { // TODO: verify 'id' is valid. if not valid, then assert/return (don't do anything or signal error) QByteArray txtRecord = attribsToTxtRecord(attributes); - if(txtRecord.isEmpty()) - { + if (txtRecord.isEmpty()) { // TODO: signal error rather than die Q_ASSERT(0); } - if(global->dns.recordUpdateTxt(id, txtRecord, 4500)) - { + if (global->dns.recordUpdateTxt(id, txtRecord, 4500)) { // FIXME: SR QMetaObject::invokeMethod(this, "publish_published", Qt::QueuedConnection, Q_ARG(int, id)); - } - else - { + } else { // TODO: unpublish // FIXME: register meta type, SR - QMetaObject::invokeMethod(this, "publish_error", Qt::QueuedConnection, - Q_ARG(int, id), + QMetaObject::invokeMethod( + this, "publish_error", Qt::QueuedConnection, Q_ARG(int, id), Q_ARG(XMPP::ServiceLocalPublisher::Error, XMPP::ServiceLocalPublisher::ErrorGeneric)); } } - virtual void publish_stop(int id) - { - global->stop(id); - } + virtual void publish_stop(int id) { global->stop(id); } virtual int publish_extra_start(int pub_id, const XMPP::NameRecord &name) { @@ -965,11 +856,10 @@ private slots: delete r; QList results; - foreach(const QHostAddress &addr, addrs) - { + for (const QHostAddress &addr : addrs) { ResolveResult r; r.address = addr; - r.port = port; + r.port = port; results += r; } @@ -993,26 +883,14 @@ private slots: }; // AppleProvider -XMPP::NameProvider *AppleProvider::createNameProviderInternet() -{ - return new AppleNameProvider(this); -} +XMPP::NameProvider *AppleProvider::createNameProviderInternet() { return new AppleNameProvider(this); } -XMPP::NameProvider *AppleProvider::createNameProviderLocal() -{ - return new AppleNameProvider(this); -} +XMPP::NameProvider *AppleProvider::createNameProviderLocal() { return new AppleNameProvider(this); } -XMPP::ServiceProvider *AppleProvider::createServiceProvider() -{ - return new AppleServiceProvider(this); -} +XMPP::ServiceProvider *AppleProvider::createServiceProvider() { return new AppleServiceProvider(this); } #ifdef APPLEDNS_STATIC -XMPP::IrisNetProvider *irisnet_createAppleProvider() -{ - return new AppleProvider; -} +XMPP::IrisNetProvider *irisnet_createAppleProvider() { return new AppleProvider; } #else Q_EXPORT_PLUGIN2(appledns, AppleProvider) #endif diff --git a/src/irisnet/appledns/appledns.pri b/src/irisnet/appledns/appledns.pri deleted file mode 100644 index ff4c190f..00000000 --- a/src/irisnet/appledns/appledns.pri +++ /dev/null @@ -1,6 +0,0 @@ -QT *= network - -HEADERS += $$PWD/qdnssd.h -SOURCES += $$PWD/qdnssd.cpp $$PWD/appledns.cpp - -!mac:LIBS += -ldns_sd diff --git a/src/irisnet/appledns/appledns.pro b/src/irisnet/appledns/appledns.pro deleted file mode 100644 index ff2bcf57..00000000 --- a/src/irisnet/appledns/appledns.pro +++ /dev/null @@ -1,14 +0,0 @@ -IRIS_BASE = ../../.. -include(../../libbase.pri) - -TEMPLATE = lib -CONFIG += plugin -QT -= gui -DESTDIR = $$IRIS_BASE/plugins - -VERSION = 1.0.0 - -INCLUDEPATH *= $$PWD/../corelib -LIBS += -L$$IRIS_BASE/lib -lirisnetcore - -include(appledns.pri) diff --git a/src/irisnet/appledns/qdnssd.cpp b/src/irisnet/appledns/qdnssd.cpp index 9be138e2..abbb4e28 100644 --- a/src/irisnet/appledns/qdnssd.cpp +++ b/src/irisnet/appledns/qdnssd.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2007,2008 Justin Karneges + * Copyright (C) 2007-2008 Justin Karneges * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -18,22 +18,18 @@ #include "qdnssd.h" +#include "dns_sd.h" + #include #include - -// for ntohs -#ifdef Q_OS_WIN -# include +#ifdef Q_OS_WIN // for ntohs +#include #else -# include +#include #endif -#include "dns_sd.h" - namespace { - // safeobj stuff, from qca - void releaseAndDeleteLater(QObject *owner, QObject *obj) { obj->disconnect(owner); @@ -41,33 +37,28 @@ void releaseAndDeleteLater(QObject *owner, QObject *obj) obj->deleteLater(); } -class SafeTimer : public QObject -{ +class SafeTimer : public QObject { Q_OBJECT public: - SafeTimer(QObject *parent = 0) : - QObject(parent) + SafeTimer(QObject *parent = 0) : QObject(parent) { t = new QTimer(this); connect(t, SIGNAL(timeout()), SIGNAL(timeout())); } - ~SafeTimer() - { - releaseAndDeleteLater(this, t); - } + ~SafeTimer() { releaseAndDeleteLater(this, t); } - int interval() const { return t->interval(); } - bool isActive() const { return t->isActive(); } - bool isSingleShot() const { return t->isSingleShot(); } - void setInterval(int msec) { t->setInterval(msec); } + int interval() const { return t->interval(); } + bool isActive() const { return t->isActive(); } + bool isSingleShot() const { return t->isSingleShot(); } + void setInterval(int msec) { t->setInterval(msec); } void setSingleShot(bool singleShot) { t->setSingleShot(singleShot); } - int timerId() const { return t->timerId(); } + int timerId() const { return t->timerId(); } public slots: - void start(int msec) { t->start(msec); } - void start() { t->start(); } - void stop() { t->stop(); } + void start(int msec) { t->start(msec); } + void start() { t->start(); } + void stop() { t->stop(); } signals: void timeout(); @@ -76,13 +67,10 @@ public slots: QTimer *t; }; -class SafeSocketNotifier : public QObject -{ +class SafeSocketNotifier : public QObject { Q_OBJECT public: - SafeSocketNotifier(int socket, QSocketNotifier::Type type, - QObject *parent = 0) : - QObject(parent) + SafeSocketNotifier(int socket, QSocketNotifier::Type type, QObject *parent = 0) : QObject(parent) { sn = new QSocketNotifier(socket, type, this); connect(sn, SIGNAL(activated(int)), SIGNAL(activated(int))); @@ -94,12 +82,12 @@ class SafeSocketNotifier : public QObject releaseAndDeleteLater(this, sn); } - bool isEnabled() const { return sn->isEnabled(); } - int socket() const { return sn->socket(); } + bool isEnabled() const { return sn->isEnabled(); } + int socket() const { return sn->socket(); } QSocketNotifier::Type type() const { return sn->type(); } public slots: - void setEnabled(bool enable) { sn->setEnabled(enable); } + void setEnabled(bool enable) { sn->setEnabled(enable); } signals: void activated(int socket); @@ -115,85 +103,58 @@ public slots: // to maintain a pointer which /can/ be copied. Also, we'll keep // a flag to indicate whether the allocated DNSServiceRef has been // initialized yet. -class ServiceRef -{ +class ServiceRef { private: DNSServiceRef *_p; - bool _initialized; + bool _initialized; public: - ServiceRef() : - _initialized(false) - { - _p = (DNSServiceRef *)malloc(sizeof(DNSServiceRef)); - } + ServiceRef() : _initialized(false) { _p = (DNSServiceRef *)malloc(sizeof(DNSServiceRef)); } ~ServiceRef() { - if(_initialized) + if (_initialized) DNSServiceRefDeallocate(*_p); free(_p); } - DNSServiceRef *data() - { - return _p; - } + DNSServiceRef *data() { return _p; } - void setInitialized() - { - _initialized = true; - } + void setInitialized() { _initialized = true; } }; -class RecordRef -{ +class RecordRef { private: DNSRecordRef *_p; public: - RecordRef() - { - _p = (DNSRecordRef *)malloc(sizeof(DNSRecordRef)); - } + RecordRef() { _p = (DNSRecordRef *)malloc(sizeof(DNSRecordRef)); } - ~RecordRef() - { - free(_p); - } + ~RecordRef() { free(_p); } - DNSRecordRef *data() - { - return _p; - } + DNSRecordRef *data() { return _p; } }; -class IdManager -{ +class IdManager { private: QSet set; - int at; + int at; inline void bump_at() { - if(at == 0x7fffffff) + if (at == 0x7fffffff) at = 0; else ++at; } public: - IdManager() : - at(0) - { - } + IdManager() : at(0) { } int reserveId() { - while(1) - { - if(!set.contains(at)) - { + while (1) { + if (!set.contains(at)) { int id = at; set.insert(id); bump_at(); @@ -204,37 +165,27 @@ class IdManager } } - void releaseId(int id) - { - set.remove(id); - } + void releaseId(int id) { set.remove(id); } }; -} +} // namespace //---------------------------------------------------------------------------- // QDnsSd //---------------------------------------------------------------------------- -class QDnsSd::Private : public QObject -{ +class QDnsSd::Private : public QObject { Q_OBJECT public: - QDnsSd *q; + QDnsSd *q; IdManager idManager; - class SubRecord - { + class SubRecord { public: - Private *_self; - int _id; + Private *_self; + int _id; RecordRef *_sdref; - SubRecord(Private *self) : - _self(self), - _id(-1), - _sdref(0) - { - } + SubRecord(Private *self) : _self(self), _id(-1), _sdref(0) { } ~SubRecord() { @@ -243,46 +194,33 @@ class QDnsSd::Private : public QObject } }; - class Request - { + class Request { public: - enum Type - { - Query, - Browse, - Resolve, - Reg - }; - - Private *_self; - int _type; - int _id; - ServiceRef *_sdref; - int _sockfd; + enum Type { Query, Browse, Resolve, Reg }; + + Private *_self; + int _type; + int _id; + ServiceRef *_sdref; + int _sockfd; SafeSocketNotifier *_sn_read; - SafeTimer *_errorTrigger; + SafeTimer *_errorTrigger; - bool _doSignal; - LowLevelError _lowLevelError; - QList _queryRecords; + bool _doSignal; + LowLevelError _lowLevelError; + QList _queryRecords; QList _browseEntries; - QByteArray _resolveFullName; - QByteArray _resolveHost; - int _resolvePort; - QByteArray _resolveTxtRecord; - QByteArray _regDomain; - bool _regConflict; + QByteArray _resolveFullName; + QByteArray _resolveHost; + int _resolvePort; + QByteArray _resolveTxtRecord; + QByteArray _regDomain; + bool _regConflict; - QList _subRecords; + QList _subRecords; Request(Private *self) : - _self(self), - _id(-1), - _sdref(0), - _sockfd(-1), - _sn_read(0), - _errorTrigger(0), - _doSignal(false) + _self(self), _id(-1), _sdref(0), _sockfd(-1), _sn_read(0), _errorTrigger(0), _doSignal(false) { } @@ -298,30 +236,22 @@ class QDnsSd::Private : public QObject int subRecordIndexById(int rec_id) const { - for(int n = 0; n < _subRecords.count(); ++n) - { - if(_subRecords[n]->_id == rec_id) + for (int n = 0; n < _subRecords.count(); ++n) { + if (_subRecords[n]->_id == rec_id) return n; } return -1; } }; - QHash _requestsById; - QHash _requestsBySocket; - QHash _requestsByTimer; - QHash _requestsByRecId; + QHash _requestsById; + QHash _requestsBySocket; + QHash _requestsByTimer; + QHash _requestsByRecId; - Private(QDnsSd *_q) : - QObject(_q), - q(_q) - { - } + Private(QDnsSd *_q) : QObject(_q), q(_q) { } - ~Private() - { - qDeleteAll(_requestsById); - } + ~Private() { qDeleteAll(_requestsById); } void setDelayedError(Request *req, const LowLevelError &lowLevelError) { @@ -341,11 +271,11 @@ class QDnsSd::Private : public QObject void removeRequest(Request *req) { - foreach(const SubRecord *srec, req->_subRecords) + for (const SubRecord *srec : req->_subRecords) _requestsByRecId.remove(srec->_id); - if(req->_errorTrigger) + if (req->_errorTrigger) _requestsByTimer.remove(req->_errorTrigger); - if(req->_sn_read) + if (req->_sn_read) _requestsBySocket.remove(req->_sn_read); _requestsById.remove(req->_id); delete req; @@ -354,7 +284,7 @@ class QDnsSd::Private : public QObject int regIdForRecId(int rec_id) const { Request *req = _requestsByRecId.value(rec_id); - if(req) + if (req) return req->_id; return -1; } @@ -364,32 +294,27 @@ class QDnsSd::Private : public QObject int id = idManager.reserveId(); Request *req = new Request(this); - req->_type = Request::Query; - req->_id = id; - req->_sdref = new ServiceRef; - - DNSServiceErrorType err = DNSServiceQueryRecord( - req->_sdref->data(), kDNSServiceFlagsLongLivedQuery, - 0, name.constData(), qType, kDNSServiceClass_IN, - cb_queryRecordReply, req); - if(err != kDNSServiceErr_NoError) - { - setDelayedError(req, LowLevelError( - "DNSServiceQueryRecord", err)); + req->_type = Request::Query; + req->_id = id; + req->_sdref = new ServiceRef; + + DNSServiceErrorType err + = DNSServiceQueryRecord(req->_sdref->data(), kDNSServiceFlagsLongLivedQuery, 0, name.constData(), qType, + kDNSServiceClass_IN, cb_queryRecordReply, req); + if (err != kDNSServiceErr_NoError) { + setDelayedError(req, LowLevelError("DNSServiceQueryRecord", err)); return id; } req->_sdref->setInitialized(); int sockfd = DNSServiceRefSockFD(*(req->_sdref->data())); - if(sockfd == -1) - { - setDelayedError(req, LowLevelError( - "DNSServiceRefSockFD", -1)); + if (sockfd == -1) { + setDelayedError(req, LowLevelError("DNSServiceRefSockFD", -1)); return id; } - req->_sockfd = sockfd; + req->_sockfd = sockfd; req->_sn_read = new SafeSocketNotifier(sockfd, QSocketNotifier::Read, this); connect(req->_sn_read, SIGNAL(activated(int)), SLOT(sn_activated())); _requestsById.insert(id, req); @@ -403,32 +328,26 @@ class QDnsSd::Private : public QObject int id = idManager.reserveId(); Request *req = new Request(this); - req->_type = Request::Browse; - req->_id = id; - req->_sdref = new ServiceRef; - - DNSServiceErrorType err = DNSServiceBrowse( - req->_sdref->data(), 0, 0, serviceType.constData(), - !domain.isEmpty() ? domain.constData() : NULL, - cb_browseReply, req); - if(err != kDNSServiceErr_NoError) - { - setDelayedError(req, LowLevelError( - "DNSServiceBrowse", err)); + req->_type = Request::Browse; + req->_id = id; + req->_sdref = new ServiceRef; + + DNSServiceErrorType err = DNSServiceBrowse(req->_sdref->data(), 0, 0, serviceType.constData(), + !domain.isEmpty() ? domain.constData() : NULL, cb_browseReply, req); + if (err != kDNSServiceErr_NoError) { + setDelayedError(req, LowLevelError("DNSServiceBrowse", err)); return id; } req->_sdref->setInitialized(); int sockfd = DNSServiceRefSockFD(*(req->_sdref->data())); - if(sockfd == -1) - { - setDelayedError(req, LowLevelError( - "DNSServiceRefSockFD", -1)); + if (sockfd == -1) { + setDelayedError(req, LowLevelError("DNSServiceRefSockFD", -1)); return id; } - req->_sockfd = sockfd; + req->_sockfd = sockfd; req->_sn_read = new SafeSocketNotifier(sockfd, QSocketNotifier::Read, this); connect(req->_sn_read, SIGNAL(activated(int)), SLOT(sn_activated())); _requestsById.insert(id, req); @@ -442,32 +361,27 @@ class QDnsSd::Private : public QObject int id = idManager.reserveId(); Request *req = new Request(this); - req->_type = Request::Resolve; - req->_id = id; - req->_sdref = new ServiceRef; - - DNSServiceErrorType err = DNSServiceResolve( - req->_sdref->data(), 0, 0, serviceName.constData(), - serviceType.constData(), domain.constData(), - (DNSServiceResolveReply)cb_resolveReply, req); - if(err != kDNSServiceErr_NoError) - { - setDelayedError(req, LowLevelError( - "DNSServiceResolve", err)); + req->_type = Request::Resolve; + req->_id = id; + req->_sdref = new ServiceRef; + + DNSServiceErrorType err + = DNSServiceResolve(req->_sdref->data(), 0, 0, serviceName.constData(), serviceType.constData(), + domain.constData(), (DNSServiceResolveReply)cb_resolveReply, req); + if (err != kDNSServiceErr_NoError) { + setDelayedError(req, LowLevelError("DNSServiceResolve", err)); return id; } req->_sdref->setInitialized(); int sockfd = DNSServiceRefSockFD(*(req->_sdref->data())); - if(sockfd == -1) - { - setDelayedError(req, LowLevelError( - "DNSServiceRefSockFD", -1)); + if (sockfd == -1) { + setDelayedError(req, LowLevelError("DNSServiceRefSockFD", -1)); return id; } - req->_sockfd = sockfd; + req->_sockfd = sockfd; req->_sn_read = new SafeSocketNotifier(sockfd, QSocketNotifier::Read, this); connect(req->_sn_read, SIGNAL(activated(int)), SLOT(sn_activated())); _requestsById.insert(id, req); @@ -476,48 +390,42 @@ class QDnsSd::Private : public QObject return id; } - int reg(const QByteArray &serviceName, const QByteArray &serviceType, const QByteArray &domain, int port, const QByteArray &txtRecord) + int reg(const QByteArray &serviceName, const QByteArray &serviceType, const QByteArray &domain, int port, + const QByteArray &txtRecord) { int id = idManager.reserveId(); Request *req = new Request(this); - req->_type = Request::Reg; - req->_id = id; + req->_type = Request::Reg; + req->_id = id; - if(port < 1 || port > 0xffff) - { + if (port < 1 || port > 0xffff) { setDelayedError(req, LowLevelError()); return id; } uint16_t sport = port; - sport = htons(sport); + sport = htons(sport); req->_sdref = new ServiceRef; DNSServiceErrorType err = DNSServiceRegister( - req->_sdref->data(), kDNSServiceFlagsNoAutoRename, 0, - serviceName.constData(), serviceType.constData(), - domain.constData(), NULL, sport, txtRecord.size(), - txtRecord.data(), cb_regReply, req); - if(err != kDNSServiceErr_NoError) - { - setDelayedError(req, LowLevelError( - "DNSServiceRegister", err)); + req->_sdref->data(), kDNSServiceFlagsNoAutoRename, 0, serviceName.constData(), serviceType.constData(), + domain.constData(), NULL, sport, txtRecord.size(), txtRecord.data(), cb_regReply, req); + if (err != kDNSServiceErr_NoError) { + setDelayedError(req, LowLevelError("DNSServiceRegister", err)); return id; } req->_sdref->setInitialized(); int sockfd = DNSServiceRefSockFD(*(req->_sdref->data())); - if(sockfd == -1) - { - setDelayedError(req, LowLevelError( - "DNSServiceRefSockFD", -1)); + if (sockfd == -1) { + setDelayedError(req, LowLevelError("DNSServiceRefSockFD", -1)); return id; } - req->_sockfd = sockfd; + req->_sockfd = sockfd; req->_sn_read = new SafeSocketNotifier(sockfd, QSocketNotifier::Read, this); connect(req->_sn_read, SIGNAL(activated(int)), SLOT(sn_activated())); _requestsById.insert(id, req); @@ -529,31 +437,27 @@ class QDnsSd::Private : public QObject int recordAdd(int reg_id, const Record &rec, LowLevelError *lowLevelError) { Request *req = _requestsById.value(reg_id); - if(!req) - { - if(lowLevelError) + if (!req) { + if (lowLevelError) *lowLevelError = LowLevelError(); return -1; } RecordRef *recordRef = new RecordRef; - DNSServiceErrorType err = DNSServiceAddRecord( - *(req->_sdref->data()), recordRef->data(), 0, - rec.rrtype, rec.rdata.size(), rec.rdata.data(), - rec.ttl); - if(err != kDNSServiceErr_NoError) - { - if(lowLevelError) + DNSServiceErrorType err = DNSServiceAddRecord(*(req->_sdref->data()), recordRef->data(), 0, rec.rrtype, + rec.rdata.size(), rec.rdata.data(), rec.ttl); + if (err != kDNSServiceErr_NoError) { + if (lowLevelError) *lowLevelError = LowLevelError("DNSServiceAddRecord", err); delete recordRef; return -1; } - int id = idManager.reserveId(); + int id = idManager.reserveId(); SubRecord *srec = new SubRecord(this); - srec->_id = id; - srec->_sdref = recordRef; + srec->_id = id; + srec->_sdref = recordRef; req->_subRecords += srec; _requestsByRecId.insert(id, req); @@ -563,33 +467,27 @@ class QDnsSd::Private : public QObject bool recordUpdate(int reg_id, int rec_id, const Record &rec, LowLevelError *lowLevelError) { Request *req = _requestsById.value(reg_id); - if(!req) - { - if(lowLevelError) + if (!req) { + if (lowLevelError) *lowLevelError = LowLevelError(); return false; } SubRecord *srec = 0; - if(rec_id != -1) - { + if (rec_id != -1) { int at = req->subRecordIndexById(rec_id); - if(at == -1) - { - if(lowLevelError) + if (at == -1) { + if (lowLevelError) *lowLevelError = LowLevelError(); return false; } srec = req->_subRecords[at]; } - DNSServiceErrorType err = DNSServiceUpdateRecord( - *(req->_sdref->data()), - srec ? *(srec->_sdref->data()) : NULL, 0, - rec.rdata.size(), rec.rdata.data(), rec.ttl); - if(err != kDNSServiceErr_NoError) - { - if(lowLevelError) + DNSServiceErrorType err = DNSServiceUpdateRecord(*(req->_sdref->data()), srec ? *(srec->_sdref->data()) : NULL, + 0, rec.rdata.size(), rec.rdata.data(), rec.ttl); + if (err != kDNSServiceErr_NoError) { + if (lowLevelError) *lowLevelError = LowLevelError("DNSServiceUpdateRecord", err); return false; } @@ -600,7 +498,7 @@ class QDnsSd::Private : public QObject void recordRemove(int rec_id) { Request *req = _requestsByRecId.value(rec_id); - if(!req) + if (!req) return; // this can't fail @@ -616,7 +514,7 @@ class QDnsSd::Private : public QObject void stop(int id) { Request *req = _requestsById.value(id); - if(req) + if (req) removeRequest(req); } @@ -624,8 +522,8 @@ private slots: void sn_activated() { SafeSocketNotifier *sn_read = static_cast(sender()); - Request *req = _requestsBySocket.value(sn_read); - if(!req) + Request *req = _requestsBySocket.value(sn_read); + if (!req) return; int id = req->_id; @@ -634,47 +532,40 @@ private slots: // do error if the above function returns an error, or if we // collected an error during a callback - if(err != kDNSServiceErr_NoError || !req->_lowLevelError.func.isEmpty()) - { + if (err != kDNSServiceErr_NoError || !req->_lowLevelError.func.isEmpty()) { LowLevelError lowLevelError; - if(err != kDNSServiceErr_NoError) + if (err != kDNSServiceErr_NoError) lowLevelError = LowLevelError("DNSServiceProcessResult", err); else lowLevelError = req->_lowLevelError; // reg conflict indicated via callback bool regConflict = false; - if(req->_type == Request::Reg && !req->_lowLevelError.func.isEmpty()) + if (req->_type == Request::Reg && !req->_lowLevelError.func.isEmpty()) regConflict = req->_regConflict; removeRequest(req); - if(req->_type == Request::Query) - { + if (req->_type == Request::Query) { QDnsSd::QueryResult r; - r.success = false; + r.success = false; r.lowLevelError = lowLevelError; emit q->queryResult(id, r); - } - else if(req->_type == Request::Browse) - { + } else if (req->_type == Request::Browse) { QDnsSd::BrowseResult r; - r.success = false; + r.success = false; r.lowLevelError = lowLevelError; emit q->browseResult(id, r); - } - else if(req->_type == Request::Resolve) - { + } else if (req->_type == Request::Resolve) { QDnsSd::ResolveResult r; - r.success = false; + r.success = false; r.lowLevelError = lowLevelError; emit q->resolveResult(id, r); - } - else // Reg + } else // Reg { QDnsSd::RegResult r; r.success = false; - if(regConflict) + if (regConflict) r.errorCode = QDnsSd::RegResult::ErrorConflict; else r.errorCode = QDnsSd::RegResult::ErrorGeneric; @@ -687,10 +578,8 @@ private slots: // handle success - if(req->_type == Request::Query) - { - if(req->_doSignal) - { + if (req->_type == Request::Query) { + if (req->_doSignal) { QDnsSd::QueryResult r; r.success = true; r.records = req->_queryRecords; @@ -698,11 +587,8 @@ private slots: req->_doSignal = false; emit q->queryResult(id, r); } - } - else if(req->_type == Request::Browse) - { - if(req->_doSignal) - { + } else if (req->_type == Request::Browse) { + if (req->_doSignal) { QDnsSd::BrowseResult r; r.success = true; r.entries = req->_browseEntries; @@ -710,17 +596,14 @@ private slots: req->_doSignal = false; emit q->browseResult(id, r); } - } - else if(req->_type == Request::Resolve) - { - if(req->_doSignal) - { + } else if (req->_type == Request::Resolve) { + if (req->_doSignal) { QDnsSd::ResolveResult r; - r.success = true; - r.fullName = req->_resolveFullName; - r.hostTarget = req->_resolveHost; - r.port = req->_resolvePort; - r.txtRecord = req->_resolveTxtRecord; + r.success = true; + r.fullName = req->_resolveFullName; + r.hostTarget = req->_resolveHost; + r.port = req->_resolvePort; + r.txtRecord = req->_resolveTxtRecord; req->_doSignal = false; // there is only one response @@ -728,14 +611,12 @@ private slots: emit q->resolveResult(id, r); } - } - else // Reg + } else // Reg { - if(req->_doSignal) - { + if (req->_doSignal) { QDnsSd::RegResult r; - r.success = true; - r.domain = req->_regDomain; + r.success = true; + r.domain = req->_regDomain; req->_doSignal = false; emit q->regResult(id, r); @@ -745,180 +626,155 @@ private slots: void doError() { - SafeTimer *t = static_cast(sender()); - Request *req = _requestsByTimer.value(t); - if(!req) + SafeTimer *t = static_cast(sender()); + Request *req = _requestsByTimer.value(t); + if (!req) return; - int id = req->_id; + int id = req->_id; int type = req->_type; removeRequest(req); - if(type == Request::Query) - { + if (type == Request::Query) { QDnsSd::QueryResult r; - r.success = false; + r.success = false; r.lowLevelError = req->_lowLevelError; emit q->queryResult(id, r); - } - else if(type == Request::Browse) - { + } else if (type == Request::Browse) { QDnsSd::BrowseResult r; - r.success = false; + r.success = false; r.lowLevelError = req->_lowLevelError; emit q->browseResult(id, r); - } - else if(type == Request::Resolve) - { + } else if (type == Request::Resolve) { QDnsSd::ResolveResult r; - r.success = false; + r.success = false; r.lowLevelError = req->_lowLevelError; emit q->resolveResult(id, r); - } - else // Reg + } else // Reg { QDnsSd::RegResult r; - r.success = false; - r.errorCode = QDnsSd::RegResult::ErrorGeneric; + r.success = false; + r.errorCode = QDnsSd::RegResult::ErrorGeneric; r.lowLevelError = req->_lowLevelError; emit q->regResult(id, r); } } private: - static void cb_queryRecordReply(DNSServiceRef ref, - DNSServiceFlags flags, uint32_t interfaceIndex, - DNSServiceErrorType errorCode, const char *fullname, - uint16_t rrtype, uint16_t rrclass, uint16_t rdlen, - const void *rdata, uint32_t ttl, void *context) + static void cb_queryRecordReply(DNSServiceRef ref, DNSServiceFlags flags, uint32_t interfaceIndex, + DNSServiceErrorType errorCode, const char *fullname, uint16_t rrtype, + uint16_t rrclass, uint16_t rdlen, const void *rdata, uint32_t ttl, void *context) { Q_UNUSED(ref); Q_UNUSED(interfaceIndex); Q_UNUSED(rrclass); Request *req = static_cast(context); - req->_self->handle_queryRecordReply(req, flags, errorCode, - fullname, rrtype, rdlen, (const char *)rdata, ttl); + req->_self->handle_queryRecordReply(req, flags, errorCode, fullname, rrtype, rdlen, (const char *)rdata, ttl); } - static void cb_browseReply(DNSServiceRef ref, - DNSServiceFlags flags, uint32_t interfaceIndex, - DNSServiceErrorType errorCode, const char *serviceName, - const char *regtype, const char *replyDomain, void *context) + static void cb_browseReply(DNSServiceRef ref, DNSServiceFlags flags, uint32_t interfaceIndex, + DNSServiceErrorType errorCode, const char *serviceName, const char *regtype, + const char *replyDomain, void *context) { Q_UNUSED(ref); Q_UNUSED(interfaceIndex); Request *req = static_cast(context); - req->_self->handle_browseReply(req, flags, errorCode, - serviceName, regtype, replyDomain); + req->_self->handle_browseReply(req, flags, errorCode, serviceName, regtype, replyDomain); } - static void cb_resolveReply(DNSServiceRef ref, - DNSServiceFlags flags, uint32_t interfaceIndex, - DNSServiceErrorType errorCode, const char *fullname, - const char *hosttarget, uint16_t port, uint16_t txtLen, - const unsigned char *txtRecord, void *context) + static void cb_resolveReply(DNSServiceRef ref, DNSServiceFlags flags, uint32_t interfaceIndex, + DNSServiceErrorType errorCode, const char *fullname, const char *hosttarget, + uint16_t port, uint16_t txtLen, const unsigned char *txtRecord, void *context) { Q_UNUSED(ref); Q_UNUSED(flags); Q_UNUSED(interfaceIndex); Request *req = static_cast(context); - req->_self->handle_resolveReply(req, errorCode, fullname, - hosttarget, port, txtLen, txtRecord); + req->_self->handle_resolveReply(req, errorCode, fullname, hosttarget, port, txtLen, txtRecord); } - static void cb_regReply(DNSServiceRef ref, - DNSServiceFlags flags, DNSServiceErrorType errorCode, - const char *name, const char *regtype, const char *domain, - void *context) + static void cb_regReply(DNSServiceRef ref, DNSServiceFlags flags, DNSServiceErrorType errorCode, const char *name, + const char *regtype, const char *domain, void *context) { Q_UNUSED(ref); Q_UNUSED(flags); Request *req = static_cast(context); - req->_self->handle_regReply(req, errorCode, name, regtype, - domain); + req->_self->handle_regReply(req, errorCode, name, regtype, domain); } - void handle_queryRecordReply(Request *req, DNSServiceFlags flags, - DNSServiceErrorType errorCode, const char *fullname, - uint16_t rrtype, uint16_t rdlen, const char *rdata, - uint16_t ttl) + void handle_queryRecordReply(Request *req, DNSServiceFlags flags, DNSServiceErrorType errorCode, + const char *fullname, uint16_t rrtype, uint16_t rdlen, const char *rdata, uint16_t ttl) { - if(errorCode != kDNSServiceErr_NoError) - { - req->_doSignal = true; + if (errorCode != kDNSServiceErr_NoError) { + req->_doSignal = true; req->_lowLevelError = LowLevelError("DNSServiceQueryRecordReply", errorCode); return; } QDnsSd::Record rec; - rec.added = (flags & kDNSServiceFlagsAdd) ? true: false; - rec.name = QByteArray(fullname); + rec.added = (flags & kDNSServiceFlagsAdd) ? true : false; + rec.name = QByteArray(fullname); rec.rrtype = rrtype; - rec.rdata = QByteArray(rdata, rdlen); - rec.ttl = ttl; + rec.rdata = QByteArray(rdata, rdlen); + rec.ttl = ttl; req->_queryRecords += rec; - if(!(flags & kDNSServiceFlagsMoreComing)) + if (!(flags & kDNSServiceFlagsMoreComing)) req->_doSignal = true; } - void handle_browseReply(Request *req, DNSServiceFlags flags, - DNSServiceErrorType errorCode, const char *serviceName, - const char *regtype, const char *replyDomain) + void handle_browseReply(Request *req, DNSServiceFlags flags, DNSServiceErrorType errorCode, const char *serviceName, + const char *regtype, const char *replyDomain) { - if(errorCode != kDNSServiceErr_NoError) - { - req->_doSignal = true; + if (errorCode != kDNSServiceErr_NoError) { + req->_doSignal = true; req->_lowLevelError = LowLevelError("DNSServiceBrowseReply", errorCode); return; } QDnsSd::BrowseEntry e; - e.added = (flags & kDNSServiceFlagsAdd) ? true: false; + e.added = (flags & kDNSServiceFlagsAdd) ? true : false; e.serviceName = QByteArray(serviceName); e.serviceType = QByteArray(regtype); e.replyDomain = QByteArray(replyDomain); req->_browseEntries += e; - if(!(flags & kDNSServiceFlagsMoreComing)) + if (!(flags & kDNSServiceFlagsMoreComing)) req->_doSignal = true; } - void handle_resolveReply(Request *req, DNSServiceErrorType errorCode, - const char *fullname, const char *hosttarget, uint16_t port, - uint16_t txtLen, const unsigned char *txtRecord) + void handle_resolveReply(Request *req, DNSServiceErrorType errorCode, const char *fullname, const char *hosttarget, + uint16_t port, uint16_t txtLen, const unsigned char *txtRecord) { - if(errorCode != kDNSServiceErr_NoError) - { - req->_doSignal = true; + if (errorCode != kDNSServiceErr_NoError) { + req->_doSignal = true; req->_lowLevelError = LowLevelError("DNSServiceResolveReply", errorCode); return; } - req->_resolveFullName = QByteArray(fullname); - req->_resolveHost = QByteArray(hosttarget); - req->_resolvePort = ntohs(port); + req->_resolveFullName = QByteArray(fullname); + req->_resolveHost = QByteArray(hosttarget); + req->_resolvePort = ntohs(port); req->_resolveTxtRecord = QByteArray((const char *)txtRecord, txtLen); req->_doSignal = true; } - void handle_regReply(Request *req, DNSServiceErrorType errorCode, - const char *name, const char *regtype, const char *domain) + void handle_regReply(Request *req, DNSServiceErrorType errorCode, const char *name, const char *regtype, + const char *domain) { Q_UNUSED(name); Q_UNUSED(regtype); - if(errorCode != kDNSServiceErr_NoError) - { - req->_doSignal = true; + if (errorCode != kDNSServiceErr_NoError) { + req->_doSignal = true; req->_lowLevelError = LowLevelError("DNSServiceRegisterReply", errorCode); - if(errorCode == kDNSServiceErr_NameConflict) + if (errorCode == kDNSServiceErr_NameConflict) req->_regConflict = true; else req->_regConflict = false; @@ -926,37 +782,25 @@ private slots: } req->_regDomain = QByteArray(domain); - req->_doSignal = true; + req->_doSignal = true; } }; -QDnsSd::QDnsSd(QObject *parent) : - QObject(parent) -{ - d = new Private(this); -} +QDnsSd::QDnsSd(QObject *parent) : QObject(parent) { d = new Private(this); } -QDnsSd::~QDnsSd() -{ - delete d; -} +QDnsSd::~QDnsSd() { delete d; } -int QDnsSd::query(const QByteArray &name, int qType) -{ - return d->query(name, qType); -} +int QDnsSd::query(const QByteArray &name, int qType) { return d->query(name, qType); } -int QDnsSd::browse(const QByteArray &serviceType, const QByteArray &domain) -{ - return d->browse(serviceType, domain); -} +int QDnsSd::browse(const QByteArray &serviceType, const QByteArray &domain) { return d->browse(serviceType, domain); } int QDnsSd::resolve(const QByteArray &serviceName, const QByteArray &serviceType, const QByteArray &domain) { return d->resolve(serviceName, serviceType, domain); } -int QDnsSd::reg(const QByteArray &serviceName, const QByteArray &serviceType, const QByteArray &domain, int port, const QByteArray &txtRecord) +int QDnsSd::reg(const QByteArray &serviceName, const QByteArray &serviceType, const QByteArray &domain, int port, + const QByteArray &txtRecord) { return d->reg(serviceName, serviceType, domain, port, txtRecord); } @@ -969,7 +813,7 @@ int QDnsSd::recordAdd(int reg_id, const Record &rec, LowLevelError *lowLevelErro bool QDnsSd::recordUpdate(int rec_id, const Record &rec, LowLevelError *lowLevelError) { int reg_id = d->regIdForRecId(rec_id); - if(reg_id == -1) + if (reg_id == -1) return false; return d->recordUpdate(reg_id, rec_id, rec, lowLevelError); @@ -979,43 +823,33 @@ bool QDnsSd::recordUpdateTxt(int reg_id, const QByteArray &txtRecord, quint32 tt { Record rec; rec.rrtype = kDNSServiceType_TXT; - rec.rdata = txtRecord; - rec.ttl = ttl; + rec.rdata = txtRecord; + rec.ttl = ttl; return d->recordUpdate(reg_id, -1, rec, lowLevelError); } -void QDnsSd::recordRemove(int rec_id) -{ - d->recordRemove(rec_id); -} +void QDnsSd::recordRemove(int rec_id) { d->recordRemove(rec_id); } -void QDnsSd::stop(int id) -{ - d->stop(id); -} +void QDnsSd::stop(int id) { d->stop(id); } QByteArray QDnsSd::createTxtRecord(const QList &strings) { // split into var/val and validate QList vars; QList vals; // null = no value, empty = empty value - foreach(const QByteArray &i, strings) - { + for (const QByteArray &i : strings) { QByteArray var; QByteArray val; - int n = i.indexOf('='); - if(n != -1) - { + int n = i.indexOf('='); + if (n != -1) { var = i.mid(0, n); val = i.mid(n + 1); - } - else + } else var = i; - for(int n = 0; n < var.size(); ++n) - { + for (int n = 0; n < var.size(); ++n) { unsigned char c = var[n]; - if(c < 0x20 || c > 0x7e) + if (c < 0x20 || c > 0x7e) return QByteArray(); } @@ -1024,21 +858,18 @@ QByteArray QDnsSd::createTxtRecord(const QList &strings) } TXTRecordRef ref; - QByteArray buf(256, 0); + QByteArray buf(256, 0); TXTRecordCreate(&ref, buf.size(), buf.data()); - for(int n = 0; n < vars.count(); ++n) - { - int valueSize = vals[n].size(); + for (int n = 0; n < vars.count(); ++n) { + int valueSize = vals[n].size(); char *value; - if(!vals[n].isNull()) + if (!vals[n].isNull()) value = vals[n].data(); else value = 0; - DNSServiceErrorType err = TXTRecordSetValue(&ref, - vars[n].data(), valueSize, value); - if(err != kDNSServiceErr_NoError) - { + DNSServiceErrorType err = TXTRecordSetValue(&ref, vars[n].data(), valueSize, value); + if (err != kDNSServiceErr_NoError) { TXTRecordDeallocate(&ref); return QByteArray(); } @@ -1051,23 +882,20 @@ QByteArray QDnsSd::createTxtRecord(const QList &strings) QList QDnsSd::parseTxtRecord(const QByteArray &txtRecord) { QList out; - int count = TXTRecordGetCount(txtRecord.size(), txtRecord.data()); - for(int n = 0; n < count; ++n) - { - QByteArray keyBuf(256, 0); - uint8_t valueLen; - const void *value; - DNSServiceErrorType err = TXTRecordGetItemAtIndex( - txtRecord.size(), txtRecord.data(), n, keyBuf.size(), - keyBuf.data(), &valueLen, &value); - if(err != kDNSServiceErr_NoError) + int count = TXTRecordGetCount(txtRecord.size(), txtRecord.data()); + for (int n = 0; n < count; ++n) { + QByteArray keyBuf(256, 0); + uint8_t valueLen; + const void *value; + DNSServiceErrorType err = TXTRecordGetItemAtIndex(txtRecord.size(), txtRecord.data(), n, keyBuf.size(), + keyBuf.data(), &valueLen, &value); + if (err != kDNSServiceErr_NoError) return QList(); keyBuf.resize(qstrlen(keyBuf.data())); QByteArray entry = keyBuf; - if(value) - { + if (value) { entry += '='; entry += QByteArray((const char *)value, valueLen); } diff --git a/src/irisnet/appledns/qdnssd.h b/src/irisnet/appledns/qdnssd.h index 6edb3bd6..2bfba78c 100644 --- a/src/irisnet/appledns/qdnssd.h +++ b/src/irisnet/appledns/qdnssd.h @@ -19,48 +19,37 @@ #ifndef QDNSSD_H #define QDNSSD_H -#include #include #include +#include // DOR-compliant -class QDnsSd : public QObject -{ +class QDnsSd : public QObject { Q_OBJECT public: - class LowLevelError - { + class LowLevelError { public: QString func; - int code; - - LowLevelError() : - code(0) - { - } - - LowLevelError(const QString &_func, int _code) : - func(_func), - code(_code) - { - } + int code; + + LowLevelError() : code(0) { } + + LowLevelError(const QString &_func, int _code) : func(_func), code(_code) { } }; - class Record - { + class Record { public: bool added; // only used by QueryResult QByteArray name; - int rrtype; + int rrtype; QByteArray rdata; - quint32 ttl; + quint32 ttl; }; - class BrowseEntry - { + class BrowseEntry { public: - bool added; + bool added; QByteArray serviceName; // these may be different from request, see dns_sd docs @@ -68,47 +57,39 @@ class QDnsSd : public QObject QByteArray replyDomain; }; - class QueryResult - { + class QueryResult { public: - bool success; + bool success; LowLevelError lowLevelError; QList records; }; - class BrowseResult - { + class BrowseResult { public: - bool success; + bool success; LowLevelError lowLevelError; QList entries; }; - class ResolveResult - { + class ResolveResult { public: - bool success; + bool success; LowLevelError lowLevelError; QByteArray fullName; QByteArray hostTarget; - int port; // host byte-order + int port; // host byte-order QByteArray txtRecord; }; - class RegResult - { + class RegResult { public: - enum Error - { - ErrorGeneric, - ErrorConflict - }; - - bool success; - Error errorCode; + enum Error { ErrorGeneric, ErrorConflict }; + + bool success; + Error errorCode; LowLevelError lowLevelError; QByteArray domain; @@ -125,7 +106,8 @@ class QDnsSd : public QObject int resolve(const QByteArray &serviceName, const QByteArray &serviceType, const QByteArray &domain); // domain may be empty - int reg(const QByteArray &serviceName, const QByteArray &serviceType, const QByteArray &domain, int port, const QByteArray &txtRecord); + int reg(const QByteArray &serviceName, const QByteArray &serviceType, const QByteArray &domain, int port, + const QByteArray &txtRecord); // return -1 on error, else a record id int recordAdd(int reg_id, const Record &rec, LowLevelError *lowLevelError = 0); @@ -156,4 +138,4 @@ class QDnsSd : public QObject Private *d; }; -#endif +#endif // QDNSSD_H diff --git a/src/irisnet/appledns/sdtest.cpp b/src/irisnet/appledns/sdtest.cpp index 5a69fe02..058c49f9 100644 --- a/src/irisnet/appledns/sdtest.cpp +++ b/src/irisnet/appledns/sdtest.cpp @@ -16,55 +16,42 @@ * */ -#include -#include #include "qdnssd.h" -// for ntohl -#ifdef Q_OS_WIN -# include +#include +#include +#ifdef Q_OS_WIN // for ntohl +#include #else -# include +#include #endif -class Command -{ +class Command { public: - enum Type - { - Query, - Browse, - Resolve, - Reg - }; + enum Type { Query, Browse, Resolve, Reg }; Type type; - QString name; // query, resolve, reg - int rtype; // query - QString stype; // browse, resolve, reg - QString domain; // browse, resolve, reg - int port; // reg + QString name; // query, resolve, reg + int rtype; // query + QString stype; // browse, resolve, reg + QString domain; // browse, resolve, reg + int port; // reg QByteArray txtRecord; // reg - int id; - int dnsId; + int id; + int dnsId; bool error; bool done; // for resolve - Command() : - error(false), - done(false) - { - } + Command() : error(false), done(false) { } }; static QString nameToString(const QByteArray &in) { QStringList parts; - int at = 0; - while(at < in.size()) - { + int at = 0; + while (at < in.size()) { int len = in[at++]; parts += QString::fromUtf8(in.mid(at, len)); at += len; @@ -76,20 +63,17 @@ static QString recordToDesc(const QDnsSd::Record &rec) { QString desc; - if(rec.rrtype == 1) // A + if (rec.rrtype == 1) // A { quint32 *p = (quint32 *)rec.rdata.data(); - desc = QHostAddress(ntohl(*p)).toString(); - } - else if(rec.rrtype == 28) // AAAA + desc = QHostAddress(ntohl(*p)).toString(); + } else if (rec.rrtype == 28) // AAAA { desc = QHostAddress((quint8 *)rec.rdata.data()).toString(); - } - else if(rec.rrtype == 12) // PTR + } else if (rec.rrtype == 12) // PTR { desc = QString("[%1]").arg(nameToString(rec.rdata)); - } - else + } else desc = QString("%1 bytes").arg(rec.rdata.size()); return desc; @@ -98,11 +82,11 @@ static QString recordToDesc(const QDnsSd::Record &rec) static QStringList txtRecordToStringList(const QByteArray &rdata) { QList txtEntries = QDnsSd::parseTxtRecord(rdata); - if(txtEntries.isEmpty()) + if (txtEntries.isEmpty()) return QStringList(); QStringList out; - foreach(const QByteArray &entry, txtEntries) + foreach (const QByteArray &entry, txtEntries) out += QString::fromUtf8(entry); return out; } @@ -110,64 +94,56 @@ static QStringList txtRecordToStringList(const QByteArray &rdata) static void printIndentedTxt(const QByteArray &txtRecord) { QStringList list = txtRecordToStringList(txtRecord); - if(!list.isEmpty()) - { - foreach(const QString &s, list) + if (!list.isEmpty()) { + foreach (const QString &s, list) printf(" %s\n", qPrintable(s)); - } - else + } else printf(" (TXT parsing error)\n"); } -class App : public QObject -{ +class App : public QObject { Q_OBJECT public: QList commands; - QDnsSd *dns; + QDnsSd *dns; App() { dns = new QDnsSd(this); - connect(dns, SIGNAL(queryResult(int,QDnsSd::QueryResult)), SLOT(dns_queryResult(int,QDnsSd::QueryResult))); - connect(dns, SIGNAL(browseResult(int,QDnsSd::BrowseResult)), SLOT(dns_browseResult(int,QDnsSd::BrowseResult))); - connect(dns, SIGNAL(resolveResult(int,QDnsSd::ResolveResult)), SLOT(dns_resolveResult(int,QDnsSd::ResolveResult))); - connect(dns, SIGNAL(regResult(int,QDnsSd::RegResult)), SLOT(dns_regResult(int,QDnsSd::RegResult))); + connect(dns, SIGNAL(queryResult(int, QDnsSd::QueryResult)), SLOT(dns_queryResult(int, QDnsSd::QueryResult))); + connect(dns, SIGNAL(browseResult(int, QDnsSd::BrowseResult)), + SLOT(dns_browseResult(int, QDnsSd::BrowseResult))); + connect(dns, SIGNAL(resolveResult(int, QDnsSd::ResolveResult)), + SLOT(dns_resolveResult(int, QDnsSd::ResolveResult))); + connect(dns, SIGNAL(regResult(int, QDnsSd::RegResult)), SLOT(dns_regResult(int, QDnsSd::RegResult))); } public slots: void start() { - for(int n = 0; n < commands.count(); ++n) - { + for (int n = 0; n < commands.count(); ++n) { Command &c = commands[n]; c.id = n; - if(c.type == Command::Query) - { + if (c.type == Command::Query) { printf("%2d: Query name=[%s], type=%d ...\n", c.id, qPrintable(c.name), c.rtype); c.dnsId = dns->query(c.name.toUtf8(), c.rtype); - } - else if(c.type == Command::Browse) - { + } else if (c.type == Command::Browse) { printf("%2d: Browse type=[%s]", c.id, qPrintable(c.stype)); - if(!c.domain.isEmpty()) + if (!c.domain.isEmpty()) printf(", domain=[%s]", qPrintable(c.domain)); printf(" ...\n"); c.dnsId = dns->browse(c.stype.toUtf8(), c.domain.toUtf8()); - } - else if(c.type == Command::Resolve) - { - printf("%2d: Resolve name=[%s], type=[%s], domain=[%s] ...\n", c.id, qPrintable(c.name), qPrintable(c.stype), qPrintable(c.domain)); + } else if (c.type == Command::Resolve) { + printf("%2d: Resolve name=[%s], type=[%s], domain=[%s] ...\n", c.id, qPrintable(c.name), + qPrintable(c.stype), qPrintable(c.domain)); c.dnsId = dns->resolve(c.name.toUtf8(), c.stype.toUtf8(), c.domain.toUtf8()); - } - else if(c.type == Command::Reg) - { + } else if (c.type == Command::Reg) { printf("%2d: Register name=[%s], type=[%s]", c.id, qPrintable(c.name), qPrintable(c.stype)); - if(!c.domain.isEmpty()) + if (!c.domain.isEmpty()) printf(", domain=[%s]", qPrintable(c.domain)); printf(", port=%d ...\n", c.port); - if(!c.txtRecord.isEmpty()) + if (!c.txtRecord.isEmpty()) printIndentedTxt(c.txtRecord); c.dnsId = dns->reg(c.name.toUtf8(), c.stype.toUtf8(), c.domain.toUtf8(), c.port, c.txtRecord); @@ -181,10 +157,9 @@ public slots: private: int cmdIdToCmdIndex(int cmdId) { - for(int n = 0; n < commands.count(); ++n) - { + for (int n = 0; n < commands.count(); ++n) { const Command &c = commands[n]; - if(c.id == cmdId) + if (c.id == cmdId) return n; } return -1; @@ -192,10 +167,9 @@ public slots: int dnsIdToCmdIndex(int dnsId) { - for(int n = 0; n < commands.count(); ++n) - { + for (int n = 0; n < commands.count(); ++n) { const Command &c = commands[n]; - if(c.dnsId == dnsId) + if (c.dnsId == dnsId) return n; } return -1; @@ -205,29 +179,27 @@ public slots: { // quit if there are nothing but errors or completed resolves bool doQuit = true; - foreach(const Command &c, commands) - { - if(c.error || (c.type == Command::Resolve && c.done)) + foreach (const Command &c, commands) { + if (c.error || (c.type == Command::Resolve && c.done)) continue; doQuit = false; break; } - if(doQuit) + if (doQuit) emit quit(); } private slots: void dns_queryResult(int id, const QDnsSd::QueryResult &result) { - int at = dnsIdToCmdIndex(id); - Command &c = commands[at]; + int at = dnsIdToCmdIndex(id); + Command &c = commands[at]; - if(!result.success) - { + if (!result.success) { printf("%2d: Error.", c.id); - if(!result.lowLevelError.func.isEmpty()) + if (!result.lowLevelError.func.isEmpty()) printf(" (%s, %d)", qPrintable(result.lowLevelError.func), result.lowLevelError.code); printf("\n"); c.error = true; @@ -235,28 +207,24 @@ private slots: return; } - foreach(const QDnsSd::Record &rec, result.records) - { - if(rec.added) - { + foreach (const QDnsSd::Record &rec, result.records) { + if (rec.added) { printf("%2d: Added: %s, ttl=%u\n", c.id, qPrintable(recordToDesc(rec)), rec.ttl); - if(rec.rrtype == 16) + if (rec.rrtype == 16) printIndentedTxt(rec.rdata); - } - else + } else printf("%2d: Removed: %s, ttl=%u\n", c.id, qPrintable(recordToDesc(rec)), rec.ttl); } } void dns_browseResult(int id, const QDnsSd::BrowseResult &result) { - int at = dnsIdToCmdIndex(id); - Command &c = commands[at]; + int at = dnsIdToCmdIndex(id); + Command &c = commands[at]; - if(!result.success) - { + if (!result.success) { printf("%2d: Error.", c.id); - if(!result.lowLevelError.func.isEmpty()) + if (!result.lowLevelError.func.isEmpty()) printf(" (%s, %d)", qPrintable(result.lowLevelError.func), result.lowLevelError.code); printf("\n"); c.error = true; @@ -264,10 +232,10 @@ private slots: return; } - foreach(const QDnsSd::BrowseEntry &e, result.entries) - { - if(e.added) - printf("%2d: Added: [%s] [%s] [%s]\n", c.id, qPrintable(QString::fromUtf8(e.serviceName)), qPrintable(QString::fromUtf8(e.serviceType)), qPrintable(QString::fromUtf8(e.replyDomain))); + foreach (const QDnsSd::BrowseEntry &e, result.entries) { + if (e.added) + printf("%2d: Added: [%s] [%s] [%s]\n", c.id, qPrintable(QString::fromUtf8(e.serviceName)), + qPrintable(QString::fromUtf8(e.serviceType)), qPrintable(QString::fromUtf8(e.replyDomain))); else printf("%2d: Removed: [%s]\n", c.id, qPrintable(QString::fromUtf8(e.serviceName))); } @@ -275,13 +243,12 @@ private slots: void dns_resolveResult(int id, const QDnsSd::ResolveResult &result) { - int at = dnsIdToCmdIndex(id); - Command &c = commands[at]; + int at = dnsIdToCmdIndex(id); + Command &c = commands[at]; - if(!result.success) - { + if (!result.success) { printf("%2d: Error.", c.id); - if(!result.lowLevelError.func.isEmpty()) + if (!result.lowLevelError.func.isEmpty()) printf(" (%s, %d)", qPrintable(result.lowLevelError.func), result.lowLevelError.code); printf("\n"); c.error = true; @@ -290,7 +257,7 @@ private slots: } printf("%2d: Result: host=[%s] port=%d\n", c.id, qPrintable(QString::fromUtf8(result.hostTarget)), result.port); - if(!result.txtRecord.isEmpty()) + if (!result.txtRecord.isEmpty()) printIndentedTxt(result.txtRecord); c.done = true; @@ -299,18 +266,17 @@ private slots: void dns_regResult(int id, const QDnsSd::RegResult &result) { - int at = dnsIdToCmdIndex(id); - Command &c = commands[at]; + int at = dnsIdToCmdIndex(id); + Command &c = commands[at]; - if(!result.success) - { + if (!result.success) { QString errstr; - if(result.errorCode == QDnsSd::RegResult::ErrorConflict) + if (result.errorCode == QDnsSd::RegResult::ErrorConflict) errstr = "Conflict"; else errstr = "Generic"; printf("%2d: Error (%s).", c.id, qPrintable(errstr)); - if(!result.lowLevelError.func.isEmpty()) + if (!result.lowLevelError.func.isEmpty()) printf(" (%s, %d)", qPrintable(result.lowLevelError.func), result.lowLevelError.code); printf("\n"); c.error = true; @@ -343,8 +309,7 @@ int main(int argc, char **argv) QStringList args = qapp.arguments(); args.removeFirst(); - if(args.count() < 1) - { + if (args.count() < 1) { usage(); return 1; } @@ -352,35 +317,28 @@ int main(int argc, char **argv) // options QStringList txt; - for(int n = 0; n < args.count(); ++n) - { + for (int n = 0; n < args.count(); ++n) { QString s = args[n]; - if(!s.startsWith("--")) + if (!s.startsWith("--")) continue; QString var; QString val; - int x = s.indexOf('='); - if(x != -1) - { + int x = s.indexOf('='); + if (x != -1) { var = s.mid(2, x - 2); val = s.mid(x + 1); - } - else - { + } else { var = s.mid(2); } bool known = true; - if(var == "txt") - { + if (var == "txt") { txt = val.split(','); - } - else + } else known = false; - if(known) - { + if (known) { args.removeAt(n); --n; // adjust position } @@ -389,89 +347,74 @@ int main(int argc, char **argv) // commands QList commands; - for(int n = 0; n < args.count(); ++n) - { + for (int n = 0; n < args.count(); ++n) { QString str = args[n]; - int n = str.indexOf('='); - if(n == -1) - { + int n = str.indexOf('='); + if (n == -1) { printf("Error: bad format of command.\n"); return 1; } - QString type = str.mid(0, n); - QString rest = str.mid(n + 1); + QString type = str.mid(0, n); + QString rest = str.mid(n + 1); QStringList parts = rest.split(','); - if(type == "q") - { - if(parts.count() < 2) - { + if (type == "q") { + if (parts.count() < 2) { usage(); return 1; } Command c; - c.type = Command::Query; - c.name = parts[0]; + c.type = Command::Query; + c.name = parts[0]; c.rtype = parts[1].toInt(); commands += c; - } - else if(type == "b") - { - if(parts.count() < 1) - { + } else if (type == "b") { + if (parts.count() < 1) { usage(); return 1; } Command c; - c.type = Command::Browse; + c.type = Command::Browse; c.stype = parts[0]; - if(parts.count() >= 2) + if (parts.count() >= 2) c.domain = parts[1]; commands += c; - } - else if(type == "r") - { - if(parts.count() < 3) - { + } else if (type == "r") { + if (parts.count() < 3) { usage(); return 1; } Command c; - c.type = Command::Resolve; - c.name = parts[0]; - c.stype = parts[1]; + c.type = Command::Resolve; + c.name = parts[0]; + c.stype = parts[1]; c.domain = parts[2]; commands += c; - } - else if(type == "e") - { - if(parts.count() < 3) - { + } else if (type == "e") { + if (parts.count() < 3) { usage(); return 1; } Command c; - c.type = Command::Reg; - c.name = parts[0]; + c.type = Command::Reg; + c.name = parts[0]; c.stype = parts[1]; - c.port = parts[2].toInt(); - if(parts.count() >= 4) + c.port = parts[2].toInt(); + if (parts.count() >= 4) c.domain = parts[3]; - if(!txt.isEmpty()) - { + if (!txt.isEmpty()) { QList strings; - foreach(const QString &str, txt) + foreach (const QString &str, txt) strings += str.toUtf8(); QByteArray txtRecord = QDnsSd::createTxtRecord(strings); - if(txtRecord.isEmpty()) - { + if (txtRecord.isEmpty()) { printf("Error: failed to create TXT record, input too large or invalid\n"); return 1; } @@ -480,9 +423,7 @@ int main(int argc, char **argv) } commands += c; - } - else - { + } else { printf("Error: unknown command type '%s'.\n", qPrintable(type)); return 1; } diff --git a/src/irisnet/appledns/sdtest.pro b/src/irisnet/appledns/sdtest.pro deleted file mode 100644 index 0f914347..00000000 --- a/src/irisnet/appledns/sdtest.pro +++ /dev/null @@ -1,9 +0,0 @@ -CONFIG += console -CONFIG -= app_bundle -QT -= gui -QT += network - -HEADERS += qdnssd.h -SOURCES += qdnssd.cpp sdtest.cpp - -!mac:LIBS += -ldns_sd diff --git a/src/irisnet/corelib/addressresolver.cpp b/src/irisnet/corelib/addressresolver.cpp index 33a54578..98c4e961 100644 --- a/src/irisnet/corelib/addressresolver.cpp +++ b/src/irisnet/corelib/addressresolver.cpp @@ -18,39 +18,28 @@ #include "addressresolver.h" -#include "objectsession.h" #include "netnames.h" +#include "objectsession.h" namespace XMPP { - -class AddressResolver::Private : public QObject -{ +class AddressResolver::Private : public QObject { Q_OBJECT public: - enum State - { - AddressWait, - AddressFirstCome - }; - - AddressResolver *q; - ObjectSession sess; - State state; - NameResolver req6; - NameResolver req4; - bool done6; - bool done4; + enum State { AddressWait, AddressFirstCome }; + + AddressResolver *q; + ObjectSession sess; + State state; + NameResolver req6; + NameResolver req4; + bool done6; + bool done4; QList addrs6; QList addrs4; - QTimer *opTimer; - - Private(AddressResolver *_q) : - QObject(_q), - q(_q), - sess(this), - req6(this), - req4(this) + QTimer *opTimer; + + Private(AddressResolver *_q) : QObject(_q), q(_q), sess(this), req6(this), req4(this) { connect(&req6, SIGNAL(resultsReady(QList)), SLOT(req6_resultsReady(QList))); connect(&req6, SIGNAL(error(XMPP::NameResolver::Error)), SLOT(req6_error(XMPP::NameResolver::Error))); @@ -66,7 +55,7 @@ class AddressResolver::Private : public QObject ~Private() { opTimer->disconnect(this); - opTimer->setParent(0); + opTimer->setParent(nullptr); opTimer->deleteLater(); } @@ -76,12 +65,11 @@ class AddressResolver::Private : public QObject // was an IP address used as input? QHostAddress addr; - if(addr.setAddress(QString::fromLatin1(hostName))) - { + if (addr.setAddress(QString::fromLatin1(hostName))) { // use this as the result, no need to perform dns query done6 = true; done4 = true; - if(addr.protocol() == QAbstractSocket::IPv6Protocol) + if (addr.protocol() == QAbstractSocket::IPv6Protocol) addrs6 += addr; else addrs4 += addr; @@ -101,10 +89,7 @@ class AddressResolver::Private : public QObject req4.start(hostName, NameRecord::A); } - void stop() - { - cleanup(); - } + void stop() { cleanup(); } private: void cleanup() @@ -121,12 +106,11 @@ class AddressResolver::Private : public QObject bool tryDone() { - if((done6 && done4) || (state == AddressFirstCome && (done6 || done4))) - { + if ((done6 && done4) || (state == AddressFirstCome && (done6 || done4))) { QList results = addrs6 + addrs4; cleanup(); - if(!results.isEmpty()) + if (!results.isEmpty()) emit q->resultsReady(results); else emit q->error(ErrorGeneric); @@ -140,7 +124,7 @@ class AddressResolver::Private : public QObject private slots: void req6_resultsReady(const QList &results) { - foreach(const NameRecord &rec, results) + for (const NameRecord &rec : results) addrs6 += rec.address(); done6 = true; @@ -157,7 +141,7 @@ private slots: void req4_resultsReady(const QList &results) { - foreach(const NameRecord &rec, results) + for (const NameRecord &rec : results) addrs4 += rec.address(); done4 = true; @@ -176,37 +160,21 @@ private slots: { state = AddressFirstCome; - if(done6 || done4) + if (done6 || done4) tryDone(); } - void ipAddress_input() - { - tryDone(); - } + void ipAddress_input() { tryDone(); } }; -AddressResolver::AddressResolver(QObject *parent) : - QObject(parent) -{ - d = new Private(this); -} +AddressResolver::AddressResolver(QObject *parent) : QObject(parent) { d = new Private(this); } -AddressResolver::~AddressResolver() -{ - delete d; -} +AddressResolver::~AddressResolver() { delete d; } -void AddressResolver::start(const QByteArray &hostName) -{ - d->start(hostName); -} +void AddressResolver::start(const QByteArray &hostName) { d->start(hostName); } -void AddressResolver::stop() -{ - d->stop(); -} +void AddressResolver::stop() { d->stop(); } -} +} // namespace XMPP #include "addressresolver.moc" diff --git a/src/irisnet/corelib/addressresolver.h b/src/irisnet/corelib/addressresolver.h index c4186b79..417574af 100644 --- a/src/irisnet/corelib/addressresolver.h +++ b/src/irisnet/corelib/addressresolver.h @@ -19,23 +19,18 @@ #ifndef ADDRESSRESOLVER_H #define ADDRESSRESOLVER_H -#include #include +#include namespace XMPP { - // resolve both AAAA and A for a hostname -class AddressResolver : public QObject -{ +class AddressResolver : public QObject { Q_OBJECT public: - enum Error - { - ErrorGeneric - }; + enum Error { ErrorGeneric }; - AddressResolver(QObject *parent = 0); + AddressResolver(QObject *parent = nullptr); ~AddressResolver(); void start(const QByteArray &hostName); @@ -50,7 +45,6 @@ class AddressResolver : public QObject friend class Private; Private *d; }; +} // namespace XMPP -} - -#endif +#endif // ADDRESSRESOLVER_H diff --git a/src/irisnet/corelib/corelib.pri b/src/irisnet/corelib/corelib.pri deleted file mode 100644 index 60a2ef44..00000000 --- a/src/irisnet/corelib/corelib.pri +++ /dev/null @@ -1,48 +0,0 @@ -QT *= network - -HEADERS += \ - $$PWD/objectsession.h \ - $$PWD/irisnetexport.h \ - $$PWD/irisnetplugin.h \ - $$PWD/irisnetglobal.h \ - $$PWD/irisnetglobal_p.h \ - $$PWD/netinterface.h \ - $$PWD/netavailability.h \ - $$PWD/netnames.h \ - $$PWD/addressresolver.h - -SOURCES += \ - $$PWD/objectsession.cpp \ - $$PWD/irisnetplugin.cpp \ - $$PWD/irisnetglobal.cpp \ - $$PWD/netinterface.cpp \ - $$PWD/netinterface_qtnet.cpp \ - $$PWD/netavailability.cpp \ - $$PWD/netnames.cpp \ - $$PWD/addressresolver.cpp - -unix { - SOURCES += \ - $$PWD/netinterface_unix.cpp -} - -need_jdns|lessThan(QT_MAJOR_VERSION, 5) { - !ext-qjdns { - include(../../jdns/jdns.pri) - INCLUDEPATH += $$PWD/../../jdns - } - - SOURCES += \ - $$PWD/netnames_jdns.cpp - - DEFINES += NEED_JDNS -} else { - SOURCES += $$PWD/netinterface_qtname.cpp \ -} - -#include(legacy/legacy.pri) - -appledns:appledns_bundle { - DEFINES += APPLEDNS_STATIC - include(../appledns/appledns.pri) -} diff --git a/src/irisnet/corelib/corelib.pro b/src/irisnet/corelib/corelib.pro deleted file mode 100644 index 1df24aaa..00000000 --- a/src/irisnet/corelib/corelib.pro +++ /dev/null @@ -1,17 +0,0 @@ -IRIS_BASE = ../../.. - -TEMPLATE = lib -QT -= gui -TARGET = irisnetcore -DESTDIR = $$IRIS_BASE/lib -windows:DLLDESTDIR = $$IRIS_BASE/bin - -VERSION = 1.0.0 - -include(../../libbase.pri) -include(corelib.pri) - -# fixme: irisnetcore builds as dll or bundled, never static? -CONFIG += create_prl -windows:!staticlib:DEFINES += IRISNET_MAKEDLL -staticlib:PRL_EXPORT_DEFINES += IRISNET_STATIC diff --git a/src/irisnet/corelib/irisnetexport.h b/src/irisnet/corelib/irisnetexport.h index 55f2226b..0e07278b 100644 --- a/src/irisnet/corelib/irisnetexport.h +++ b/src/irisnet/corelib/irisnetexport.h @@ -22,13 +22,13 @@ #include #ifdef IRISNET_STATIC -# define IRISNET_EXPORT +#define IRISNET_EXPORT #else -# ifdef IRISNET_MAKEDLL -# define IRISNET_EXPORT Q_DECL_EXPORT -# else -# define IRISNET_EXPORT Q_DECL_IMPORT -# endif +#ifdef IRISNET_MAKEDLL +#define IRISNET_EXPORT Q_DECL_EXPORT +#else +#define IRISNET_EXPORT Q_DECL_IMPORT #endif - #endif + +#endif // IRISNETEXPORT_H diff --git a/src/irisnet/corelib/irisnetglobal.cpp b/src/irisnet/corelib/irisnetglobal.cpp index baee4c95..91ebf346 100644 --- a/src/irisnet/corelib/irisnetglobal.cpp +++ b/src/irisnet/corelib/irisnetglobal.cpp @@ -16,14 +16,14 @@ * */ -#include "irisnetglobal_p.h" - +#include "corelib/irisnetglobal_p.h" #include "irisnetplugin.h" namespace XMPP { - // built-in providers +#ifdef HAVE_QTNET extern IrisNetProvider *irisnet_createQtNetProvider(); +#endif #ifdef Q_OS_UNIX extern IrisNetProvider *irisnet_createUnixNetProvider(); #endif @@ -39,65 +39,59 @@ extern IrisNetProvider *irisnet_createAppleProvider(); //---------------------------------------------------------------------------- // internal //---------------------------------------------------------------------------- -class PluginInstance -{ +class PluginInstance { private: - QPluginLoader *_loader; - QObject *_instance; - bool _ownInstance; + QPluginLoader *_loader = nullptr; + QObject *_instance = nullptr; + bool _ownInstance = false; - PluginInstance() - { - } + PluginInstance() { } public: static PluginInstance *fromFile(const QString &fname) { QPluginLoader *loader = new QPluginLoader(fname); - if(!loader->load()) - { + if (!loader->load()) { delete loader; - return 0; + return nullptr; } QObject *obj = loader->instance(); - if(!obj) - { + if (!obj) { loader->unload(); delete loader; - return 0; + return nullptr; } PluginInstance *i = new PluginInstance; - i->_loader = loader; - i->_instance = obj; - i->_ownInstance = true; + i->_loader = loader; + i->_instance = obj; + i->_ownInstance = true; return i; } static PluginInstance *fromStatic(QObject *obj) { PluginInstance *i = new PluginInstance; - i->_loader = 0; - i->_instance = obj; - i->_ownInstance = false; + i->_loader = nullptr; + i->_instance = obj; + i->_ownInstance = false; return i; } static PluginInstance *fromInstance(QObject *obj) { PluginInstance *i = new PluginInstance; - i->_loader = 0; - i->_instance = obj; - i->_ownInstance = true; + i->_loader = nullptr; + i->_instance = obj; + i->_ownInstance = true; return i; } ~PluginInstance() { - if(_ownInstance) + if (_ownInstance) delete _instance; - if(_loader) - { + if (_loader) { _loader->unload(); delete _loader; } @@ -105,64 +99,50 @@ class PluginInstance void claim() { - if(_loader) + if (_loader) _loader->moveToThread(0); - if(_ownInstance) - _instance->moveToThread(0); + if (_ownInstance) + _instance->moveToThread(nullptr); } - QObject *instance() - { - return _instance; - } + QObject *instance() { return _instance; } bool sameType(const PluginInstance *other) { - if(!_instance || !other->_instance) - return false; - - if(qstrcmp(_instance->metaObject()->className(), other->_instance->metaObject()->className()) != 0) + if (!_instance || !other->_instance) return false; - return true; + return qstrcmp(_instance->metaObject()->className(), other->_instance->metaObject()->className()) == 0; } }; -class PluginManager -{ +class PluginManager { public: - bool builtin_done; - QStringList paths; - QList plugins; - QList providers; + bool builtin_done; + QStringList paths; + QList plugins; + QList providers; - PluginManager() - { - builtin_done = false; - } + PluginManager() { builtin_done = false; } - ~PluginManager() - { - unload(); - } + ~PluginManager() { unload(); } bool tryAdd(PluginInstance *i, bool lowPriority = false) { // is it the right kind of plugin? - IrisNetProvider *p = qobject_cast(i->instance()); - if(!p) + IrisNetProvider *p = qobject_cast(i->instance()); + if (!p) return false; // make sure we don't have it already - for(int n = 0; n < plugins.count(); ++n) - { - if(i->sameType(plugins[n])) + for (int n = 0; n < plugins.count(); ++n) { + if (i->sameType(plugins[n])) return false; } i->claim(); plugins += i; - if(lowPriority) + if (lowPriority) providers.append(p); else providers.prepend(p); @@ -172,15 +152,16 @@ class PluginManager void addBuiltIn(IrisNetProvider *p) { PluginInstance *i = PluginInstance::fromInstance(p); - if(!tryAdd(i, true)) + if (!tryAdd(i, true)) delete i; } void scan() { - if(!builtin_done) - { + if (!builtin_done) { +#ifdef HAVE_QTNET addBuiltIn(irisnet_createQtNetProvider()); // interfaces. crossplatform. no need to reimplement +#endif #ifdef Q_OS_UNIX addBuiltIn(irisnet_createUnixNetProvider()); // gateways #endif @@ -193,30 +174,27 @@ class PluginManager } QObjectList list = QPluginLoader::staticInstances(); - for(int n = 0; n < list.count(); ++n) - { + for (int n = 0; n < list.count(); ++n) { PluginInstance *i = PluginInstance::fromStatic(list[n]); - if(!tryAdd(i)) + if (!tryAdd(i)) delete i; } - for(int n = 0; n < paths.count(); ++n) - { + for (int n = 0; n < paths.count(); ++n) { QDir dir(paths[n]); - if(!dir.exists()) + if (!dir.exists()) continue; QStringList entries = dir.entryList(); - for(int k = 0; k < entries.count(); ++k) - { + for (int k = 0; k < entries.count(); ++k) { QFileInfo fi(dir.filePath(entries[k])); - if(!fi.exists()) + if (!fi.exists()) continue; - QString fname = fi.filePath(); - PluginInstance *i = PluginInstance::fromFile(fname); - if(!i) + QString fname = fi.filePath(); + PluginInstance *i = PluginInstance::fromFile(fname); + if (!i) continue; - if(!tryAdd(i)) + if (!tryAdd(i)) delete i; } } @@ -225,8 +203,8 @@ class PluginManager void unload() { // unload in reverse order - QList revlist; - for(int n = 0; n < plugins.count(); ++n) + QList revlist; + for (int n = 0; n < plugins.count(); ++n) revlist.prepend(plugins[n]); qDeleteAll(revlist); @@ -235,39 +213,39 @@ class PluginManager } }; -class IrisNetGlobal -{ +class IrisNetGlobal { public: - QMutex m; - PluginManager pluginManager; + QMutex m; + PluginManager pluginManager; QList cleanupList; }; Q_GLOBAL_STATIC(QMutex, global_mutex) -static IrisNetGlobal *global = 0; +static IrisNetGlobal *global = nullptr; static void deinit(); static void init() { QMutexLocker locker(global_mutex()); - if(global) + if (global) return; + qRegisterMetaType("QHostAddress"); global = new IrisNetGlobal; qAddPostRoutine(deinit); } void deinit() { - if(!global) + if (!global) return; - while(!global->cleanupList.isEmpty()) + while (!global->cleanupList.isEmpty()) (global->cleanupList.takeFirst())(); delete global; - global = 0; + global = nullptr; } //---------------------------------------------------------------------------- @@ -295,7 +273,7 @@ void irisNetAddPostRoutine(IrisNetCleanUpFunction func) global->cleanupList.prepend(func); } -QList irisNetProviders() +QList irisNetProviders() { init(); @@ -304,4 +282,4 @@ QList irisNetProviders() return global->pluginManager.providers; } -} +} // namespace XMPP diff --git a/src/irisnet/corelib/irisnetglobal.h b/src/irisnet/corelib/irisnetglobal.h index 9aae0198..8d62337e 100644 --- a/src/irisnet/corelib/irisnetglobal.h +++ b/src/irisnet/corelib/irisnetglobal.h @@ -19,19 +19,17 @@ #ifndef IRISNETGLOBAL_H #define IRISNETGLOBAL_H +#include "irisnetexport.h" + #include #include -#include "irisnetexport.h" namespace XMPP { - // set the directories for plugins. call before doing anything else. IRISNET_EXPORT void irisNetSetPluginPaths(const QStringList &paths); - // free any shared data and plugins. // note: this is automatically called when qapp shuts down. IRISNET_EXPORT void irisNetCleanup(); +} // namespace XMPP -} - -#endif +#endif // IRISNETGLOBAL_H diff --git a/src/irisnet/corelib/irisnetglobal_p.h b/src/irisnet/corelib/irisnetglobal_p.h index 4b9d070e..0ff31856 100644 --- a/src/irisnet/corelib/irisnetglobal_p.h +++ b/src/irisnet/corelib/irisnetglobal_p.h @@ -23,12 +23,10 @@ #include "irisnetplugin.h" namespace XMPP { - typedef void (*IrisNetCleanUpFunction)(); IRISNET_EXPORT void irisNetAddPostRoutine(IrisNetCleanUpFunction func); -IRISNET_EXPORT QList irisNetProviders(); - -} +IRISNET_EXPORT QList irisNetProviders(); +} // namespace XMPP -#endif +#endif // IRISNETGLOBAL_P_H diff --git a/src/irisnet/corelib/irisnetplugin.cpp b/src/irisnet/corelib/irisnetplugin.cpp index fb7b8754..a2d24fe7 100644 --- a/src/irisnet/corelib/irisnetplugin.cpp +++ b/src/irisnet/corelib/irisnetplugin.cpp @@ -19,52 +19,27 @@ #include "irisnetplugin.h" namespace XMPP { - //---------------------------------------------------------------------------- // IrisNetProvider //---------------------------------------------------------------------------- -NetInterfaceProvider *IrisNetProvider::createNetInterfaceProvider() -{ - return 0; -} +NetInterfaceProvider *IrisNetProvider::createNetInterfaceProvider() { return nullptr; } -NetGatewayProvider *IrisNetProvider::createNetGatewayProvider() -{ - return 0; -} +NetGatewayProvider *IrisNetProvider::createNetGatewayProvider() { return nullptr; } -NetAvailabilityProvider *IrisNetProvider::createNetAvailabilityProvider() -{ - return 0; -} +NetAvailabilityProvider *IrisNetProvider::createNetAvailabilityProvider() { return nullptr; } -NameProvider *IrisNetProvider::createNameProviderInternet() -{ - return 0; -} +NameProvider *IrisNetProvider::createNameProviderInternet() { return nullptr; } -NameProvider *IrisNetProvider::createNameProviderLocal() -{ - return 0; -} +NameProvider *IrisNetProvider::createNameProviderLocal() { return nullptr; } -ServiceProvider *IrisNetProvider::createServiceProvider() -{ - return 0; -} +ServiceProvider *IrisNetProvider::createServiceProvider() { return nullptr; } //---------------------------------------------------------------------------- // NameProvider //---------------------------------------------------------------------------- -bool NameProvider::supportsSingle() const -{ - return false; -} +bool NameProvider::supportsSingle() const { return false; } -bool NameProvider::supportsLongLived() const -{ - return false; -} +bool NameProvider::supportsLongLived() const { return false; } bool NameProvider::supportsRecordType(int type) const { @@ -84,4 +59,4 @@ void NameProvider::resolve_localError(int id, XMPP::NameResolver::Error e) Q_UNUSED(e); } -} +} // namespace XMPP diff --git a/src/irisnet/corelib/irisnetplugin.h b/src/irisnet/corelib/irisnetplugin.h index 88fad7f1..67941a47 100644 --- a/src/irisnet/corelib/irisnetplugin.h +++ b/src/irisnet/corelib/irisnetplugin.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006,2008 Justin Karneges + * Copyright (C) 2006-2008 Justin Karneges * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -20,119 +20,99 @@ #define IRISNETPLUGIN_H #include "irisnetglobal.h" -#include "netinterface.h" #include "netavailability.h" +#include "netinterface.h" #include "netnames.h" namespace XMPP { - -class NetInterfaceProvider; -class NetGatewayProvider; -class NetAvailabilityProvider; class NameProvider; +class NetAvailabilityProvider; +class NetGatewayProvider; +class NetInterfaceProvider; class ServiceProvider; -class IRISNET_EXPORT IrisNetProvider : public QObject -{ +class IRISNET_EXPORT IrisNetProvider : public QObject { Q_OBJECT public: - virtual NetInterfaceProvider *createNetInterfaceProvider(); - virtual NetGatewayProvider *createNetGatewayProvider(); + virtual NetInterfaceProvider *createNetInterfaceProvider(); + virtual NetGatewayProvider *createNetGatewayProvider(); virtual NetAvailabilityProvider *createNetAvailabilityProvider(); - virtual NameProvider *createNameProviderInternet(); - virtual NameProvider *createNameProviderLocal(); - virtual ServiceProvider *createServiceProvider(); + virtual NameProvider *createNameProviderInternet(); + virtual NameProvider *createNameProviderLocal(); + virtual ServiceProvider *createServiceProvider(); }; -class IRISNET_EXPORT NetInterfaceProvider : public QObject -{ +class IRISNET_EXPORT NetInterfaceProvider : public QObject { Q_OBJECT public: - class Info - { + class Info { public: - QString id, name; - bool isLoopback; + QString id, name; + bool isLoopback; QList addresses; }; - NetInterfaceProvider(QObject *parent = 0) : - QObject(parent) - { - } + NetInterfaceProvider(QObject *parent = nullptr) : QObject(parent) { } // calling start should populate an initial list that can be // immediately fetched. do not signal updated() for this. - virtual void start() = 0; + virtual void start() = 0; virtual QList interfaces() const = 0; signals: void updated(); }; -class IRISNET_EXPORT NetGatewayProvider : public QObject -{ +class IRISNET_EXPORT NetGatewayProvider : public QObject { Q_OBJECT public: - class Info - { + class Info { public: - QString ifaceId; + QString ifaceId; QHostAddress gateway; }; - NetGatewayProvider(QObject *parent = 0) : - QObject(parent) - { - } + NetGatewayProvider(QObject *parent = nullptr) : QObject(parent) { } // calling start should populate an initial list that can be // immediately fetched. do not signal updated() for this. - virtual void start() = 0; + virtual void start() = 0; virtual QList gateways() const = 0; signals: void updated(); }; -class IRISNET_EXPORT NetAvailabilityProvider : public QObject -{ +class IRISNET_EXPORT NetAvailabilityProvider : public QObject { Q_OBJECT public: - NetAvailabilityProvider(QObject *parent = 0) : - QObject(parent) - { - } + NetAvailabilityProvider(QObject *parent = nullptr) : QObject(parent) { } // calling start should populate an initial value that can be // immediately fetched. do not signal updated() for this. - virtual void start() = 0; + virtual void start() = 0; virtual bool isAvailable() const = 0; signals: void updated(); }; -class IRISNET_EXPORT NameProvider : public QObject -{ +class IRISNET_EXPORT NameProvider : public QObject { Q_OBJECT public: - NameProvider(QObject *parent = 0) : - QObject(parent) - { - } + NameProvider(QObject *parent = nullptr) : QObject(parent) { } virtual bool supportsSingle() const; virtual bool supportsLongLived() const; virtual bool supportsRecordType(int type) const; - virtual int resolve_start(const QByteArray &name, int qType, bool longLived) = 0; - virtual void resolve_stop(int id) = 0; + virtual int resolve_start(const QByteArray &name, int qType, bool longLived) = 0; + virtual void resolve_stop(int id) = 0; // transfer from local back to internet virtual void resolve_localResultsReady(int id, const QList &results); @@ -146,35 +126,32 @@ class IRISNET_EXPORT NameProvider : public QObject void resolve_useLocal(int id, const QByteArray &name); }; -class IRISNET_EXPORT ServiceProvider : public QObject -{ +class IRISNET_EXPORT ServiceProvider : public QObject { Q_OBJECT public: - class ResolveResult - { + class ResolveResult { public: - QMap attributes; - QHostAddress address; - int port; - QByteArray hostName; // optional + QMap attributes; + QHostAddress address; + int port; + QString hostName; // optional }; - ServiceProvider(QObject *parent = 0) : - QObject(parent) - { - } - - virtual int browse_start(const QString &type, const QString &domain) = 0; - virtual void browse_stop(int id) = 0; - virtual int resolve_start(const QByteArray &name) = 0; - virtual void resolve_stop(int id) = 0; - virtual int publish_start(const QString &instance, const QString &type, int port, const QMap &attributes) = 0; - virtual void publish_update(int id, const QMap &attributes) = 0; - virtual void publish_stop(int id) = 0; - virtual int publish_extra_start(int pub_id, const NameRecord &name) = 0; - virtual void publish_extra_update(int id, const NameRecord &name) = 0; - virtual void publish_extra_stop(int id) = 0; + ServiceProvider(QObject *parent = nullptr) : QObject(parent) { } + + virtual int browse_start(const QString &type, const QString &domain) = 0; + virtual void browse_stop(int id) = 0; + virtual int resolve_start(const QByteArray &name) = 0; + virtual void resolve_stop(int id) = 0; + virtual int publish_start(const QString &instance, const QString &type, int port, + const QMap &attributes) + = 0; + virtual void publish_update(int id, const QMap &attributes) = 0; + virtual void publish_stop(int id) = 0; + virtual int publish_extra_start(int pub_id, const NameRecord &name) = 0; + virtual void publish_extra_update(int id, const NameRecord &name) = 0; + virtual void publish_extra_stop(int id) = 0; signals: void browse_instanceAvailable(int id, const XMPP::ServiceInstance &instance); @@ -191,13 +168,12 @@ class IRISNET_EXPORT ServiceProvider : public QObject void publish_extra_published(int id); void publish_extra_error(int id, XMPP::ServiceLocalPublisher::Error e); }; +} // namespace XMPP -} - -Q_DECLARE_INTERFACE(XMPP::NetGatewayProvider, "com.affinix.irisnet.IrisGatewayProvider/1.0") -Q_DECLARE_INTERFACE(XMPP::IrisNetProvider, "com.affinix.irisnet.IrisNetProvider/1.0") +Q_DECLARE_INTERFACE(XMPP::NetGatewayProvider, "com.affinix.irisnet.IrisGatewayProvider/1.0") +Q_DECLARE_INTERFACE(XMPP::IrisNetProvider, "com.affinix.irisnet.IrisNetProvider/1.0") Q_DECLARE_INTERFACE(XMPP::NetInterfaceProvider, "com.affinix.irisnet.NetInterfaceProvider/2.0") -Q_DECLARE_INTERFACE(XMPP::NameProvider, "com.affinix.irisnet.NameProvider/1.0") -Q_DECLARE_INTERFACE(XMPP::ServiceProvider, "com.affinix.irisnet.ServiceProvider/1.0") +Q_DECLARE_INTERFACE(XMPP::NameProvider, "com.affinix.irisnet.NameProvider/1.0") +Q_DECLARE_INTERFACE(XMPP::ServiceProvider, "com.affinix.irisnet.ServiceProvider/1.0") -#endif +#endif // IRISNETPLUGIN_H diff --git a/src/irisnet/corelib/netavailability.cpp b/src/irisnet/corelib/netavailability.cpp index 7c5db6ed..6ec68361 100644 --- a/src/irisnet/corelib/netavailability.cpp +++ b/src/irisnet/corelib/netavailability.cpp @@ -19,31 +19,18 @@ #include "netavailability.h" namespace XMPP { - -class NetAvailability::Private : public QObject -{ +class NetAvailability::Private : public QObject { Q_OBJECT public: NetAvailability *q; - Private(NetAvailability *_q) : - QObject(_q), - q(_q) - { - } + Private(NetAvailability *_q) : QObject(_q), q(_q) { } }; -NetAvailability::NetAvailability(QObject *parent) : - QObject(parent) -{ - d = new Private(this); -} +NetAvailability::NetAvailability(QObject *parent) : QObject(parent) { d = new Private(this); } -NetAvailability::~NetAvailability() -{ - delete d; -} +NetAvailability::~NetAvailability() { delete d; } bool NetAvailability::isAvailable() const { @@ -51,6 +38,6 @@ bool NetAvailability::isAvailable() const return true; } -} +} // namespace XMPP #include "netavailability.moc" diff --git a/src/irisnet/corelib/netavailability.h b/src/irisnet/corelib/netavailability.h index a53c45bf..aa9da916 100644 --- a/src/irisnet/corelib/netavailability.h +++ b/src/irisnet/corelib/netavailability.h @@ -22,13 +22,11 @@ #include "irisnetglobal.h" namespace XMPP { - -class NetAvailability : public QObject -{ +class NetAvailability : public QObject { Q_OBJECT public: - NetAvailability(QObject *parent = 0); + NetAvailability(QObject *parent = nullptr); ~NetAvailability(); bool isAvailable() const; @@ -41,7 +39,6 @@ class NetAvailability : public QObject friend class Private; Private *d; }; +} // namespace XMPP -} - -#endif +#endif // NETAVAILABILITY_H diff --git a/src/irisnet/corelib/netinterface.cpp b/src/irisnet/corelib/netinterface.cpp index a4459c50..0ceea7ed 100644 --- a/src/irisnet/corelib/netinterface.cpp +++ b/src/irisnet/corelib/netinterface.cpp @@ -18,34 +18,36 @@ #include "netinterface.h" +#include "corelib/irisnetglobal_p.h" #include "irisnetplugin.h" -#include "irisnetglobal_p.h" -#include -#include #include +#include +#include namespace XMPP { - //---------------------------------------------------------------------------- // NetTracker //---------------------------------------------------------------------------- class NetTracker : public QObject { Q_OBJECT public: - QList getInterfaces() { + QList getInterfaces() + { QMutexLocker locker(&m); return info; } - NetTracker() { - QList list = irisNetProviders(); + NetTracker() + { + QList list = irisNetProviders(); - c = 0; - foreach(IrisNetProvider* p, list) { + c = nullptr; + for (IrisNetProvider *p : list) { c = p->createNetInterfaceProvider(); - if(c) break; + if (c) + break; } Q_ASSERT(c); // we have built-in support, so this should never fail connect(c, SIGNAL(updated()), SLOT(c_updated())); @@ -54,28 +56,29 @@ class NetTracker : public QObject { info = filterList(c->interfaces()); } - ~NetTracker() { + ~NetTracker() + { QMutexLocker locker(&m); delete c; } - signals: void updated(); -private: - - static QList filterList(const QList &in) { +private: + static QList filterList(const QList &in) + { QList out; - for(int n = 0; n < in.count(); ++n) - { - if(!in[n].isLoopback) out += in[n]; + for (int n = 0; n < in.count(); ++n) { + if (!in[n].isLoopback) + out += in[n]; } return out; } private slots: - void c_updated() { + void c_updated() + { { QMutexLocker locker(&m); info = filterList(c->interfaces()); @@ -83,16 +86,13 @@ private slots: emit updated(); } - private: // this are all protected by m - NetInterfaceProvider *c; - QMutex m; + NetInterfaceProvider *c; + QMutex m; QList info; - }; - // Global because static getRef needs this too. Q_GLOBAL_STATIC(QMutex, nettracker_mutex) @@ -102,7 +102,8 @@ class NetTrackerThread : public QThread { /** Get a reference to the NetTracker singleton. Calls to getInterfaces will immediately give valid results */ - static NetTrackerThread* getRef() { + static NetTrackerThread *getRef() + { QMutexLocker locker(nettracker_mutex()); if (!self) { @@ -114,7 +115,8 @@ class NetTrackerThread : public QThread { /** Release reference. */ - void releaseRef() { + void releaseRef() + { QMutexLocker locker(nettracker_mutex()); Q_ASSERT(refs > 0); @@ -123,24 +125,23 @@ class NetTrackerThread : public QThread { exit(0); wait(); delete this; - self = 0; + self = nullptr; } } - QList getInterfaces() { - return nettracker->getInterfaces(); - } - + QList getInterfaces() { return nettracker->getInterfaces(); } - ~NetTrackerThread() { + ~NetTrackerThread() + { // locked from caller } - signals: void updated(); + private: - NetTrackerThread() { + NetTrackerThread() + { // locked from caller refs = 0; moveToThread(QCoreApplication::instance()->thread()); @@ -151,10 +152,11 @@ class NetTrackerThread : public QThread { startCond.wait(startMutex); // wait for thread startup finished } delete startMutex; - startMutex = 0; + startMutex = nullptr; } - void run() { + void run() + { { QMutexLocker locker(startMutex); @@ -165,26 +167,24 @@ class NetTrackerThread : public QThread { } exec(); delete nettracker; - nettracker = 0; + nettracker = nullptr; } private: QWaitCondition startCond; - QMutex *startMutex; + QMutex *startMutex = nullptr; // these are all protected by global nettracker_mutex. - int refs; + int refs = 0; static NetTrackerThread *self; - NetTracker *nettracker; + NetTracker *nettracker = nullptr; }; -NetTrackerThread *NetTrackerThread::self = 0; - +NetTrackerThread *NetTrackerThread::self = nullptr; //---------------------------------------------------------------------------- // NetInterface //---------------------------------------------------------------------------- -class NetInterfacePrivate : public QObject -{ +class NetInterfacePrivate : public QObject { Q_OBJECT public: friend class NetInterfaceManagerPrivate; @@ -192,36 +192,34 @@ class NetInterfacePrivate : public QObject NetInterface *q; QPointer man; - bool valid; - QString id, name; - QList addrs; + bool valid; + QString id, name; + QList addrs; - NetInterfacePrivate(NetInterface *_q) : QObject(_q), q(_q) - { - valid = false; - } + NetInterfacePrivate(NetInterface *_q) : QObject(_q), q(_q) { valid = false; } void doUnavailable() { - if (!valid) return; + if (!valid) + return; valid = false; - if (man.isNull()) return; + if (man.isNull()) + return; man->unreg(q); emit q->unavailable(); } }; -NetInterface::NetInterface(const QString &id, NetInterfaceManager *manager) - : QObject(manager) +NetInterface::NetInterface(const QString &id, NetInterfaceManager *manager) : QObject(manager) { - d = new NetInterfacePrivate(this); + d = new NetInterfacePrivate(this); d->man = manager; NetInterfaceProvider::Info *info = (NetInterfaceProvider::Info *)d->man->reg(id, this); if (info) { d->valid = true; - d->id = info->id; - d->name = info->name; + d->id = info->id; + d->name = info->name; d->addrs = info->addresses; delete info; } @@ -229,42 +227,30 @@ NetInterface::NetInterface(const QString &id, NetInterfaceManager *manager) NetInterface::~NetInterface() { - if (d->valid && !d->man.isNull()) d->man->unreg(this); + if (d->valid && !d->man.isNull()) + d->man->unreg(this); delete d; } -bool NetInterface::isValid() const -{ - return d->valid && !d->man.isNull(); -} +bool NetInterface::isValid() const { return d->valid && !d->man.isNull(); } -QString NetInterface::id() const -{ - return d->id; -} +QString NetInterface::id() const { return d->id; } -QString NetInterface::name() const -{ - return d->name; -} +QString NetInterface::name() const { return d->name; } -QList NetInterface::addresses() const -{ - return d->addrs; -} +QList NetInterface::addresses() const { return d->addrs; } //---------------------------------------------------------------------------- // NetInterfaceManager //---------------------------------------------------------------------------- -class NetInterfaceManagerPrivate : public QObject -{ +class NetInterfaceManagerPrivate : public QObject { Q_OBJECT public: NetInterfaceManager *q; QList info; - QList listeners; - NetTrackerThread *tracker; + QList listeners; + NetTrackerThread *tracker; bool pending; @@ -275,15 +261,17 @@ class NetInterfaceManagerPrivate : public QObject connect(tracker, SIGNAL(updated()), SLOT(tracker_updated())); } - ~NetInterfaceManagerPrivate() { + ~NetInterfaceManagerPrivate() + { tracker->releaseRef(); - tracker = 0; + tracker = nullptr; } static int lookup(const QList &list, const QString &id) { - for(int n = 0; n < list.count(); ++n) { - if(list[n].id == id) return n; + for (int n = 0; n < list.count(); ++n) { + if (list[n].id == id) + return n; } return -1; } @@ -302,13 +290,12 @@ class NetInterfaceManagerPrivate : public QObject QStringList here_ids, gone_ids; // removed / changed - for(int n = 0; n < info.count(); ++n) - { + for (int n = 0; n < info.count(); ++n) { int i = lookup(newinfo, info[n].id); // id is still here - if(i != -1) { + if (i != -1) { // content changed? - if(!sameContent(info[n], newinfo[i])) { + if (!sameContent(info[n], newinfo[i])) { gone_ids += info[n].id; here_ids += info[n].id; } @@ -318,29 +305,29 @@ class NetInterfaceManagerPrivate : public QObject } // added - for(int n = 0; n < newinfo.count(); ++n) { + for (int n = 0; n < newinfo.count(); ++n) { int i = lookup(info, newinfo[n].id); - if(i == -1) + if (i == -1) here_ids += newinfo[n].id; } info = newinfo; // announce gone - for(int n = 0; n < gone_ids.count(); ++n) { + for (int n = 0; n < gone_ids.count(); ++n) { // work on a copy, just in case the list changes. // it is important to make the copy here, and not // outside the outer loop, in case the items // get deleted - QList list = listeners; - for(int i = 0; i < list.count(); ++i) { - if(list[i]->d->id == gone_ids[n]) { + QList list = listeners; + for (int i = 0; i < list.count(); ++i) { + if (list[i]->d->id == gone_ids[n]) { list[i]->d->doUnavailable(); } } } // announce here - for(int n = 0; n < here_ids.count(); ++n) + for (int n = 0; n < here_ids.count(); ++n) emit q->interfaceAvailable(here_ids[n]); } @@ -348,7 +335,7 @@ public slots: void tracker_updated() { // collapse multiple updates by queuing up an update if there isn't any queued yet. - if(!pending) { + if (!pending) { QMetaObject::invokeMethod(this, "update", Qt::QueuedConnection); pending = true; } @@ -361,22 +348,18 @@ public slots: } }; -NetInterfaceManager::NetInterfaceManager(QObject *parent) - :QObject(parent) +NetInterfaceManager::NetInterfaceManager(QObject *parent) : QObject(parent) { d = new NetInterfaceManagerPrivate(this); } -NetInterfaceManager::~NetInterfaceManager() -{ - delete d; -} +NetInterfaceManager::~NetInterfaceManager() { delete d; } QStringList NetInterfaceManager::interfaces() const { d->info = d->tracker->getInterfaces(); QStringList out; - for(int n = 0; n < d->info.count(); ++n) { + for (int n = 0; n < d->info.count(); ++n) { out += d->info[n].id; } return out; @@ -385,30 +368,27 @@ QStringList NetInterfaceManager::interfaces() const QString NetInterfaceManager::interfaceForAddress(const QHostAddress &a) { NetInterfaceManager netman; - QStringList list = netman.interfaces(); - for(int n = 0; n < list.count(); ++n) { + QStringList list = netman.interfaces(); + for (int n = 0; n < list.count(); ++n) { NetInterface iface(list[n], &netman); - if(iface.addresses().contains(a)) return list[n]; + if (iface.addresses().contains(a)) + return list[n]; } return QString(); } void *NetInterfaceManager::reg(const QString &id, NetInterface *i) { - for(int n = 0; n < d->info.count(); ++n) { - if(d->info[n].id == id) { + for (int n = 0; n < d->info.count(); ++n) { + if (d->info[n].id == id) { d->listeners += i; return new NetInterfaceProvider::Info(d->info[n]); } } - return 0; -} - -void NetInterfaceManager::unreg(NetInterface *i) -{ - d->listeners.removeAll(i); + return nullptr; } -} +void NetInterfaceManager::unreg(NetInterface *i) { d->listeners.removeAll(i); } +} // namespace XMPP #include "netinterface.moc" diff --git a/src/irisnet/corelib/netinterface.h b/src/irisnet/corelib/netinterface.h index a384c2dc..9bdd939f 100644 --- a/src/irisnet/corelib/netinterface.h +++ b/src/irisnet/corelib/netinterface.h @@ -22,17 +22,18 @@ #include "irisnetglobal.h" namespace XMPP { - class NetInterfaceManager; -class NetInterfacePrivate; class NetInterfaceManagerPrivate; +class NetInterfacePrivate; /** \brief Provides information about a network interface - NetInterface provides information about a particular network interface. Construct it by passing the interface id of interest (e.g. "eth0") and a NetInterfaceManager parent object. Interface ids can be obtained from NetInterfaceManager. + NetInterface provides information about a particular network interface. Construct it by passing the interface id of +interest (e.g. "eth0") and a NetInterfaceManager parent object. Interface ids can be obtained from NetInterfaceManager. - To test if a NetInterface is valid, call isValid(). Use name() to return a display-friendly name of the interface. The addresses() function returns a list of IP addresses for this interface. + To test if a NetInterface is valid, call isValid(). Use name() to return a display-friendly name of the interface. +The addresses() function returns a list of IP addresses for this interface. Here's an example of how to print the IP addresses of eth0: \code @@ -49,14 +50,15 @@ if(iface.isValid()) \sa NetInterfaceManager */ -class IRISNET_EXPORT NetInterface : public QObject -{ +class IRISNET_EXPORT NetInterface : public QObject { Q_OBJECT public: /** \brief Constructs a new interface object with the given \a id and \a manager - If \a id is not a valid interface id, then the object will not be valid (isValid() will return false). Normally it is not necessary to check for validity, since interface ids obtained from NetInterfaceManager are guaranteed to be valid until the event loop resumes. + If \a id is not a valid interface id, then the object will not be valid (isValid() will return false). Normally + it is not necessary to check for validity, since interface ids obtained from NetInterfaceManager are guaranteed + to be valid until the event loop resumes. \sa isValid */ @@ -93,7 +95,8 @@ class IRISNET_EXPORT NetInterface : public QObject /** \brief Returns the addresses of this interface - There will always be at least one address. In some cases there might be multiple, such as on Unix where it is possible for the same interface to have both an IPv4 and an IPv6 address. + There will always be at least one address. In some cases there might be multiple, such as on Unix where it is + possible for the same interface to have both an IPv4 and an IPv6 address. */ QList addresses() const; @@ -101,7 +104,8 @@ class IRISNET_EXPORT NetInterface : public QObject /** \brief Notifies when the interface becomes unavailable - Once this signal is emitted, the NetInterface object becomes invalid and is no longer very useful. A new NetInterface object must be created if a valid object with current information is desired. + Once this signal is emitted, the NetInterface object becomes invalid and is no longer very useful. A new + NetInterface object must be created if a valid object with current information is desired. \note If the interface information changes, the interface is considered to have become unavailable. @@ -123,7 +127,8 @@ class IRISNET_EXPORT NetInterface : public QObject An interface is considered available if it exists, is "Up", has at least one IP address, and is non-Loopback. - The interfaces() function returns a list of available interface ids. These ids can be used with NetInterface to get information about the interfaces. For example, here is how you could print the names of the available interfaces: + The interfaces() function returns a list of available interface ids. These ids can be used with NetInterface to get +information about the interfaces. For example, here is how you could print the names of the available interfaces: \code NetInterfaceManager netman; @@ -135,20 +140,22 @@ for(int n = 0; n < id_list.count(); ++n) } \endcode - When a new network interface is available, the interfaceAvailable() signal will be emitted. Note that interface unavailability is not notified by NetInterfaceManager. Instead, use NetInterface to monitor a specific network interface for unavailability. + When a new network interface is available, the interfaceAvailable() signal will be emitted. Note that interface +unavailability is not notified by NetInterfaceManager. Instead, use NetInterface to monitor a specific network +interface for unavailability. - Interface ids obtained through NetInterfaceManager are guaranteed to be valid until the event loop resumes, or until the next call to interfaces() or interfaceForAddress(). + Interface ids obtained through NetInterfaceManager are guaranteed to be valid until the event loop resumes, or until +the next call to interfaces() or interfaceForAddress(). \sa NetInterface */ -class IRISNET_EXPORT NetInterfaceManager : public QObject -{ +class IRISNET_EXPORT NetInterfaceManager : public QObject { Q_OBJECT public: /** \brief Constructs a new manager object with the given \a parent */ - NetInterfaceManager(QObject *parent = 0); + NetInterfaceManager(QObject *parent = nullptr); /** \brief Destroys the manager object @@ -166,7 +173,8 @@ class IRISNET_EXPORT NetInterfaceManager : public QObject /** \brief Looks up an interface id by IP address - This function looks for an interface that has the address \a a. If there is no such interface, a null string is returned. + This function looks for an interface that has the address \a a. If there is no such interface, a null string is +returned. This is useful for determing the network interface associated with an outgoing QTcpSocket: @@ -194,9 +202,8 @@ QString iface = NetInterfaceManager::interfaceForAddress(tcpSocket->localAddress friend class NetInterfacePrivate; void *reg(const QString &id, NetInterface *i); - void unreg(NetInterface *i); + void unreg(NetInterface *i); }; +} // namespace XMPP -} - -#endif +#endif // NETINTERFACE_H diff --git a/src/irisnet/corelib/netinterface_qtname.cpp b/src/irisnet/corelib/netinterface_qtname.cpp index e9a57a99..2e9d3cc1 100644 --- a/src/irisnet/corelib/netinterface_qtname.cpp +++ b/src/irisnet/corelib/netinterface_qtname.cpp @@ -21,50 +21,40 @@ #include namespace XMPP { - -class IrisQtName : public NameProvider -{ +class IrisQtName : public NameProvider { Q_OBJECT Q_INTERFACES(XMPP::NameProvider) struct Query { - bool isHostInfo; + bool isHostInfo; quintptr handle; }; - int currentId; - QHash lookups; - QHash hostInfo; // we need all these tricks with double mapping because we still support Qt 5.5.1. (5.12 is way better) + int currentId; + QHash lookups; + QHash hostInfo; // we need all these tricks with double mapping because we still support Qt 5.5.1. (5.12 + // is way better) public: - IrisQtName(QObject *parent = 0) : - NameProvider(parent), - currentId(0) - { - - } + IrisQtName(QObject *parent = nullptr) : NameProvider(parent), currentId(0) { } ~IrisQtName() { - for (auto const &l: lookups) { + for (auto const &l : std::as_const(lookups)) { if (!l.isHostInfo) { - delete reinterpret_cast(l.handle); + delete reinterpret_cast(l.handle); } } } - bool supportsSingle() const - { - return true; - } + bool supportsSingle() const { return true; } bool supportsRecordType(int type) const { // yes the types matched to ones from jdns, so it's fine. - static QVector types = { - QDnsLookup::A, QDnsLookup::AAAA, QDnsLookup::ANY, - QDnsLookup::CNAME, QDnsLookup::MX, QDnsLookup::NS, - QDnsLookup::PTR, QDnsLookup::SRV, QDnsLookup::TXT}; + static QVector types + = { QDnsLookup::A, QDnsLookup::AAAA, QDnsLookup::ANY, QDnsLookup::CNAME, QDnsLookup::MX, + QDnsLookup::NS, QDnsLookup::PTR, QDnsLookup::SRV, QDnsLookup::TXT }; return types.contains(type); } @@ -77,22 +67,22 @@ class IrisQtName : public NameProvider QHostAddress addr(QString::fromLatin1(name)); if (!addr.isNull()) { QList results; - XMPP::NameRecord r; + XMPP::NameRecord r; r.setAddress(addr); results.append(r); - QMetaObject::invokeMethod(this, "resolve_resultsReady", Qt::QueuedConnection, - Q_ARG(int, id), Q_ARG(QList, results)); + QMetaObject::invokeMethod(this, "resolve_resultsReady", Qt::QueuedConnection, Q_ARG(int, id), + Q_ARG(QList, results)); } else { if (qType == QDnsLookup::A || qType == QDnsLookup::AAAA) { // QDnsLookup doesn't support A and AAAA according to docs (see corresponding note) int hiid = QHostInfo::lookupHost(QString::fromLatin1(name), this, SLOT(hostInfoFinished(QHostInfo))); hostInfo.insert(hiid, id); - lookups.insert(id, Query{true, quintptr(hiid)}); + lookups.insert(id, Query { true, quintptr(hiid) }); } else { QDnsLookup *lookup = new QDnsLookup((QDnsLookup::Type)qType, QString::fromLatin1(name), this); connect(lookup, SIGNAL(finished()), this, SLOT(handleLookup())); lookup->setProperty("iid", id); - lookups.insert(id, Query{false, quintptr(lookup)}); + lookups.insert(id, Query { false, quintptr(lookup) }); QMetaObject::invokeMethod(lookup, "lookup", Qt::QueuedConnection); } } @@ -110,14 +100,15 @@ class IrisQtName : public NameProvider hostInfo.remove(hiid); lookups.erase(it); } else { - auto lookup = reinterpret_cast(q.handle); + auto lookup = reinterpret_cast(q.handle); lookup->abort(); // handleLookup will catch it and delete } } } private slots: - void hostInfoFinished(const QHostInfo &info) { + void hostInfoFinished(const QHostInfo &info) + { auto hiid = info.lookupId(); auto idIt = hostInfo.find(hiid); if (idIt == hostInfo.end()) { // removed already? @@ -136,7 +127,8 @@ private slots: return; } QList results; - for (const auto &a: info.addresses()) { + const auto &addresses = info.addresses(); + for (const auto &a : addresses) { XMPP::NameRecord ir(info.hostName().toLatin1(), 5 * 60); // ttl = 5 mins ir.setAddress(a); results += ir; @@ -147,25 +139,25 @@ private slots: void handleLookup() { QDnsLookup *lookup = static_cast(sender()); - int id = lookup->property("iid").toInt(); + int id = lookup->property("iid").toInt(); lookups.remove(id); if (lookup->error() != QDnsLookup::NoError) { XMPP::NameResolver::Error e; switch (lookup->error()) { - case QDnsLookup::InvalidReplyError: - e = XMPP::NameResolver::ErrorTimeout; - break; - case QDnsLookup::NotFoundError: - e = XMPP::NameResolver::ErrorNoName; - break; - case QDnsLookup::ResolverError: - case QDnsLookup::OperationCancelledError: - case QDnsLookup::InvalidRequestError: - case QDnsLookup::ServerFailureError: - case QDnsLookup::ServerRefusedError: - default: - e = XMPP::NameResolver::ErrorGeneric; - break; + case QDnsLookup::InvalidReplyError: + e = XMPP::NameResolver::ErrorTimeout; + break; + case QDnsLookup::NotFoundError: + e = XMPP::NameResolver::ErrorNoName; + break; + case QDnsLookup::ResolverError: + case QDnsLookup::OperationCancelledError: + case QDnsLookup::InvalidRequestError: + case QDnsLookup::ServerFailureError: + case QDnsLookup::ServerRefusedError: + default: + e = XMPP::NameResolver::ErrorGeneric; + break; } if (lookup->error() != QDnsLookup::OperationCancelledError) { // don't report after resolve_stop() emit resolve_error(id, e); @@ -175,38 +167,38 @@ private slots: } QList results; - for (auto &qtr: lookup->hostAddressRecords()) { - XMPP::NameRecord ir(qtr.name().toLatin1(), qtr.timeToLive()); + for (auto &qtr : lookup->hostAddressRecords()) { + XMPP::NameRecord ir(qtr.name(), qtr.timeToLive()); ir.setAddress(qtr.value()); results += ir; } - for (auto &qtr: lookup->mailExchangeRecords()) { - XMPP::NameRecord ir(qtr.name().toLatin1(), qtr.timeToLive()); + for (auto &qtr : lookup->mailExchangeRecords()) { + XMPP::NameRecord ir(qtr.name(), qtr.timeToLive()); ir.setMx(qtr.exchange().toLatin1(), qtr.preference()); results += ir; } - for (auto &qtr: lookup->nameServerRecords()) { - XMPP::NameRecord ir(qtr.name().toLatin1(), qtr.timeToLive()); + for (auto &qtr : lookup->nameServerRecords()) { + XMPP::NameRecord ir(qtr.name(), qtr.timeToLive()); ir.setNs(qtr.value().toLatin1()); results += ir; } - for (auto &qtr: lookup->pointerRecords()) { - XMPP::NameRecord ir(qtr.name().toLatin1(), qtr.timeToLive()); + for (auto &qtr : lookup->pointerRecords()) { + XMPP::NameRecord ir(qtr.name(), qtr.timeToLive()); ir.setPtr(qtr.value().toLatin1()); results += ir; } - for (auto &qtr: lookup->canonicalNameRecords()) { - XMPP::NameRecord ir(qtr.name().toLatin1(), qtr.timeToLive()); + for (auto &qtr : lookup->canonicalNameRecords()) { + XMPP::NameRecord ir(qtr.name(), qtr.timeToLive()); ir.setCname(qtr.value().toLatin1()); results += ir; } - for (auto &qtr: lookup->serviceRecords()) { - XMPP::NameRecord ir(qtr.name().toLatin1(), qtr.timeToLive()); - ir.setSrv(qtr.target().toLatin1(),qtr.port(),qtr.priority(),qtr.weight()); + for (auto &qtr : lookup->serviceRecords()) { + XMPP::NameRecord ir(qtr.name(), qtr.timeToLive()); + ir.setSrv(qtr.target().toLatin1(), qtr.port(), qtr.priority(), qtr.weight()); results += ir; } - for (auto &qtr: lookup->textRecords()) { - XMPP::NameRecord ir(qtr.name().toLatin1(), qtr.timeToLive()); + for (auto &qtr : lookup->textRecords()) { + XMPP::NameRecord ir(qtr.name(), qtr.timeToLive()); ir.setTxt(qtr.values()); results += ir; } @@ -215,23 +207,14 @@ private slots: } }; -class IrisQtNameProvider : public IrisNetProvider -{ +class IrisQtNameProvider : public IrisNetProvider { Q_OBJECT Q_INTERFACES(XMPP::IrisNetProvider) public: - - NameProvider *createNameProviderInternet() - { - return new IrisQtName; - } + NameProvider *createNameProviderInternet() { return new IrisQtName; } }; -IrisNetProvider *irisnet_createQtNameProvider() -{ - return new IrisQtNameProvider; -} - -} +IrisNetProvider *irisnet_createQtNameProvider() { return new IrisQtNameProvider; } +} // namespace XMPP #include "netinterface_qtname.moc" diff --git a/src/irisnet/corelib/netinterface_qtnet.cpp b/src/irisnet/corelib/netinterface_qtnet.cpp index dc3730da..2a303ad8 100644 --- a/src/irisnet/corelib/netinterface_qtnet.cpp +++ b/src/irisnet/corelib/netinterface_qtnet.cpp @@ -20,43 +20,111 @@ #include -namespace XMPP { +#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) +#ifdef Q_OS_LINUX +#include +#include +#include +#include +#include -class IrisQtNet : public NetInterfaceProvider -{ +class InterfaceMonitor : public QObject { Q_OBJECT - Q_INTERFACES(XMPP::NetInterfaceProvider) public: - QList info; - QNetworkConfigurationManager ncm; - - IrisQtNet() + InterfaceMonitor() { - connect(&ncm, SIGNAL(configurationAdded(QNetworkConfiguration)), SLOT(check())); - connect(&ncm, SIGNAL(configurationChanged(QNetworkConfiguration)), SLOT(check())); - connect(&ncm, SIGNAL(configurationRemoved(QNetworkConfiguration)), SLOT(check())); + struct sockaddr_nl sa; + + memset(&sa, 0, sizeof(sa)); + sa.nl_family = AF_NETLINK; + sa.nl_groups = RTMGRP_LINK | RTMGRP_IPV4_IFADDR | RTMGRP_IPV6_IFADDR; + + netlinkFd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); + if (netlinkFd == -1) { + return; + } + if (bind(netlinkFd, (struct sockaddr *)&sa, sizeof(sa)) == -1) { + close(netlinkFd); + netlinkFd = -1; + return; + } + notifier = new QSocketNotifier(netlinkFd, QSocketNotifier::Read, this); + + connect(notifier, &QSocketNotifier::activated, this, + [=](QSocketDescriptor, QSocketNotifier::Type) { emit changed(); }); } - void start() + ~InterfaceMonitor() { - poll(); + if (notifier) { + delete notifier; + close(netlinkFd); + } } - QList interfaces() const +signals: + void changed(); + +private: + QSocketNotifier *notifier = nullptr; + int netlinkFd = -1; +}; + +#else +// not linux version. polling? TODO. probably with Qt6 we can use QNetworkStatusMonitor +class InterfaceMonitor : public QObject { + Q_OBJECT +public: + InterfaceMonitor() { } + +signals: + void changed(); +}; +#endif +#else // old Qt < 5.15 +class InterfaceMonitor : public QObject { + Q_OBJECT +public: + InterfaceMonitor() { - return info; + connect(&ncm, SIGNAL(configurationAdded(QNetworkConfiguration)), SIGNAL(changed())); + connect(&ncm, SIGNAL(configurationChanged(QNetworkConfiguration)), SIGNAL(changed())); + connect(&ncm, SIGNAL(configurationRemoved(QNetworkConfiguration)), SIGNAL(changed())); } +signals: + void changed(); + +private: + QNetworkConfigurationManager ncm; +}; +#endif + +namespace XMPP { +class IrisQtNet : public NetInterfaceProvider { + Q_OBJECT + Q_INTERFACES(XMPP::NetInterfaceProvider) +public: + QList info; + InterfaceMonitor monitor; + + IrisQtNet() { connect(&monitor, &InterfaceMonitor::changed, this, &IrisQtNet::check); } + + void start() { poll(); } + + QList interfaces() const { return info; } + void poll() { QList ifaces; - for (auto &iface: QNetworkInterface::allInterfaces()) { + auto const interfaces = QNetworkInterface::allInterfaces(); + for (auto &iface : interfaces) { Info i; - i.id = iface.name(); - i.name = iface.humanReadableName(); - i.isLoopback = (iface.flags() & QNetworkInterface::IsLoopBack); - for (auto &ae: iface.addressEntries()) { + i.id = iface.name(); + i.name = iface.humanReadableName(); + i.isLoopback = bool(iface.flags() & QNetworkInterface::IsLoopBack); + for (auto &ae : iface.addressEntries()) { i.addresses.append(ae.ip()); } ifaces << i; @@ -73,22 +141,14 @@ public slots: } }; -class IrisQtNetProvider : public IrisNetProvider -{ +class IrisQtNetProvider : public IrisNetProvider { Q_OBJECT Q_INTERFACES(XMPP::IrisNetProvider) public: - NetInterfaceProvider *createNetInterfaceProvider() - { - return new IrisQtNet; - } + NetInterfaceProvider *createNetInterfaceProvider() { return new IrisQtNet; } }; -IrisNetProvider *irisnet_createQtNetProvider() -{ - return new IrisQtNetProvider; -} - -} +IrisNetProvider *irisnet_createQtNetProvider() { return new IrisQtNetProvider; } +} // namespace XMPP #include "netinterface_qtnet.moc" diff --git a/src/irisnet/corelib/netinterface_unix.cpp b/src/irisnet/corelib/netinterface_unix.cpp index 3459d152..7d5b4d99 100644 --- a/src/irisnet/corelib/netinterface_unix.cpp +++ b/src/irisnet/corelib/netinterface_unix.cpp @@ -24,19 +24,17 @@ #include "irisnetplugin.h" -#include -#include -#include +#include #include #include #include -#include -#include - -// for solaris -#ifndef SIOCGIFCONF -# include +#include +#include +#ifndef SIOCGIFCONF // for solaris +#include #endif +#include +#include #ifdef Q_OS_LINUX static QStringList read_proc_as_lines(const char *procfile) @@ -44,16 +42,15 @@ static QStringList read_proc_as_lines(const char *procfile) QStringList out; FILE *f = fopen(procfile, "r"); - if(!f) + if (!f) return out; QByteArray buf; - while(!feof(f)) - { + while (!feof(f)) { // max read on a proc is 4K QByteArray block(4096, 0); - int ret = fread(block.data(), 1, block.size(), f); - if(ret <= 0) + int ret = int(fread(block.data(), 1, size_t(block.size()), f)); + if (ret <= 0) break; block.resize(ret); buf += block; @@ -61,21 +58,24 @@ static QStringList read_proc_as_lines(const char *procfile) fclose(f); QString str = QString::fromLocal8Bit(buf); +#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) + out = str.split('\n', Qt::SkipEmptyParts); +#else out = str.split('\n', QString::SkipEmptyParts); +#endif return out; } static QHostAddress linux_ipv6_to_qaddr(const QString &in) { QHostAddress out; - if(in.length() != 32) + if (in.length() != 32) return out; quint8 raw[16]; - for(int n = 0; n < 16; ++n) - { + for (int n = 0; n < 16; ++n) { bool ok; - int x = in.mid(n * 2, 2).toInt(&ok, 16); - if(!ok) + int x = QStringView { in }.mid(n * 2, 2).toInt(&ok, 16); + if (!ok) return out; raw[n] = (quint8)x; } @@ -86,17 +86,16 @@ static QHostAddress linux_ipv6_to_qaddr(const QString &in) static QHostAddress linux_ipv4_to_qaddr(const QString &in) { QHostAddress out; - if(in.length() != 8) + if (in.length() != 8) return out; - quint32 raw; + quint32 raw; unsigned char *rawp = (unsigned char *)&raw; - for(int n = 0; n < 4; ++n) - { + for (int n = 0; n < 4; ++n) { bool ok; - int x = in.mid(n * 2, 2).toInt(&ok, 16); - if(!ok) + int x = QStringView { in }.mid(n * 2, 2).toInt(&ok, 16); + if (!ok) return out; - rawp[n] = (unsigned char )x; + rawp[n] = (unsigned char)x; } out.setAddress(raw); return out; @@ -108,22 +107,25 @@ static QList get_linux_gateways() QStringList lines = read_proc_as_lines("/proc/net/route"); // skip the first line, so we start at 1 - for(int n = 1; n < lines.count(); ++n) - { + for (int n = 1; n < lines.count(); ++n) { const QString &line = lines[n]; +#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) + QStringList parts = line.simplified().split(' ', Qt::SkipEmptyParts); +#else QStringList parts = line.simplified().split(' ', QString::SkipEmptyParts); - if(parts.count() < 10) // net-tools does 10, but why not 11? +#endif + if (parts.count() < 10) // net-tools does 10, but why not 11? continue; QHostAddress addr = linux_ipv4_to_qaddr(parts[2]); - if(addr.isNull()) + if (addr.isNull()) continue; - int iflags = parts[3].toInt(0, 16); - if(!(iflags & RTF_UP)) + int iflags = parts[3].toInt(nullptr, 16); + if (!(iflags & RTF_UP)) continue; - if(!(iflags & RTF_GATEWAY)) + if (!(iflags & RTF_GATEWAY)) continue; XMPP::NetGatewayProvider::Info g; @@ -133,22 +135,25 @@ static QList get_linux_gateways() } lines = read_proc_as_lines("/proc/net/ipv6_route"); - for(int n = 0; n < lines.count(); ++n) - { + for (int n = 0; n < lines.count(); ++n) { const QString &line = lines[n]; +#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) + QStringList parts = line.simplified().split(' ', Qt::SkipEmptyParts); +#else QStringList parts = line.simplified().split(' ', QString::SkipEmptyParts); - if(parts.count() < 10) +#endif + if (parts.count() < 10) continue; QHostAddress addr = linux_ipv6_to_qaddr(parts[4]); - if(addr.isNull()) + if (addr.isNull()) continue; - int iflags = parts[8].toInt(0, 16); - if(!(iflags & RTF_UP)) + int iflags = parts[8].toInt(nullptr, 16); + if (!(iflags & RTF_UP)) continue; - if(!(iflags & RTF_GATEWAY)) + if (!(iflags & RTF_GATEWAY)) continue; XMPP::NetGatewayProvider::Info g; @@ -172,36 +177,28 @@ static QList get_unix_gateways() } namespace XMPP { - -class UnixGateway : public NetGatewayProvider -{ +class UnixGateway : public NetGatewayProvider { Q_OBJECT Q_INTERFACES(XMPP::NetGatewayProvider) public: QList info; - //QTimer t; + // QTimer t; UnixGateway() //: t(this) { - //connect(&t, SIGNAL(timeout()), SLOT(check())); + // connect(&t, SIGNAL(timeout()), SLOT(check())); // TODO track changes without timers } void start() { - //t.start(5000); + // t.start(5000); poll(); } - QList gateways() const - { - return info; - } + QList gateways() const { return info; } - void poll() - { - info = get_unix_gateways(); - } + void poll() { info = get_unix_gateways(); } public slots: void check() @@ -211,22 +208,14 @@ public slots: } }; -class UnixNetProvider : public IrisNetProvider -{ +class UnixNetProvider : public IrisNetProvider { Q_OBJECT Q_INTERFACES(XMPP::IrisNetProvider) public: - virtual NetGatewayProvider *createNetGatewayProvider() - { - return new UnixGateway; - } + virtual NetGatewayProvider *createNetGatewayProvider() { return new UnixGateway; } }; -IrisNetProvider *irisnet_createUnixNetProvider() -{ - return new UnixNetProvider; -} - -} +IrisNetProvider *irisnet_createUnixNetProvider() { return new UnixNetProvider; } +} // namespace XMPP #include "netinterface_unix.moc" diff --git a/src/irisnet/corelib/netnames.cpp b/src/irisnet/corelib/netnames.cpp index 3e1e5a8b..64be6fbe 100644 --- a/src/irisnet/corelib/netnames.cpp +++ b/src/irisnet/corelib/netnames.cpp @@ -19,107 +19,96 @@ #include "netnames.h" -#include - -//#include -#include "irisnetplugin.h" -#include "irisnetglobal_p.h" #include "addressresolver.h" +#include "corelib/irisnetglobal_p.h" +#include "irisnetplugin.h" +#if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0) +#include +#endif +#include -//#define NETNAMES_DEBUG - +// #define NETNAMES_DEBUG #ifdef NETNAMES_DEBUG -# define NNDEBUG (qDebug() << this << "#" << __FUNCTION__ << ":") +#define NNDEBUG (qDebug() << this << "#" << __FUNCTION__ << ":") #endif - namespace XMPP { - //---------------------------------------------------------------------------- // NameRecord //---------------------------------------------------------------------------- -class NameRecord::Private : public QSharedData -{ +class NameRecord::Private : public QSharedData { public: - QByteArray owner; + QString owner; // de-ACE-ed version of domain name from dns reply NameRecord::Type type; - int ttl; + int ttl; - QHostAddress address; - QByteArray name; - int priority, weight, port; + QHostAddress address; + QByteArray name; + int priority, weight, port; QList texts; - QByteArray cpu, os; - QByteArray rawData; + QByteArray cpu, os; + QByteArray rawData; }; -#define ENSURE_D { if(!d) d = new Private; } +#define ENSURE_D \ + { \ + if (!d) \ + d = new Private; \ + } -NameRecord::NameRecord() : - d(nullptr) -{ -} +NameRecord::NameRecord() : d(nullptr) { } -NameRecord::NameRecord(const QByteArray &owner, int ttl) : - d(nullptr) +NameRecord::NameRecord(const QString &owner, int ttl) : d(nullptr) { setOwner(owner); setTtl(ttl); } -NameRecord::NameRecord(const NameRecord &from) : - d(nullptr) -{ - *this = from; -} +NameRecord::NameRecord(const NameRecord &from) : d(nullptr) { *this = from; } -NameRecord::~NameRecord() -{ -} +NameRecord::~NameRecord() { } -NameRecord & NameRecord::operator=(const NameRecord &from) +NameRecord &NameRecord::operator=(const NameRecord &from) { d = from.d; return *this; } -bool NameRecord::operator==(const NameRecord &o) { +bool NameRecord::operator==(const NameRecord &o) const +{ if (isNull() != o.isNull() || owner() != o.owner() || ttl() != o.ttl() || type() != o.type()) { return false; } switch (type()) { - case XMPP::NameRecord::A: - case XMPP::NameRecord::Aaaa: - return address() == o.address(); - case XMPP::NameRecord::Mx: - return name() == o.name() && priority() == o.priority(); - case XMPP::NameRecord::Srv: - return name() == o.name() && port() == o.port() && priority() == o.priority() && weight() == o.weight(); - case XMPP::NameRecord::Cname: - case XMPP::NameRecord::Ptr: - case XMPP::NameRecord::Ns: - return name() == o.name(); - case XMPP::NameRecord::Txt: - return texts() == o.texts(); - case XMPP::NameRecord::Hinfo: - return cpu() == o.cpu() && os() == o.os(); - case XMPP::NameRecord::Null: - return rawData() == o.rawData(); - case XMPP::NameRecord::Any: - return false; + case XMPP::NameRecord::A: + case XMPP::NameRecord::Aaaa: + return address() == o.address(); + case XMPP::NameRecord::Mx: + return name() == o.name() && priority() == o.priority(); + case XMPP::NameRecord::Srv: + return name() == o.name() && port() == o.port() && priority() == o.priority() && weight() == o.weight(); + case XMPP::NameRecord::Cname: + case XMPP::NameRecord::Ptr: + case XMPP::NameRecord::Ns: + return name() == o.name(); + case XMPP::NameRecord::Txt: + return texts() == o.texts(); + case XMPP::NameRecord::Hinfo: + return cpu() == o.cpu() && os() == o.os(); + case XMPP::NameRecord::Null: + return rawData() == o.rawData(); + case XMPP::NameRecord::Any: + return false; } return false; } -bool NameRecord::isNull() const -{ - return (d ? false : true); -} +bool NameRecord::isNull() const { return (d ? false : true); } -QByteArray NameRecord::owner() const +QString NameRecord::owner() const { Q_ASSERT(d); return d->owner; @@ -191,7 +180,7 @@ QByteArray NameRecord::rawData() const return d->rawData; } -void NameRecord::setOwner(const QByteArray &name) +void NameRecord::setOwner(const QString &name) { ENSURE_D d->owner = name; @@ -206,7 +195,7 @@ void NameRecord::setTtl(int seconds) void NameRecord::setAddress(const QHostAddress &a) { ENSURE_D - if(a.protocol() == QAbstractSocket::IPv6Protocol) + if (a.protocol() == QAbstractSocket::IPv6Protocol) d->type = NameRecord::Aaaa; else d->type = NameRecord::A; @@ -216,19 +205,19 @@ void NameRecord::setAddress(const QHostAddress &a) void NameRecord::setMx(const QByteArray &name, int priority) { ENSURE_D - d->type = NameRecord::Mx; - d->name = name; + d->type = NameRecord::Mx; + d->name = name; d->priority = priority; } void NameRecord::setSrv(const QByteArray &name, int port, int priority, int weight) { ENSURE_D - d->type = NameRecord::Srv; - d->name = name; - d->port = port; + d->type = NameRecord::Srv; + d->name = name; + d->port = port; d->priority = priority; - d->weight = weight; + d->weight = weight; } void NameRecord::setCname(const QByteArray &name) @@ -248,7 +237,7 @@ void NameRecord::setPtr(const QByteArray &name) void NameRecord::setTxt(const QList &texts) { ENSURE_D - d->type = NameRecord::Txt; + d->type = NameRecord::Txt; d->texts = texts; } @@ -256,8 +245,8 @@ void NameRecord::setHinfo(const QByteArray &cpu, const QByteArray &os) { ENSURE_D d->type = NameRecord::Hinfo; - d->cpu = cpu; - d->os = os; + d->cpu = cpu; + d->os = os; } void NameRecord::setNs(const QByteArray &name) @@ -270,7 +259,7 @@ void NameRecord::setNs(const QByteArray &name) void NameRecord::setNull(const QByteArray &rawData) { ENSURE_D - d->type = NameRecord::Null; + d->type = NameRecord::Null; d->rawData = rawData; } @@ -278,41 +267,40 @@ QDebug operator<<(QDebug dbg, XMPP::NameRecord::Type type) { dbg.nospace() << "XMPP::NameRecord::"; - switch(type) - { - case XMPP::NameRecord::A: - dbg.nospace() << "A"; - break; - case XMPP::NameRecord::Aaaa: - dbg.nospace() << "Aaaa"; - break; - case XMPP::NameRecord::Mx: - dbg.nospace() << "Mx"; - break; - case XMPP::NameRecord::Srv: - dbg.nospace() << "Srv"; - break; - case XMPP::NameRecord::Cname: - dbg.nospace() << "Cname"; - break; - case XMPP::NameRecord::Ptr: - dbg.nospace() << "Ptr"; - break; - case XMPP::NameRecord::Txt: - dbg.nospace() << "Txt"; - break; - case XMPP::NameRecord::Hinfo: - dbg.nospace() << "Hinfo"; - break; - case XMPP::NameRecord::Ns: - dbg.nospace() << "Ns"; - break; - case XMPP::NameRecord::Null: - dbg.nospace() << "Null"; - break; - case XMPP::NameRecord::Any: - dbg.nospace() << "Any"; - break; + switch (type) { + case XMPP::NameRecord::A: + dbg.nospace() << "A"; + break; + case XMPP::NameRecord::Aaaa: + dbg.nospace() << "Aaaa"; + break; + case XMPP::NameRecord::Mx: + dbg.nospace() << "Mx"; + break; + case XMPP::NameRecord::Srv: + dbg.nospace() << "Srv"; + break; + case XMPP::NameRecord::Cname: + dbg.nospace() << "Cname"; + break; + case XMPP::NameRecord::Ptr: + dbg.nospace() << "Ptr"; + break; + case XMPP::NameRecord::Txt: + dbg.nospace() << "Txt"; + break; + case XMPP::NameRecord::Hinfo: + dbg.nospace() << "Hinfo"; + break; + case XMPP::NameRecord::Ns: + dbg.nospace() << "Ns"; + break; + case XMPP::NameRecord::Null: + dbg.nospace() << "Null"; + break; + case XMPP::NameRecord::Any: + dbg.nospace() << "Any"; + break; } return dbg; @@ -320,48 +308,40 @@ QDebug operator<<(QDebug dbg, XMPP::NameRecord::Type type) QDebug operator<<(QDebug dbg, const XMPP::NameRecord &record) { - dbg.nospace() << "XMPP::NameRecord(" - << "owner=" << record.owner() - << ", ttl=" << record.ttl() - << ", type=" << record.type(); - - switch(record.type()) - { - case XMPP::NameRecord::A: - case XMPP::NameRecord::Aaaa: - dbg.nospace() << ", address=" << record.address(); - break; - case XMPP::NameRecord::Mx: - dbg.nospace() - << ", name=" << record.name() - << ", priority=" << record.priority(); - break; - case XMPP::NameRecord::Srv: - dbg.nospace() - << ", name=" << record.name() - << ", port=" << record.port() - << ", priority=" << record.priority() - << ", weight=" << record.weight(); - break; - case XMPP::NameRecord::Cname: - case XMPP::NameRecord::Ptr: - case XMPP::NameRecord::Ns: - dbg.nospace() << ", name=" << record.name(); - break; - case XMPP::NameRecord::Txt: - dbg.nospace() << ", texts={" << record.texts() << "}"; - break; - case XMPP::NameRecord::Hinfo: - dbg.nospace() << ", cpu=" << record.cpu() << ", os=" << record.os(); - break; - case XMPP::NameRecord::Null: - dbg.nospace() << ", size=" << record.rawData().size(); - break; - case XMPP::NameRecord::Any: - dbg.nospace() << ", "; - // should not happen - Q_ASSERT(false); - break; + dbg.nospace() << "XMPP::NameRecord(" << "owner=" << record.owner() << ", ttl=" << record.ttl() + << ", type=" << record.type(); + + switch (record.type()) { + case XMPP::NameRecord::A: + case XMPP::NameRecord::Aaaa: + dbg.nospace() << ", address=" << record.address(); + break; + case XMPP::NameRecord::Mx: + dbg.nospace() << ", name=" << record.name() << ", priority=" << record.priority(); + break; + case XMPP::NameRecord::Srv: + dbg.nospace() << ", name=" << record.name() << ", port=" << record.port() << ", priority=" << record.priority() + << ", weight=" << record.weight(); + break; + case XMPP::NameRecord::Cname: + case XMPP::NameRecord::Ptr: + case XMPP::NameRecord::Ns: + dbg.nospace() << ", name=" << record.name(); + break; + case XMPP::NameRecord::Txt: + dbg.nospace() << ", texts={" << record.texts() << "}"; + break; + case XMPP::NameRecord::Hinfo: + dbg.nospace() << ", cpu=" << record.cpu() << ", os=" << record.os(); + break; + case XMPP::NameRecord::Null: + dbg.nospace() << ", size=" << record.rawData().size(); + break; + case XMPP::NameRecord::Any: + dbg.nospace() << ", "; + // should not happen + Q_ASSERT(false); + break; } dbg.nospace() << ")"; @@ -372,71 +352,46 @@ QDebug operator<<(QDebug dbg, const XMPP::NameRecord &record) //---------------------------------------------------------------------------- // ServiceInstance //---------------------------------------------------------------------------- -class ServiceInstance::Private : public QSharedData -{ +class ServiceInstance::Private : public QSharedData { public: - QString instance, type, domain; - QMap attribs; - QByteArray name; + QString instance, type, domain; + QMap attribs; + QByteArray name; }; -ServiceInstance::ServiceInstance() : - d(new Private) -{ -} +ServiceInstance::ServiceInstance() : d(new Private) { } -ServiceInstance::ServiceInstance(const QString &instance, const QString &type, const QString &domain, const QMap &attribs) : - d(new Private) +ServiceInstance::ServiceInstance(const QString &instance, const QString &type, const QString &domain, + const QMap &attribs) : d(new Private) { d->instance = instance; - d->type = type; - d->domain = domain; - d->attribs = attribs; + d->type = type; + d->domain = domain; + d->attribs = attribs; // FIXME: escape the items d->name = instance.toLatin1() + '.' + type.toLatin1() + '.' + domain.toLatin1(); } -ServiceInstance::ServiceInstance(const ServiceInstance &from) : - d(nullptr) -{ - *this = from; -} +ServiceInstance::ServiceInstance(const ServiceInstance &from) : d(nullptr) { *this = from; } -ServiceInstance::~ServiceInstance() -{ -} +ServiceInstance::~ServiceInstance() { } -ServiceInstance & ServiceInstance::operator=(const ServiceInstance &from) +ServiceInstance &ServiceInstance::operator=(const ServiceInstance &from) { d = from.d; return *this; } -QString ServiceInstance::instance() const -{ - return d->instance; -} +QString ServiceInstance::instance() const { return d->instance; } -QString ServiceInstance::type() const -{ - return d->type; -} +QString ServiceInstance::type() const { return d->type; } -QString ServiceInstance::domain() const -{ - return d->domain; -} +QString ServiceInstance::domain() const { return d->domain; } -QMap ServiceInstance::attributes() const -{ - return d->attribs; -} +QMap ServiceInstance::attributes() const { return d->attribs; } -QByteArray ServiceInstance::name() const -{ - return d->name; -} +QByteArray ServiceInstance::name() const { return d->name; } //---------------------------------------------------------------------------- // NameManager @@ -444,99 +399,85 @@ QByteArray ServiceInstance::name() const class NameManager; Q_GLOBAL_STATIC(QMutex, nman_mutex) -static NameManager *g_nman = 0; +static NameManager *g_nman = nullptr; -class NameResolver::Private -{ +class NameResolver::Private { public: NameResolver *q; - int type; + int type; bool longLived; - int id; + int id; - Private(NameResolver *_q) : q(_q) - { - } + Private(NameResolver *_q) : q(_q) { } }; -class ServiceBrowser::Private -{ +class ServiceBrowser::Private { public: ServiceBrowser *q; int id; - Private(ServiceBrowser *_q) : q(_q) - { - } + Private(ServiceBrowser *_q) : q(_q) { } }; -class ServiceResolver::Private : public QObject -{ +class ServiceResolver::Private : public QObject { Q_OBJECT public: - Private(ServiceResolver *parent) - : q(parent), dns_sd_resolve_id(0), requestedProtocol(IPv6_IPv4), port(0), protocol(QAbstractSocket::IPv6Protocol) + Private(ServiceResolver *parent) : + q(parent), dns_sd_resolve_id(0), requestedProtocol(IPv6_IPv4), port(0), protocol(QAbstractSocket::IPv6Protocol) { } /* DNS-SD interaction with NameManager */ - ServiceResolver *q; //!< Pointing upwards, so NameManager can call its signals - int dns_sd_resolve_id; //!< DNS-SD lookup id, set by NameManager + ServiceResolver *q; //!< Pointing upwards, so NameManager can call its signals + int dns_sd_resolve_id; //!< DNS-SD lookup id, set by NameManager /* configuration */ Protocol requestedProtocol; //!< IP protocol requested by user /* state trackers */ - QString domain; //!< Domain we are currently looking up - QString host; //!< Hostname we are currently looking up - QHostAddress address; //!< IP address we are currently looking up - quint16 port; //!< Port we are currently looking up + QString domain; //!< Domain we are currently looking up + QString host; //!< Hostname we are currently looking up + QHostAddress address; //!< IP address we are currently looking up + quint16 port; //!< Port we are currently looking up QAbstractSocket::NetworkLayerProtocol protocol; //!< IP protocol we are currently looking up - XMPP::WeightedNameRecordList srvList; //!< List of resolved SRV names - QList hostList; //!< List or resolved hostnames for current SRV name - QList resolverList; //!< NameResolvers currently in use, needed for cleanup - + XMPP::WeightedNameRecordList srvList; //!< List of resolved SRV names + QList hostList; //!< List or resolved hostnames for current SRV name + QList resolverList; //!< NameResolvers currently in use, needed for cleanup }; - -WeightedNameRecordList::WeightedNameRecordList() - : currentPriorityGroup(priorityGroups.end()) /* void current state */ -{} - -WeightedNameRecordList::WeightedNameRecordList(const QList &list) +WeightedNameRecordList::WeightedNameRecordList() : currentPriorityGroup(priorityGroups.end()) /* void current state */ { - append(list); } -WeightedNameRecordList::WeightedNameRecordList(const WeightedNameRecordList &other) -{ - *this = other; -} +WeightedNameRecordList::WeightedNameRecordList(const QList &list) { append(list); } + +WeightedNameRecordList::WeightedNameRecordList(const WeightedNameRecordList &other) { *this = other; } WeightedNameRecordList &WeightedNameRecordList::operator=(const WeightedNameRecordList &other) { priorityGroups = other.priorityGroups; if (other.currentPriorityGroup != other.priorityGroups.end()) { - currentPriorityGroup = priorityGroups.find(other.currentPriorityGroup.key()); + currentPriorityGroup = priorityGroups.find(other.currentPriorityGroup->first); } else { currentPriorityGroup = priorityGroups.end(); } return *this; } -WeightedNameRecordList::~WeightedNameRecordList() { -} +WeightedNameRecordList::~WeightedNameRecordList() { } -bool WeightedNameRecordList::isEmpty() const { +bool WeightedNameRecordList::isEmpty() const +{ return currentPriorityGroup == const_cast(this)->priorityGroups.end(); } -XMPP::NameRecord WeightedNameRecordList::takeNext() { +XMPP::NameRecord WeightedNameRecordList::takeNext() +{ /* Find the next useful priority group */ - while (currentPriorityGroup != priorityGroups.end() && currentPriorityGroup->empty()) { + while (currentPriorityGroup != priorityGroups.end() && currentPriorityGroup->second.empty()) { ++currentPriorityGroup; } /* There are no priority groups left, return failure */ @@ -549,7 +490,7 @@ XMPP::NameRecord WeightedNameRecordList::takeNext() { /* Find the new total weight of this priority group */ int totalWeight = 0; - foreach (const XMPP::NameRecord &record, *currentPriorityGroup) { + for (const auto &record : std::as_const(currentPriorityGroup->second)) { totalWeight += record.weight(); } @@ -558,16 +499,20 @@ XMPP::NameRecord WeightedNameRecordList::takeNext() { #endif /* Pick a random entry */ - int randomWeight = qrand()/static_cast(RAND_MAX)*totalWeight; +#if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0) + int randomWeight = totalWeight ? QRandomGenerator::global()->bounded(totalWeight) : 0; +#else + int randomWeight = qrand() / static_cast(RAND_MAX) * totalWeight; +#endif #ifdef NETNAMES_DEBUG NNDEBUG << "Picked weight:" << randomWeight; #endif /* Iterate through the priority group until we found the randomly selected entry */ - WeightedNameRecordPriorityGroup::iterator it(currentPriorityGroup->begin()); - for (int currentWeight = it->weight(); currentWeight < randomWeight; currentWeight += (++it)->weight()) {} - Q_ASSERT(it != currentPriorityGroup->end()); + WeightedNameRecordPriorityGroup::iterator it(currentPriorityGroup->second.begin()); + for (int currentWeight = it->weight(); currentWeight < randomWeight; currentWeight += (++it)->weight()) { } + Q_ASSERT(it != currentPriorityGroup->second.end()); /* We are going to delete the entry in the list, so save it */ XMPP::NameRecord result(*it); @@ -577,66 +522,56 @@ XMPP::NameRecord WeightedNameRecordList::takeNext() { #endif /* Delete the entry from list, to prevent it from being tried multiple times */ - currentPriorityGroup->remove(it->weight(), *it); - if (currentPriorityGroup->isEmpty()) { - priorityGroups.erase(currentPriorityGroup++); + currentPriorityGroup->second.remove(it->weight(), *it); + if (currentPriorityGroup->second.isEmpty()) { + currentPriorityGroup = priorityGroups.erase(currentPriorityGroup); } return result; } -void WeightedNameRecordList::clear() { +void WeightedNameRecordList::clear() +{ priorityGroups.clear(); /* void current state */ currentPriorityGroup = priorityGroups.end(); } -void WeightedNameRecordList::append(const XMPP::WeightedNameRecordList &list) { +void WeightedNameRecordList::append(const XMPP::WeightedNameRecordList &list) +{ /* Copy over all records from all groups */ - foreach (const WeightedNameRecordPriorityGroup &group, list.priorityGroups) { - foreach(const NameRecord& record, group) { + for (const auto &group : list.priorityGroups) { + for (const NameRecord &record : group.second) append(record); - } } /* Reset to beginning */ currentPriorityGroup = priorityGroups.begin(); } -void WeightedNameRecordList::append(const QList &list) { - foreach (const XMPP::NameRecord &record, list) { - if (record.type() != XMPP::NameRecord::Srv) { - continue; - } - WeightedNameRecordPriorityGroup group(priorityGroups.value(record.priority())); - - group.insert(record.weight(), record); - - if (!priorityGroups.contains(record.priority())) { - priorityGroups.insert(record.priority(), group); - } - } +void WeightedNameRecordList::append(const QList &list) +{ + for (const XMPP::NameRecord &record : list) + if (record.type() == XMPP::NameRecord::Srv) + append(record); /* Reset to beginning */ currentPriorityGroup = priorityGroups.begin(); } -void WeightedNameRecordList::append(const XMPP::NameRecord &record) { - WeightedNameRecordPriorityGroup group(priorityGroups.value(record.priority())); - +void WeightedNameRecordList::append(const XMPP::NameRecord &record) +{ Q_ASSERT(record.type() == XMPP::NameRecord::Srv); - group.insert(record.weight(), record); - - if (!priorityGroups.contains(record.priority())) { - priorityGroups.insert(record.priority(), group); - } + auto [it, _] = priorityGroups.try_emplace(record.priority(), WeightedNameRecordPriorityGroup {}); + it->second.insert(record.weight(), record); /* Reset to beginning */ currentPriorityGroup = priorityGroups.begin(); } -void WeightedNameRecordList::append(const QString &hostname, quint16 port) { +void WeightedNameRecordList::append(const QString &hostname, quint16 port) +{ NameRecord record(hostname.toLocal8Bit(), std::numeric_limits::max()); record.setSrv(hostname.toLocal8Bit(), port, std::numeric_limits::max(), 0); @@ -646,71 +581,80 @@ void WeightedNameRecordList::append(const QString &hostname, quint16 port) { currentPriorityGroup = priorityGroups.begin(); } -XMPP::WeightedNameRecordList& WeightedNameRecordList::operator<<(const XMPP::WeightedNameRecordList &list) { +XMPP::WeightedNameRecordList &WeightedNameRecordList::operator<<(const XMPP::WeightedNameRecordList &list) +{ append(list); return *this; } -WeightedNameRecordList& WeightedNameRecordList::operator<<(const QList &list) { +WeightedNameRecordList &WeightedNameRecordList::operator<<(const QList &list) +{ append(list); return *this; } -XMPP::WeightedNameRecordList& WeightedNameRecordList::operator<<(const XMPP::NameRecord &record) { +XMPP::WeightedNameRecordList &WeightedNameRecordList::operator<<(const XMPP::NameRecord &record) +{ append(record); return *this; } - -QDebug operator<<(QDebug dbg, const XMPP::WeightedNameRecordList &list) { +QDebug operator<<(QDebug dbg, const XMPP::WeightedNameRecordList &list) +{ dbg.nospace() << "XMPP::WeightedNameRecordList(\n"; - /* operator(QDebug, QMap const&) has a bug which makes it crash when trying to print the dereferenced end() iterator */ + /* operator(QDebug, QMap const&) has a bug which makes it crash when trying to print the dereferenced end() iterator + */ if (!list.isEmpty()) { - dbg.nospace() << "current=" << *list.currentPriorityGroup << endl; + dbg.nospace() << "current=" << *list.currentPriorityGroup +#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) + << Qt::endl; +#else + << endl; +#endif } dbg.nospace() << "{"; - foreach(int priority, list.priorityGroups.keys()) { - dbg.nospace() << "\t" << priority << "->" << list.priorityGroups.value(priority) << endl; + for (const auto &[priority, group] : list.priorityGroups) { + dbg.nospace() << "\t" << priority << "->" << group +#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) + << Qt::endl; +#else + << endl; +#endif } dbg.nospace() << "})"; return dbg; } - -class ServiceLocalPublisher::Private -{ +class ServiceLocalPublisher::Private { public: ServiceLocalPublisher *q; int id; - Private(ServiceLocalPublisher *_q) : q(_q) - { - } + Private(ServiceLocalPublisher *_q) : q(_q) { } }; -class NameManager : public QObject -{ +class NameManager : public QObject { Q_OBJECT public: - NameProvider *p_net, *p_local; - ServiceProvider *p_serv; - QHash res_instances; - QHash res_sub_instances; + NameProvider *p_net, *p_local; + ServiceProvider *p_serv; + QHash res_instances; + QHash res_sub_instances; - QHash br_instances; - QHash sres_instances; - QHash slp_instances; + QHash br_instances; + QHash sres_instances; + QHash slp_instances; - NameManager(QObject *parent = 0) : QObject(parent) + NameManager(QObject *parent = nullptr) : QObject(parent) { - p_net = 0; - p_local = 0; - p_serv = 0; + p_net = nullptr; + p_local = nullptr; + p_serv = 0; } ~NameManager() @@ -723,8 +667,7 @@ class NameManager : public QObject static NameManager *instance() { QMutexLocker locker(nman_mutex()); - if(!g_nman) - { + if (!g_nman) { g_nman = new NameManager; irisNetAddPostRoutine(NetNames::cleanup); } @@ -734,40 +677,46 @@ class NameManager : public QObject static void cleanup() { delete g_nman; - g_nman = 0; + g_nman = nullptr; } void resolve_start(NameResolver::Private *np, const QByteArray &name, int qType, bool longLived) { QMutexLocker locker(nman_mutex()); - np->type = qType; + np->type = qType; np->longLived = longLived; - if(!p_net) - { - NameProvider *c = 0; - QList list = irisNetProviders(); - for(int n = 0; n < list.count(); ++n) - { + if (!p_net) { + NameProvider *c = 0; + QList list = irisNetProviders(); + for (int n = 0; n < list.count(); ++n) { IrisNetProvider *p = list[n]; - c = p->createNameProviderInternet(); - if(c) + c = p->createNameProviderInternet(); + if (c) break; } Q_ASSERT(c); // we have built-in support, so this should never fail p_net = c; // use queued connections - qRegisterMetaType< QList >("QList"); + qRegisterMetaType>("QList"); qRegisterMetaType("XMPP::NameResolver::Error"); - connect(p_net, SIGNAL(resolve_resultsReady(int,QList)), SLOT(provider_resolve_resultsReady(int,QList))); - connect(p_net, SIGNAL(resolve_error(int,XMPP::NameResolver::Error)), SLOT(provider_resolve_error(int,XMPP::NameResolver::Error))); - connect(p_net, SIGNAL(resolve_useLocal(int,QByteArray)), SLOT(provider_resolve_useLocal(int,QByteArray))); + connect(p_net, &NameProvider::resolve_resultsReady, this, + [this](int id, const QList &results) { + NameResolver::Private *np = res_instances.value(id); + NameResolver *q = np->q; // resolve_cleanup deletes np + if (!np->longLived) + resolve_cleanup(np); + emit q->resultsReady(results); + }); + connect(p_net, SIGNAL(resolve_error(int, XMPP::NameResolver::Error)), + SLOT(provider_resolve_error(int, XMPP::NameResolver::Error))); + connect(p_net, SIGNAL(resolve_useLocal(int, QByteArray)), SLOT(provider_resolve_useLocal(int, QByteArray))); } np->id = p_net->resolve_start(name, qType, longLived); - //printf("assigning %d to %p\n", req_id, np); + // printf("assigning %d to %p\n", req_id, np); res_instances.insert(np->id, np); } @@ -782,17 +731,15 @@ class NameManager : public QObject { // clean up any sub instances - QList sub_instances_to_remove; + QList sub_instances_to_remove; QHashIterator it(res_sub_instances); - while(it.hasNext()) - { + while (it.hasNext()) { it.next(); - if(it.value() == np->id) + if (it.value() == np->id) sub_instances_to_remove += it.key(); } - foreach(int res_sub_id, sub_instances_to_remove) - { + for (int res_sub_id : std::as_const(sub_instances_to_remove)) { res_sub_instances.remove(res_sub_id); p_local->resolve_stop(res_sub_id); } @@ -802,22 +749,20 @@ class NameManager : public QObject res_instances.remove(np->id); NameResolver *q = np->q; delete q->d; - q->d = 0; + q->d = nullptr; } void browse_start(ServiceBrowser::Private *np, const QString &type, const QString &domain) { QMutexLocker locker(nman_mutex()); - if(!p_serv) - { - ServiceProvider *c = 0; - QList list = irisNetProviders(); - for(int n = 0; n < list.count(); ++n) - { + if (!p_serv) { + ServiceProvider *c = nullptr; + QList list = irisNetProviders(); + for (int n = 0; n < list.count(); ++n) { IrisNetProvider *p = list[n]; - c = p->createServiceProvider(); - if(c) + c = p->createServiceProvider(); + if (c) break; } Q_ASSERT(c); // we have built-in support, so this should never fail @@ -827,9 +772,12 @@ class NameManager : public QObject qRegisterMetaType("XMPP::ServiceInstance"); qRegisterMetaType("XMPP::ServiceBrowser::Error"); - connect(p_serv, SIGNAL(browse_instanceAvailable(int,XMPP::ServiceInstance)), SLOT(provider_browse_instanceAvailable(int,XMPP::ServiceInstance)), Qt::QueuedConnection); - connect(p_serv, SIGNAL(browse_instanceUnavailable(int,XMPP::ServiceInstance)), SLOT(provider_browse_instanceUnavailable(int,XMPP::ServiceInstance)), Qt::QueuedConnection); - connect(p_serv, SIGNAL(browse_error(int,XMPP::ServiceBrowser::Error)), SLOT(provider_browse_error(int,XMPP::ServiceBrowser::Error)), Qt::QueuedConnection); + connect(p_serv, SIGNAL(browse_instanceAvailable(int, XMPP::ServiceInstance)), + SLOT(provider_browse_instanceAvailable(int, XMPP::ServiceInstance)), Qt::QueuedConnection); + connect(p_serv, SIGNAL(browse_instanceUnavailable(int, XMPP::ServiceInstance)), + SLOT(provider_browse_instanceUnavailable(int, XMPP::ServiceInstance)), Qt::QueuedConnection); + connect(p_serv, SIGNAL(browse_error(int, XMPP::ServiceBrowser::Error)), + SLOT(provider_browse_error(int, XMPP::ServiceBrowser::Error)), Qt::QueuedConnection); } /*np->id = */ @@ -843,24 +791,28 @@ class NameManager : public QObject { QMutexLocker locker(nman_mutex()); - if(!p_serv) - { - ServiceProvider *c = 0; - QList list = irisNetProviders(); - for(int n = 0; n < list.count(); ++n) - { + if (!p_serv) { + ServiceProvider *c = nullptr; + QList list = irisNetProviders(); + for (int n = 0; n < list.count(); ++n) { IrisNetProvider *p = list[n]; - c = p->createServiceProvider(); - if(c) + c = p->createServiceProvider(); + if (c) break; } Q_ASSERT(c); // we have built-in support, so this should never fail p_serv = c; // use queued connections - qRegisterMetaType("QHostAddress"); - qRegisterMetaType< QList >("QList"); - connect(p_serv, SIGNAL(resolve_resultsReady(int,QList)), SLOT(provider_resolve_resultsReady(int,QList)), Qt::QueuedConnection); + qRegisterMetaType>( + "QList"); + connect( + p_serv, &ServiceProvider::resolve_resultsReady, this, + [this](int id, const QList &results) { + ServiceResolver::Private *np = sres_instances.value(id); + emit np->q->resultReady(results[0].address, quint16(results[0].port), results[0].hostName); + }, + Qt::QueuedConnection); } /* store the id so we can stop it later */ @@ -869,19 +821,18 @@ class NameManager : public QObject sres_instances.insert(np->dns_sd_resolve_id, np); } - void publish_start(ServiceLocalPublisher::Private *np, const QString &instance, const QString &type, int port, const QMap &attribs) + void publish_start(ServiceLocalPublisher::Private *np, const QString &instance, const QString &type, int port, + const QMap &attribs) { QMutexLocker locker(nman_mutex()); - if(!p_serv) - { - ServiceProvider *c = 0; - QList list = irisNetProviders(); - for(int n = 0; n < list.count(); ++n) - { + if (!p_serv) { + ServiceProvider *c = nullptr; + QList list = irisNetProviders(); + for (int n = 0; n < list.count(); ++n) { IrisNetProvider *p = list[n]; - c = p->createServiceProvider(); - if(c) + c = p->createServiceProvider(); + if (c) break; } Q_ASSERT(c); // we have built-in support, so this should never fail @@ -889,8 +840,10 @@ class NameManager : public QObject // use queued connections qRegisterMetaType("XMPP::ServiceLocalPublisher::Error"); - connect(p_serv, SIGNAL(publish_published(int)), SLOT(provider_publish_published(int)), Qt::QueuedConnection); - connect(p_serv, SIGNAL(publish_extra_published(int)), SLOT(provider_publish_extra_published(int)), Qt::QueuedConnection); + connect(p_serv, SIGNAL(publish_published(int)), SLOT(provider_publish_published(int)), + Qt::QueuedConnection); + connect(p_serv, SIGNAL(publish_extra_published(int)), SLOT(provider_publish_extra_published(int)), + Qt::QueuedConnection); } /*np->id = */ @@ -906,28 +859,20 @@ class NameManager : public QObject } private slots: - void provider_resolve_resultsReady(int id, const QList &results) - { - NameResolver::Private *np = res_instances.value(id); - NameResolver *q = np->q; // resolve_cleanup deletes np - if(!np->longLived) - resolve_cleanup(np); - emit q->resultsReady(results); - } void provider_resolve_error(int id, XMPP::NameResolver::Error e) { NameResolver::Private *np = res_instances.value(id); - NameResolver *q = np->q; // resolve_cleanup deletes np + NameResolver *q = np->q; // resolve_cleanup deletes np resolve_cleanup(np); emit q->error(e); } void provider_local_resolve_resultsReady(int id, const QList &results) { - int par_id = res_sub_instances.value(id); - NameResolver::Private *np = res_instances.value(par_id); - if(!np->longLived) + int par_id = res_sub_instances.value(id); + NameResolver::Private *np = res_instances.value(par_id); + if (!np->longLived) res_sub_instances.remove(id); p_net->resolve_localResultsReady(par_id, results); } @@ -942,15 +887,13 @@ private slots: void provider_resolve_useLocal(int id, const QByteArray &name) { // transfer to local - if(!p_local) - { - NameProvider *c = 0; - QList list = irisNetProviders(); - for(int n = 0; n < list.count(); ++n) - { + if (!p_local) { + NameProvider *c = nullptr; + QList list = irisNetProviders(); + for (int n = 0; n < list.count(); ++n) { IrisNetProvider *p = list[n]; - c = p->createNameProviderLocal(); - if(c) + c = p->createNameProviderLocal(); + if (c) break; } Q_ASSERT(c); // we have built-in support, so this should never fail @@ -958,10 +901,12 @@ private slots: p_local = c; // use queued connections - qRegisterMetaType< QList >("QList"); + qRegisterMetaType>("QList"); qRegisterMetaType("XMPP::NameResolver::Error"); - connect(p_local, SIGNAL(resolve_resultsReady(int,QList)), SLOT(provider_local_resolve_resultsReady(int,QList)), Qt::QueuedConnection); - connect(p_local, SIGNAL(resolve_error(int,XMPP::NameResolver::Error)), SLOT(provider_local_resolve_error(int,XMPP::NameResolver::Error)), Qt::QueuedConnection); + connect(p_local, SIGNAL(resolve_resultsReady(int, QList)), + SLOT(provider_local_resolve_resultsReady(int, QList)), Qt::QueuedConnection); + connect(p_local, SIGNAL(resolve_error(int, XMPP::NameResolver::Error)), + SLOT(provider_local_resolve_error(int, XMPP::NameResolver::Error)), Qt::QueuedConnection); } NameResolver::Private *np = res_instances.value(id); @@ -989,13 +934,13 @@ private slots: void provider_browse_instanceAvailable(int id, const XMPP::ServiceInstance &i) { ServiceBrowser::Private *np = br_instances.value(id); - emit np->q->instanceAvailable(i); + emit np->q->instanceAvailable(i); } void provider_browse_instanceUnavailable(int id, const XMPP::ServiceInstance &i) { ServiceBrowser::Private *np = br_instances.value(id); - emit np->q->instanceUnavailable(i); + emit np->q->instanceUnavailable(i); } void provider_browse_error(int id, XMPP::ServiceBrowser::Error e) @@ -1006,23 +951,17 @@ private slots: emit np->q->error(); } - void provider_resolve_resultsReady(int id, const QList &results) - { - ServiceResolver::Private *np = sres_instances.value(id); - emit np->q->resultReady(results[0].address, results[0].port); - } - void provider_publish_published(int id) { ServiceLocalPublisher::Private *np = slp_instances.value(id); - emit np->q->published(); + emit np->q->published(); } void provider_publish_extra_published(int id) { Q_UNUSED(id); - //ServiceLocalPublisher::Private *np = slp_instances.value(id); - //emit np->q->published(); + // ServiceLocalPublisher::Private *np = slp_instances.value(id); + // emit np->q->published(); } }; @@ -1031,64 +970,66 @@ private slots: //---------------------------------------------------------------------------- // copied from JDNS -#define JDNS_RTYPE_A 1 -#define JDNS_RTYPE_AAAA 28 -#define JDNS_RTYPE_MX 15 -#define JDNS_RTYPE_SRV 33 -#define JDNS_RTYPE_CNAME 5 -#define JDNS_RTYPE_PTR 12 -#define JDNS_RTYPE_TXT 16 -#define JDNS_RTYPE_HINFO 13 -#define JDNS_RTYPE_NS 2 -#define JDNS_RTYPE_ANY 255 +#define JDNS_RTYPE_A 1 +#define JDNS_RTYPE_AAAA 28 +#define JDNS_RTYPE_MX 15 +#define JDNS_RTYPE_SRV 33 +#define JDNS_RTYPE_CNAME 5 +#define JDNS_RTYPE_PTR 12 +#define JDNS_RTYPE_TXT 16 +#define JDNS_RTYPE_HINFO 13 +#define JDNS_RTYPE_NS 2 +#define JDNS_RTYPE_ANY 255 static int recordType2Rtype(NameRecord::Type type) { - switch(type) - { - case NameRecord::A: return JDNS_RTYPE_A; - case NameRecord::Aaaa: return JDNS_RTYPE_AAAA; - case NameRecord::Mx: return JDNS_RTYPE_MX; - case NameRecord::Srv: return JDNS_RTYPE_SRV; - case NameRecord::Cname: return JDNS_RTYPE_CNAME; - case NameRecord::Ptr: return JDNS_RTYPE_PTR; - case NameRecord::Txt: return JDNS_RTYPE_TXT; - case NameRecord::Hinfo: return JDNS_RTYPE_HINFO; - case NameRecord::Ns: return JDNS_RTYPE_NS; - case NameRecord::Null: return 10; - case NameRecord::Any: return JDNS_RTYPE_ANY; + switch (type) { + case NameRecord::A: + return JDNS_RTYPE_A; + case NameRecord::Aaaa: + return JDNS_RTYPE_AAAA; + case NameRecord::Mx: + return JDNS_RTYPE_MX; + case NameRecord::Srv: + return JDNS_RTYPE_SRV; + case NameRecord::Cname: + return JDNS_RTYPE_CNAME; + case NameRecord::Ptr: + return JDNS_RTYPE_PTR; + case NameRecord::Txt: + return JDNS_RTYPE_TXT; + case NameRecord::Hinfo: + return JDNS_RTYPE_HINFO; + case NameRecord::Ns: + return JDNS_RTYPE_NS; + case NameRecord::Null: + return 10; + case NameRecord::Any: + return JDNS_RTYPE_ANY; } return -1; } -NameResolver::NameResolver(QObject *parent) -:QObject(parent) -{ - d = 0; -} +NameResolver::NameResolver(QObject *parent) : QObject(parent) { d = nullptr; } -NameResolver::~NameResolver() -{ - stop(); -} +NameResolver::~NameResolver() { stop(); } void NameResolver::start(const QByteArray &name, NameRecord::Type type, Mode mode) { stop(); - d = new Private(this); + d = new Private(this); int qType = recordType2Rtype(type); - if(qType == -1) + if (qType == -1) qType = JDNS_RTYPE_A; - NameManager::instance()->resolve_start(d, name, qType, mode == NameResolver::LongLived ? true : false); + NameManager::instance()->resolve_start(d, name, qType, mode == NameResolver::LongLived); } void NameResolver::stop() { - if(d) - { + if (d) { NameManager::instance()->resolve_stop(d); delete d; - d = 0; + d = nullptr; } } @@ -1096,58 +1037,45 @@ QDebug operator<<(QDebug dbg, XMPP::NameResolver::Error e) { dbg.nospace() << "XMPP::NameResolver::"; - switch(e) - { - case XMPP::NameResolver::ErrorGeneric: - dbg.nospace() << "ErrorGeneric"; - break; - case XMPP::NameResolver::ErrorNoName: - dbg.nospace() << "ErrorNoName"; - break; - case XMPP::NameResolver::ErrorTimeout: - dbg.nospace() << "ErrorTimeout"; - break; - case XMPP::NameResolver::ErrorNoLocal: - dbg.nospace() << "ErrorNoLocal"; - break; - case XMPP::NameResolver::ErrorNoLongLived: - dbg.nospace() << "ErrorNoLongLived"; - break; + switch (e) { + case XMPP::NameResolver::ErrorGeneric: + dbg.nospace() << "ErrorGeneric"; + break; + case XMPP::NameResolver::ErrorNoName: + dbg.nospace() << "ErrorNoName"; + break; + case XMPP::NameResolver::ErrorTimeout: + dbg.nospace() << "ErrorTimeout"; + break; + case XMPP::NameResolver::ErrorNoLocal: + dbg.nospace() << "ErrorNoLocal"; + break; + case XMPP::NameResolver::ErrorNoLongLived: + dbg.nospace() << "ErrorNoLongLived"; + break; } return dbg; } - //---------------------------------------------------------------------------- // ServiceBrowser //---------------------------------------------------------------------------- -ServiceBrowser::ServiceBrowser(QObject *parent) -:QObject(parent) -{ - d = new Private(this); -} +ServiceBrowser::ServiceBrowser(QObject *parent) : QObject(parent) { d = new Private(this); } -ServiceBrowser::~ServiceBrowser() -{ - delete d; -} +ServiceBrowser::~ServiceBrowser() { delete d; } void ServiceBrowser::start(const QString &type, const QString &domain) { NameManager::instance()->browse_start(d, type, domain); } -void ServiceBrowser::stop() -{ -} - +void ServiceBrowser::stop() { } //---------------------------------------------------------------------------- // ServiceResolver //---------------------------------------------------------------------------- -ServiceResolver::ServiceResolver(QObject *parent) - : QObject(parent) +ServiceResolver::ServiceResolver(QObject *parent) : QObject(parent) { #ifdef NETNAMES_DEBUG NNDEBUG; @@ -1156,10 +1084,7 @@ ServiceResolver::ServiceResolver(QObject *parent) d = new Private(this); } -ServiceResolver::~ServiceResolver() -{ - delete d; -} +ServiceResolver::~ServiceResolver() { delete d; } void ServiceResolver::clear_resolvers() { @@ -1168,7 +1093,7 @@ void ServiceResolver::clear_resolvers() #endif /* cleanup all resolvers */ - foreach (XMPP::NameResolver *resolver, d->resolverList) { + for (XMPP::NameResolver *resolver : std::as_const(d->resolverList)) { cleanup_resolver(resolver); } } @@ -1193,18 +1118,12 @@ void ServiceResolver::cleanup_resolver(XMPP::NameResolver *resolver) } } -ServiceResolver::Protocol ServiceResolver::protocol() const { - return d->requestedProtocol; -} +ServiceResolver::Protocol ServiceResolver::protocol() const { return d->requestedProtocol; } -void ServiceResolver::setProtocol(ServiceResolver::Protocol p) { - d->requestedProtocol = p; -} +void ServiceResolver::setProtocol(ServiceResolver::Protocol p) { d->requestedProtocol = p; } /* DNS-SD lookup */ -void ServiceResolver::start(const QByteArray &name) { - NameManager::instance()->resolve_instance_start(d, name); -} +void ServiceResolver::start(const QByteArray &name) { NameManager::instance()->resolve_instance_start(d, name); } /* normal host lookup */ void ServiceResolver::start(const QString &host, quint16 port) @@ -1216,19 +1135,23 @@ void ServiceResolver::start(const QString &host, quint16 port) /* clear host list */ d->hostList.clear(); - d->protocol = (d->requestedProtocol == IPv6_IPv4 || d->requestedProtocol == IPv6 ? QAbstractSocket::IPv6Protocol : QAbstractSocket::IPv4Protocol); - d->host = host; - d->port = port; + d->protocol = (d->requestedProtocol == IPv6_IPv4 || d->requestedProtocol == IPv6 ? QAbstractSocket::IPv6Protocol + : QAbstractSocket::IPv4Protocol); + d->host = host; + d->port = port; #ifdef NETNAMES_DEBUG NNDEBUG << "d->p:" << d->protocol; #endif /* initiate the host lookup */ - XMPP::NameRecord::Type querytype = (d->protocol == QAbstractSocket::IPv6Protocol ? XMPP::NameRecord::Aaaa : XMPP::NameRecord::A); + XMPP::NameRecord::Type querytype + = (d->protocol == QAbstractSocket::IPv6Protocol ? XMPP::NameRecord::Aaaa : XMPP::NameRecord::A); XMPP::NameResolver *resolver = new XMPP::NameResolver; - connect(resolver, SIGNAL(resultsReady(QList)), this, SLOT(handle_host_ready(QList))); - connect(resolver, SIGNAL(error(XMPP::NameResolver::Error)), this, SLOT(handle_host_error(XMPP::NameResolver::Error))); + connect(resolver, SIGNAL(resultsReady(QList)), this, + SLOT(handle_host_ready(QList))); + connect(resolver, SIGNAL(error(XMPP::NameResolver::Error)), this, + SLOT(handle_host_error(XMPP::NameResolver::Error))); resolver->start(host.toLocal8Bit(), querytype); d->resolverList << resolver; } @@ -1249,17 +1172,18 @@ void ServiceResolver::start(const QString &service, const QString &transport, co /* after we tried all SRV hosts, we shall connect directly (if requested) */ if (port < std::numeric_limits::max()) { - d->srvList.append(domain.toLocal8Bit(), port); - } - else { + d->srvList.append(domain.toLocal8Bit(), quint16(port)); + } else { /* The only "valid" port above the valid port range is our specification of an invalid port */ Q_ASSERT(port == std::numeric_limits::max()); } /* initiate the SRV lookup */ XMPP::NameResolver *resolver = new XMPP::NameResolver; - connect(resolver, SIGNAL(resultsReady(QList)), this, SLOT(handle_srv_ready(QList))); - connect(resolver, SIGNAL(error(XMPP::NameResolver::Error)), this, SLOT(handle_srv_error(XMPP::NameResolver::Error))); + connect(resolver, SIGNAL(resultsReady(QList)), this, + SLOT(handle_srv_ready(QList))); + connect(resolver, SIGNAL(error(XMPP::NameResolver::Error)), this, + SLOT(handle_srv_error(XMPP::NameResolver::Error))); resolver->start(srv_request.toLocal8Bit(), XMPP::NameRecord::Srv); d->resolverList << resolver; } @@ -1272,7 +1196,7 @@ void ServiceResolver::handle_srv_ready(const QList &r) #endif /* cleanup resolver */ - cleanup_resolver(static_cast(sender())); + cleanup_resolver(static_cast(sender())); /* lookup srv pointers */ d->srvList << r; @@ -1292,7 +1216,7 @@ void ServiceResolver::handle_srv_error(XMPP::NameResolver::Error e) #endif /* cleanup resolver */ - cleanup_resolver(static_cast(sender())); + cleanup_resolver(static_cast(sender())); /* srvList already contains a failsafe host, try that */ emit srvFailed(); @@ -1309,7 +1233,7 @@ void ServiceResolver::handle_host_ready(const QList &r) #endif /* cleanup resolver */ - cleanup_resolver(static_cast(sender())); + cleanup_resolver(static_cast(sender())); /* connect to host */ d->hostList << r; @@ -1324,7 +1248,7 @@ void ServiceResolver::handle_host_error(XMPP::NameResolver::Error e) #endif /* cleanup resolver */ - cleanup_resolver(static_cast(sender())); + cleanup_resolver(static_cast(sender())); /* try a fallback lookup if requested*/ if (!lookup_host_fallback()) { @@ -1343,7 +1267,7 @@ void ServiceResolver::handle_host_fallback_error(XMPP::NameResolver::Error e) #endif /* cleanup resolver */ - cleanup_resolver(static_cast(sender())); + cleanup_resolver(static_cast(sender())); /* lookup next SRV */ try_next_srv(); @@ -1357,7 +1281,8 @@ bool ServiceResolver::check_protocol_fallback() } /* lookup the fallback host */ -bool ServiceResolver::lookup_host_fallback() { +bool ServiceResolver::lookup_host_fallback() +{ #ifdef NETNAMES_DEBUG NNDEBUG; #endif @@ -1367,17 +1292,21 @@ bool ServiceResolver::lookup_host_fallback() { return false; } - d->protocol = (d->protocol == QAbstractSocket::IPv6Protocol ? QAbstractSocket::IPv4Protocol : QAbstractSocket::IPv6Protocol); + d->protocol = (d->protocol == QAbstractSocket::IPv6Protocol ? QAbstractSocket::IPv4Protocol + : QAbstractSocket::IPv6Protocol); #ifdef NETNAMES_DEBUG NNDEBUG << "d->p:" << d->protocol; #endif /* initiate the fallback host lookup */ - XMPP::NameRecord::Type querytype = (d->protocol == QAbstractSocket::IPv6Protocol ? XMPP::NameRecord::Aaaa : XMPP::NameRecord::A); + XMPP::NameRecord::Type querytype + = (d->protocol == QAbstractSocket::IPv6Protocol ? XMPP::NameRecord::Aaaa : XMPP::NameRecord::A); XMPP::NameResolver *resolver = new XMPP::NameResolver; - connect(resolver, SIGNAL(resultsReady(QList)), this, SLOT(handle_host_ready(QList))); - connect(resolver, SIGNAL(error(XMPP::NameResolver::Error)), this, SLOT(handle_host_fallback_error(XMPP::NameResolver::Error))); + connect(resolver, SIGNAL(resultsReady(QList)), this, + SLOT(handle_host_ready(QList))); + connect(resolver, SIGNAL(error(XMPP::NameResolver::Error)), this, + SLOT(handle_host_fallback_error(XMPP::NameResolver::Error))); resolver->start(d->host.toLocal8Bit(), querytype); d->resolverList << resolver; @@ -1385,7 +1314,8 @@ bool ServiceResolver::lookup_host_fallback() { } /* notify user about next host */ -bool ServiceResolver::try_next_host() { +bool ServiceResolver::try_next_host() +{ #ifdef NETNAMES_DEBUG NNDEBUG << "hl:" << d->hostList; #endif @@ -1394,7 +1324,7 @@ bool ServiceResolver::try_next_host() { if (!d->hostList.empty()) { XMPP::NameRecord record(d->hostList.takeFirst()); /* emit found address and the port specified earlier */ - emit resultReady(record.address(), d->port); + emit resultReady(record.address(), d->port, record.owner()); return true; } @@ -1410,12 +1340,11 @@ void ServiceResolver::try_next_srv() #endif /* if there are still hosts we did not try */ - if (!d->srvList.isEmpty()) { - XMPP::NameRecord record(d->srvList.takeNext()); + XMPP::NameRecord record = d->srvList.takeNext(); + if (!record.isNull()) { /* lookup host by name and specify port for later use */ - start(record.name(), record.port()); - } - else { + start(record.name(), quint16(record.port())); + } else { #ifdef NETNAMES_DEBUG NNDEBUG << "SRV list empty, failing"; #endif @@ -1424,21 +1353,17 @@ void ServiceResolver::try_next_srv() } } -void ServiceResolver::tryNext() { +void ServiceResolver::tryNext() +{ /* if the host list cannot help, try the SRV list */ if (!try_next_host()) { - try_next_srv(); + try_next_srv(); } } -void ServiceResolver::stop() { - clear_resolvers(); -} +void ServiceResolver::stop() { clear_resolvers(); } -bool ServiceResolver::hasPendingSrv() const -{ - return !d->srvList.isEmpty(); -} +bool ServiceResolver::hasPendingSrv() const { return !d->srvList.isEmpty(); } ServiceResolver::ProtoSplit ServiceResolver::happySplit() { @@ -1446,58 +1371,40 @@ ServiceResolver::ProtoSplit ServiceResolver::happySplit() ProtoSplit s; s.ipv4 = new ServiceResolver(this); s.ipv4->setProtocol(IPv4); - s.ipv4->d->srvList = d->srvList; + s.ipv4->d->srvList = d->srvList; s.ipv4->d->hostList = d->hostList; - s.ipv4->d->domain = d->domain; - s.ipv6 = new ServiceResolver(this); + s.ipv4->d->domain = d->domain; + s.ipv6 = new ServiceResolver(this); s.ipv6->setProtocol(IPv6); - s.ipv6->d->srvList = d->srvList; + s.ipv6->d->srvList = d->srvList; s.ipv6->d->hostList = d->hostList; - s.ipv6->d->domain = d->domain; + s.ipv6->d->domain = d->domain; return s; } - //---------------------------------------------------------------------------- // ServiceLocalPublisher //---------------------------------------------------------------------------- -ServiceLocalPublisher::ServiceLocalPublisher(QObject *parent) -:QObject(parent) -{ - d = new Private(this); -} +ServiceLocalPublisher::ServiceLocalPublisher(QObject *parent) : QObject(parent) { d = new Private(this); } -ServiceLocalPublisher::~ServiceLocalPublisher() -{ - delete d; -} +ServiceLocalPublisher::~ServiceLocalPublisher() { delete d; } -void ServiceLocalPublisher::publish(const QString &instance, const QString &type, int port, const QMap &attributes) +void ServiceLocalPublisher::publish(const QString &instance, const QString &type, int port, + const QMap &attributes) { NameManager::instance()->publish_start(d, instance, type, port, attributes); } -void ServiceLocalPublisher::updateAttributes(const QMap &attributes) -{ - Q_UNUSED(attributes); -} +void ServiceLocalPublisher::updateAttributes(const QMap &attributes) { Q_UNUSED(attributes); } -void ServiceLocalPublisher::addRecord(const NameRecord &rec) -{ - NameManager::instance()->publish_extra_start(d, rec); -} +void ServiceLocalPublisher::addRecord(const NameRecord &rec) { NameManager::instance()->publish_extra_start(d, rec); } -void ServiceLocalPublisher::cancel() -{ -} +void ServiceLocalPublisher::cancel() { } //---------------------------------------------------------------------------- // NetNames //---------------------------------------------------------------------------- -void NetNames::cleanup() -{ - NameManager::cleanup(); -} +void NetNames::cleanup() { NameManager::cleanup(); } QString NetNames::diagnosticText() { @@ -1532,7 +1439,6 @@ QByteArray NetNames::unescapeDomain(const QByteArray &in) Q_UNUSED(in); return QByteArray(); } - -} +} // namespace XMPP #include "netnames.moc" diff --git a/src/irisnet/corelib/netnames.h b/src/irisnet/corelib/netnames.h index ee5ee350..81767f63 100644 --- a/src/irisnet/corelib/netnames.h +++ b/src/irisnet/corelib/netnames.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006,2008 Justin Karneges + * Copyright (C) 2006-2008 Justin Karneges * Copyright (C) 2009-2010 Dennis Schridde * * This library is free software; you can redistribute it and/or @@ -20,24 +20,22 @@ #ifndef NETNAMES_H #define NETNAMES_H +#include "irisnetglobal.h" + #include #include - #include - -#include "irisnetglobal.h" +#include // it seems visual studio defines it somewhere #ifdef max -# undef max +#undef max #endif namespace XMPP { - class NameManager; -class IRISNET_EXPORT NetNames -{ +class IRISNET_EXPORT NetNames { public: // free any shared data, shutdown internal dns sessions if necessary. static void cleanup(); @@ -47,7 +45,7 @@ class IRISNET_EXPORT NetNames // convert idn names static QByteArray idnaFromString(const QString &in); - static QString idnaToString(const QByteArray &in); + static QString idnaToString(const QByteArray &in); // dns escaping static QByteArray escapeDomain(const QByteArray &in); @@ -60,9 +58,13 @@ class IRISNET_EXPORT NetNames /** \brief Provides a DNS record - NameRecord provides a DNS (Domain Name System) record, which is information assicated with a domain name. For most purposes, the information is an IP address. However, DNS records are capable of holding a variety of data types, such as named pointers to other domain names and even arbitrary text strings. The results of a NameResolver operation are a list of NameRecords. + NameRecord provides a DNS (Domain Name System) record, which is information assicated with a domain name. For most +purposes, the information is an IP address. However, DNS records are capable of holding a variety of data types, such +as named pointers to other domain names and even arbitrary text strings. The results of a NameResolver operation are a +list of NameRecords. - The most common type is the address record, "A", which contains an IPv4 address. Here is an example of how to get the IP address out of an address record: + The most common type is the address record, "A", which contains an IPv4 address. Here is an example of how to get +the IP address out of an address record: \code NameRecord record = ... // obtain a record from somewhere @@ -73,9 +75,13 @@ if(record.type() == NameRecord::A) } \endcode - Getting the data out of a NameRecord involves calling the right retrieval functions, depending on the type. Many types share retrieval functions. For example, the "AAAA" type holds an IPv6 address, which is accessed the same way as the "A" type, by calling address(). See the NameRecord::Type enum for further information about which retrieval functions should be called for each type. + Getting the data out of a NameRecord involves calling the right retrieval functions, depending on the type. Many +types share retrieval functions. For example, the "AAAA" type holds an IPv6 address, which is accessed the same way as +the "A" type, by calling address(). See the NameRecord::Type enum for further information about which retrieval +functions should be called for each type. - To create a NameRecord, use setOwner() and setTtl() as necessary, and then call one of the setX functions (where X is the desired type). For example, to set an A or AAAA record, use setAddress() like this: + To create a NameRecord, use setOwner() and setTtl() as necessary, and then call one of the setX functions +(where X is the desired type). For example, to set an A or AAAA record, use setAddress() like this: \code // make example.com the owner, with 1 hour TTL @@ -83,20 +89,19 @@ NameRecord record("example.com", 3600); record.setAddress(QHostAddress("1.2.3.4")); \endcode - Note that in the case of setAddress(), the record type need not be specified. NameRecord will determine the type to use based on the given QHostAddress. + Note that in the case of setAddress(), the record type need not be specified. NameRecord will determine the type to +use based on the given QHostAddress. \sa NameResolver */ -class IRISNET_EXPORT NameRecord -{ +class IRISNET_EXPORT NameRecord { public: /** \brief The type of DNS record The retrieval functions are shown for each type. */ - enum Type - { + enum Type { A, ///< IPv4 address. Use address(). Aaaa, ///< IPv6 address. Use address(). Mx, ///< Mail server. Use name() and priority(). @@ -107,7 +112,7 @@ class IRISNET_EXPORT NameRecord Hinfo, ///< Host information. Use cpu() and os(). Ns, ///< Name server. Use name(). Null, ///< Null type. Use rawData(). - Any ///< "Any record", for use with NameResolver::start() only. A NameRecord object will never be of this type. + Any ///< "Any record", for use with NameResolver::start() only. A NameRecord object will never be of this type. }; /** @@ -120,9 +125,10 @@ class IRISNET_EXPORT NameRecord /** \brief Constructs a partially initialized record object, with the given \a owner and \a ttl - For the record to be usable, call an appropriate setX function (where X is the desired type) afterwards. + For the record to be usable, call an appropriate setX function (where X is the desired type) + afterwards. */ - NameRecord(const QByteArray &owner, int ttl); + NameRecord(const QString &owner, int ttl); /** \brief Constructs a copy of \a from @@ -137,35 +143,40 @@ class IRISNET_EXPORT NameRecord /** \brief Assigns \a from to this object and returns a reference to this object */ - NameRecord & operator=(const NameRecord &from); + NameRecord &operator=(const NameRecord &from); /** \brief Compares \a other with this object */ - bool operator==(const NameRecord &other); + bool operator==(const NameRecord &other) const; /** \brief Returns true if this record object is null, otherwise returns false - Be sure not to confuse a null object with the NULL type (NameRecord::Null). Don't ask why DNS has a type called NULL that contains valid data. + Be sure not to confuse a null object with the NULL type (NameRecord::Null). Don't ask why DNS has a type called + NULL that contains valid data. */ bool isNull() const; // don't confuse with Null type /** \brief Returns the owner of this record - The owner is usually not a useful attribute, since it will be the same as the name searched for with NameResolver. For example, if the A record of "example.com" is looked up, then the resulting records will all have "example.com" as the owner. + The owner is usually not a useful attribute, since it will be the same as the name searched for with + NameResolver. For example, if the A record of "example.com" is looked up, then the resulting records will all + have "example.com" as the owner. \sa setOwner */ - QByteArray owner() const; + QString owner() const; /** \brief Returns the TTL (time-to-live) of this record - This is the number of seconds the record should be considered valid, which is useful information when performing caching. + This is the number of seconds the record should be considered valid, which is useful information when performing + caching. - As a special exception, a TTL of 0 when performing a long-lived lookup indicates that a record is no longer available. + As a special exception, a TTL of 0 when performing a long-lived lookup indicates that a record is no longer + available. \sa setTtl */ @@ -244,7 +255,7 @@ class IRISNET_EXPORT NameRecord \sa owner */ - void setOwner(const QByteArray &name); + void setOwner(const QString &name); /** \brief Sets the TTL (time-to-live) of this record to \a ttl seconds @@ -308,21 +319,20 @@ class IRISNET_EXPORT NameRecord IRISNET_EXPORT QDebug operator<<(QDebug, XMPP::NameRecord::Type); IRISNET_EXPORT QDebug operator<<(QDebug, const XMPP::NameRecord &); - -class IRISNET_EXPORT ServiceInstance -{ +class IRISNET_EXPORT ServiceInstance { public: ServiceInstance(); - ServiceInstance(const QString &instance, const QString &type, const QString &domain, const QMap &attributes); + ServiceInstance(const QString &instance, const QString &type, const QString &domain, + const QMap &attributes); ServiceInstance(const ServiceInstance &from); ~ServiceInstance(); - ServiceInstance & operator=(const ServiceInstance &from); + ServiceInstance &operator=(const ServiceInstance &from); - QString instance() const; - QString type() const; - QString domain() const; - QMap attributes() const; - QByteArray name() const; // full dns label + QString instance() const; + QString type() const; + QString domain() const; + QMap attributes() const; + QByteArray name() const; // full dns label private: class Private; @@ -334,9 +344,11 @@ class IRISNET_EXPORT ServiceInstance /** \brief Represents a DNS query/lookup - NameResolver performs an asynchronous DNS lookup for a given domain name and record type. Call start() to begin. The resultsReady() signal is emitted on success, otherwise error() is emitted. To cancel a lookup, call stop(). + NameResolver performs an asynchronous DNS lookup for a given domain name and record type. Call start() to begin. The +resultsReady() signal is emitted on success, otherwise error() is emitted. To cancel a lookup, call stop(). - Each NameResolver object can only perform one DNS lookup at a time. If start() is called while a lookup is already in progress, then the existing lookup is stopped before starting the new lookup. + Each NameResolver object can only perform one DNS lookup at a time. If start() is called while a lookup is already +in progress, then the existing lookup is stopped before starting the new lookup. Each NameResolver object should be used for just one DNS query and then be deleted. Otherwise ambiguity might arise when receiving multiple answers to future queries. @@ -371,57 +383,62 @@ void dns_error(XMPP::NameResolver::Error error) } \endcode - Yes, a domain name can have multiple IP addresses. Many applications ignore this fact, and use only one of the answers. A proper network application should try connecting to each IP address until one succeeds. + Yes, a domain name can have multiple IP addresses. Many applications ignore this fact, and use only one of the +answers. A proper network application should try connecting to each IP address until one succeeds. - To lookup other types, pass the desired type to start(). For example, suppose you want to look up the MX record of a domain name: + To lookup other types, pass the desired type to start(). For example, suppose you want to look up the MX record of a +domain name: \code // look up the MX record for affinix.com resolver->start("affinix.com", NameRecord::Mx); \endcode - It is also possible to perform long-lived queries. This is generally useful for DNS Service Discovery. Long-lived queries are continuous, and resultsReady() may be emitted multiple times. Unlike a normal lookup, which stops once the results are returned, a long-lived query will keep going until stop() is called. + It is also possible to perform long-lived queries. This is generally useful for DNS Service Discovery. Long-lived +queries are continuous, and resultsReady() may be emitted multiple times. Unlike a normal lookup, which stops once the +results are returned, a long-lived query will keep going until stop() is called. - For example, suppose you want to scan the local network for SSH services. According to the DNS-SD protocol, this is done by querying for the name "_ssh._tcp.local." of type PTR. + For example, suppose you want to scan the local network for SSH services. According to the DNS-SD protocol, this is +done by querying for the name "_ssh._tcp.local." of type PTR. \code // monitor for SSH services on the local network resolver->start("_ssh._tcp.local.", NameRecord::Ptr, NameResolver::LongLived); \endcode - Don't be alarmed by the trailing dot (".") character in this last example. It is not well known, but all valid DNS domain names end with a dot. However, NameResolver, like most DNS programming interfaces, allows the dot to be left out. What this means is that if a trailing dot is missing in the input to start(), NameResolver will internally append one before performing the query. + Don't be alarmed by the trailing dot (".") character in this last example. It is not well known, but all valid DNS +domain names end with a dot. However, NameResolver, like most DNS programming interfaces, allows the dot to be left +out. What this means is that if a trailing dot is missing in the input to start(), NameResolver will internally append +one before performing the query. \sa NameRecord */ -class IRISNET_EXPORT NameResolver : public QObject -{ +class IRISNET_EXPORT NameResolver : public QObject { Q_OBJECT public: /** \brief Resolve mode */ - enum Mode - { - Single, ///< A normal DNS query with a single result set. - LongLived ///< An endless query, with multiple result sets allowed. + enum Mode { + Single, ///< A normal DNS query with a single result set. + LongLived ///< An endless query, with multiple result sets allowed. }; /** \brief Resolve error */ - enum Error - { - ErrorGeneric, ///< General failure during lookup, no further details. - ErrorNoName, ///< Name does not exist. - ErrorTimeout, ///< The operation timed out. - ErrorNoLocal, ///< The query is to the local network, but no mechanism for Multicast DNS is available. - ErrorNoLongLived ///< The query requires long-lived capability, but no mechanism for doing so is available. + enum Error { + ErrorGeneric, ///< General failure during lookup, no further details. + ErrorNoName, ///< Name does not exist. + ErrorTimeout, ///< The operation timed out. + ErrorNoLocal, ///< The query is to the local network, but no mechanism for Multicast DNS is available. + ErrorNoLongLived ///< The query requires long-lived capability, but no mechanism for doing so is available. }; /** \brief Constructs a new resolver object with the given \a parent */ - NameResolver(QObject *parent = 0); + NameResolver(QObject *parent = nullptr); /** \brief Destroys the resolver object @@ -433,7 +450,8 @@ class IRISNET_EXPORT NameResolver : public QObject /** \brief Starts a lookup - A lookup for \a name of \a type is started. For normal queries, \a mode should be NameResolver::Single (this is the default). For long-lived queries, use NameResolver::LongLived. + A lookup for \a name of \a type is started. For normal queries, \a mode should be NameResolver::Single (this is + the default). For long-lived queries, use NameResolver::LongLived. If a lookup is already in progress, it is stopped before starting the new lookup. @@ -444,7 +462,8 @@ class IRISNET_EXPORT NameResolver : public QObject /** \brief Stops a lookup - Use this function if you want to stop the current lookup, such that the resolver object may be reused again later. If you don't plan to reuse the object, then destroying the object is enough. + Use this function if you want to stop the current lookup, such that the resolver object may be reused again + later. If you don't plan to reuse the object, then destroying the object is enough. \sa start */ @@ -454,16 +473,21 @@ class IRISNET_EXPORT NameResolver : public QObject /** \brief Notification of result records - This signal is emitted when results of the lookup operation have arrived. The \a results parameter is a list of NameRecords. All records will be of the type queried for with start(), unless the NameRecord::Any type was specified, in which case the records may be of any type + This signal is emitted when results of the lookup operation have arrived. The \a results parameter is a list of + NameRecords. All records will be of the type queried for with start(), unless the NameRecord::Any type was + specified, in which case the records may be of any type - When using the NameResolver::Single mode, the lookup is stopped once results are ready. However, with the NameResolver::LongLived mode, the lookup stays active, and in that case this signal may be emitted multiple times. + When using the NameResolver::Single mode, the lookup is stopped once results are ready. However, with the + NameResolver::LongLived mode, the lookup stays active, and in that case this signal may be emitted multiple + times. */ void resultsReady(const QList &results); /** \brief Notification of error - This signal is emitted if an error has occurred while performing a lookup. The reason for error can be found in \a e. Regardless of the mode used, the lookup is stopped when an error occurs. + This signal is emitted if an error has occurred while performing a lookup. The reason for error can be found in + \a e. Regardless of the mode used, the lookup is stopped when an error occurs. */ void error(XMPP::NameResolver::Error e); @@ -477,53 +501,44 @@ class IRISNET_EXPORT NameResolver : public QObject IRISNET_EXPORT QDebug operator<<(QDebug, XMPP::NameResolver::Error); - -class IRISNET_EXPORT WeightedNameRecordList -{ - friend QDebug operator<<(QDebug, const WeightedNameRecordList&); +class IRISNET_EXPORT WeightedNameRecordList { + friend QDebug operator<<(QDebug, const WeightedNameRecordList &); public: WeightedNameRecordList(); WeightedNameRecordList(const QList &list); WeightedNameRecordList(const WeightedNameRecordList &other); - WeightedNameRecordList& operator=(const WeightedNameRecordList &other); + WeightedNameRecordList &operator=(const WeightedNameRecordList &other); ~WeightedNameRecordList(); - bool isEmpty() const; //!< Returns true if the list contains no items; otherwise returns false. - NameRecord takeNext(); //!< Removes the next host to try from the list and returns it. + bool isEmpty() const; //!< Returns true if the list contains no items; otherwise returns false. + NameRecord takeNext(); //!< Removes the next host to try from the list and returns it. void clear(); //!< Removes all items from the list. - void append(const WeightedNameRecordList&); - void append(const QList&); - void append(const NameRecord&); + void append(const WeightedNameRecordList &); + void append(const QList &); + void append(const NameRecord &); void append(const QString &hostname, quint16 port); - WeightedNameRecordList& operator<<(const WeightedNameRecordList&); - WeightedNameRecordList& operator<<(const QList&); - WeightedNameRecordList& operator<<(const NameRecord&); + WeightedNameRecordList &operator<<(const WeightedNameRecordList &); + WeightedNameRecordList &operator<<(const QList &); + WeightedNameRecordList &operator<<(const NameRecord &); private: - typedef QMultiMap WeightedNameRecordPriorityGroup; - typedef QMap WNRL; + typedef QMultiMap WeightedNameRecordPriorityGroup; + typedef std::map WNRL; - WNRL priorityGroups; + WNRL priorityGroups; WNRL::iterator currentPriorityGroup; }; -QDebug operator<<(QDebug, const XMPP::WeightedNameRecordList&); +QDebug operator<<(QDebug, const XMPP::WeightedNameRecordList &); - -class IRISNET_EXPORT ServiceBrowser : public QObject -{ +class IRISNET_EXPORT ServiceBrowser : public QObject { Q_OBJECT public: - enum Error - { - ErrorGeneric, - ErrorNoLocal, - ErrorNoWide - }; + enum Error { ErrorGeneric, ErrorNoLocal, ErrorNoWide }; - ServiceBrowser(QObject *parent = 0); + ServiceBrowser(QObject *parent = nullptr); ~ServiceBrowser(); void start(const QString &type, const QString &domain = "local"); @@ -542,7 +557,6 @@ class IRISNET_EXPORT ServiceBrowser : public QObject friend class NameManager; }; - /*! DNS resolver with DNS-SD/mDNS and recursive lookup support */ /* Flow: @@ -566,12 +580,10 @@ class IRISNET_EXPORT ServiceBrowser : public QObject 8) host[] empty -> (9) 9) Try servername directly */ -class IRISNET_EXPORT ServiceResolver : public QObject -{ +class IRISNET_EXPORT ServiceResolver : public QObject { Q_OBJECT public: - struct ProtoSplit - { + struct ProtoSplit { ServiceResolver *ipv4; ServiceResolver *ipv6; }; @@ -579,8 +591,10 @@ class IRISNET_EXPORT ServiceResolver : public QObject /*! Error codes for (SRV) lookups */ enum Error { ServiceNotFound, //!< There is no service with the specified parameters - NoHostLeft, //!< we did all we could, none of the found host seemed to suffice the users needs - ErrorGeneric, ErrorTimeout, ErrorNoLocal // Stuff that netnames_jdns.cpp needs ... + NoHostLeft, //!< we did all we could, none of the found host seemed to suffice the users needs + ErrorGeneric, + ErrorTimeout, + ErrorNoLocal // Stuff that netnames_jdns.cpp needs ... }; /*! Order of lookup / IP protocols to try */ enum Protocol { IPv6_IPv4, IPv4_IPv6, HappyEyeballs, IPv6, IPv4 }; @@ -589,11 +603,11 @@ class IRISNET_EXPORT ServiceResolver : public QObject * Create a new ServiceResolver. * This resolver can be used for multiple lookups in a row, but not concurrently! */ - ServiceResolver(QObject *parent = 0); + ServiceResolver(QObject *parent = nullptr); ~ServiceResolver(); - Protocol protocol() const; //!< IP protocol to use, defaults to IPv6_IPv4 - void setProtocol(Protocol); //!< Set IP protocol to use, \sa protocol + Protocol protocol() const; //!< IP protocol to use, defaults to IPv6_IPv4 + void setProtocol(Protocol); //!< Set IP protocol to use, \sa protocol /*! * Start a DNS-SD lookup @@ -614,7 +628,8 @@ class IRISNET_EXPORT ServiceResolver : public QObject * \param domain Domainname to lookup * \param port Specify a valid port number to make ServiceResolver fallback to domain:port */ - void start(const QString &service, const QString &transport, const QString &domain, int port = std::numeric_limits::max()); + void start(const QString &service, const QString &transport, const QString &domain, + int port = std::numeric_limits::max()); /*! Announce the next resolved host, \sa resultReady */ void tryNext(); @@ -634,8 +649,9 @@ class IRISNET_EXPORT ServiceResolver : public QObject * The lookup succeeded * \param address Resolved IP address * \param port Port the service resides on + * \param hostname Hostname form DNS reply with address:port */ - void resultReady(const QHostAddress &address, quint16 port); + void resultReady(const QHostAddress &address, quint16 port, const QString &hostname); /*! The lookup failed */ void error(XMPP::ServiceResolver::Error); /*! SRV domain:port records received. No IP yet. */ @@ -643,15 +659,15 @@ class IRISNET_EXPORT ServiceResolver : public QObject void srvFailed(); private slots: - void handle_srv_ready(const QList&); + void handle_srv_ready(const QList &); void handle_srv_error(XMPP::NameResolver::Error); - void handle_host_ready(const QList&); + void handle_host_ready(const QList &); void handle_host_error(XMPP::NameResolver::Error); void handle_host_fallback_error(XMPP::NameResolver::Error); private: void clear_resolvers(); - void cleanup_resolver(XMPP::NameResolver*); + void cleanup_resolver(XMPP::NameResolver *); bool check_protocol_fallback(); bool lookup_host_fallback(); bool try_next_host(); @@ -664,22 +680,20 @@ private slots: friend class NameManager; }; -class IRISNET_EXPORT ServiceLocalPublisher : public QObject -{ +class IRISNET_EXPORT ServiceLocalPublisher : public QObject { Q_OBJECT public: - enum Error - { + enum Error { ErrorGeneric, // generic error ErrorConflict, // name in use ErrorNoLocal // unable to setup multicast dns }; - ServiceLocalPublisher(QObject *parent = 0); + ServiceLocalPublisher(QObject *parent = nullptr); ~ServiceLocalPublisher(); - void publish(const QString &instance, const QString &type, int port, const QMap &attributes); - void updateAttributes(const QMap &attributes); + void publish(const QString &instance, const QString &type, int port, const QMap &attributes); + void updateAttributes(const QMap &attributes); void addRecord(const NameRecord &rec); void cancel(); @@ -695,10 +709,8 @@ class IRISNET_EXPORT ServiceLocalPublisher : public QObject friend class NameManager; }; -} - +} // namespace XMPP Q_DECLARE_METATYPE(XMPP::NameResolver::Error) - -#endif +#endif // NETNAMES_H diff --git a/src/irisnet/corelib/netnames_jdns.cpp b/src/irisnet/corelib/netnames_jdns.cpp index e1ee1a68..e1d27cdf 100644 --- a/src/irisnet/corelib/netnames_jdns.cpp +++ b/src/irisnet/corelib/netnames_jdns.cpp @@ -17,38 +17,55 @@ */ #include "irisnetplugin.h" - +#include "netinterface.h" #include "objectsession.h" #include "qjdnsshared.h" -#include "netinterface.h" -//#define JDNS_DEBUG +// #define JDNS_DEBUG Q_DECLARE_METATYPE(XMPP::NameRecord) -//Q_DECLARE_METATYPE(XMPP::NameResolver::Error) +// Q_DECLARE_METATYPE(XMPP::NameResolver::Error) Q_DECLARE_METATYPE(XMPP::ServiceBrowser::Error) Q_DECLARE_METATYPE(XMPP::ServiceResolver::Error) Q_DECLARE_METATYPE(XMPP::ServiceLocalPublisher::Error) namespace XMPP { - static NameRecord importJDNSRecord(const QJDns::Record &in) { NameRecord out; - switch(in.type) - { - case QJDns::A: out.setAddress(in.address); break; - case QJDns::Aaaa: out.setAddress(in.address); break; - case QJDns::Mx: out.setMx(in.name, in.priority); break; - case QJDns::Srv: out.setSrv(in.name, in.port, in.priority, in.weight); break; - case QJDns::Cname: out.setCname(in.name); break; - case QJDns::Ptr: out.setPtr(in.name); break; - case QJDns::Txt: out.setTxt(in.texts); break; - case QJDns::Hinfo: out.setHinfo(in.cpu, in.os); break; - case QJDns::Ns: out.setNs(in.name); break; - case 10: out.setNull(in.rdata); break; - default: - return out; + switch (in.type) { + case QJDns::A: + out.setAddress(in.address); + break; + case QJDns::Aaaa: + out.setAddress(in.address); + break; + case QJDns::Mx: + out.setMx(in.name, in.priority); + break; + case QJDns::Srv: + out.setSrv(in.name, in.port, in.priority, in.weight); + break; + case QJDns::Cname: + out.setCname(in.name); + break; + case QJDns::Ptr: + out.setPtr(in.name); + break; + case QJDns::Txt: + out.setTxt(in.texts); + break; + case QJDns::Hinfo: + out.setHinfo(in.cpu, in.os); + break; + case QJDns::Ns: + out.setNs(in.name); + break; + case 10: + out.setNull(in.rdata); + break; + default: + return out; } out.setOwner(in.owner); out.setTtl(in.ttl); @@ -58,90 +75,87 @@ static NameRecord importJDNSRecord(const QJDns::Record &in) static QJDns::Record exportJDNSRecord(const NameRecord &in) { QJDns::Record out; - switch(in.type()) - { - case NameRecord::A: - out.type = QJDns::A; - out.haveKnown = true; - out.address = in.address(); - break; - case NameRecord::Aaaa: - out.type = QJDns::Aaaa; - out.haveKnown = true; - out.address = in.address(); - break; - case NameRecord::Mx: - out.type = QJDns::Mx; - out.haveKnown = true; - out.name = in.name(); - out.priority = in.priority(); - break; - case NameRecord::Srv: - out.type = QJDns::Srv; - out.haveKnown = true; - out.name = in.name(); - out.port = in.port(); - out.priority = in.priority(); - out.weight = in.weight(); - break; - case NameRecord::Cname: - out.type = QJDns::Cname; - out.haveKnown = true; - out.name = in.name(); - break; - case NameRecord::Ptr: - out.type = QJDns::Ptr; - out.haveKnown = true; - out.name = in.name(); - break; - case NameRecord::Txt: - out.type = QJDns::Txt; - out.haveKnown = true; - out.texts = in.texts(); - break; - case NameRecord::Hinfo: - out.type = QJDns::Hinfo; - out.haveKnown = true; - out.cpu = in.cpu(); - out.os = in.os(); - break; - case NameRecord::Ns: - out.type = QJDns::Ns; - out.haveKnown = true; - out.name = in.name(); - break; - case NameRecord::Null: - out.type = 10; - out.rdata = in.rawData(); - break; - default: - return out; + switch (in.type()) { + case NameRecord::A: + out.type = QJDns::A; + out.haveKnown = true; + out.address = in.address(); + break; + case NameRecord::Aaaa: + out.type = QJDns::Aaaa; + out.haveKnown = true; + out.address = in.address(); + break; + case NameRecord::Mx: + out.type = QJDns::Mx; + out.haveKnown = true; + out.name = in.name(); + out.priority = in.priority(); + break; + case NameRecord::Srv: + out.type = QJDns::Srv; + out.haveKnown = true; + out.name = in.name(); + out.port = in.port(); + out.priority = in.priority(); + out.weight = in.weight(); + break; + case NameRecord::Cname: + out.type = QJDns::Cname; + out.haveKnown = true; + out.name = in.name(); + break; + case NameRecord::Ptr: + out.type = QJDns::Ptr; + out.haveKnown = true; + out.name = in.name(); + break; + case NameRecord::Txt: + out.type = QJDns::Txt; + out.haveKnown = true; + out.texts = in.texts(); + break; + case NameRecord::Hinfo: + out.type = QJDns::Hinfo; + out.haveKnown = true; + out.cpu = in.cpu(); + out.os = in.os(); + break; + case NameRecord::Ns: + out.type = QJDns::Ns; + out.haveKnown = true; + out.name = in.name(); + break; + case NameRecord::Null: + out.type = 10; + out.rdata = in.rawData(); + break; + default: + return out; } out.owner = in.owner(); - out.ttl = in.ttl(); + out.ttl = in.ttl(); return out; } static bool validServiceType(const QByteArray &in) { // can't be empty, or start/end with a dot - if(in.isEmpty() || in[0] == '.' || in[in.length() - 1] == '.') + if (in.isEmpty() || in[0] == '.' || in[in.length() - 1] == '.') return false; // must contain exactly one dot int dotcount = 0; - for(int n = 0; n < in.length(); ++n) - { - if(in[n] == '.') - { + for (int n = 0; n < in.length(); ++n) { + if (in[n] == '.') { ++dotcount; // no need to count more than 2 - if(dotcount >= 2) + if (dotcount >= 2) break; } } - if(dotcount != 1) + if (dotcount != 1) return false; return true; @@ -150,11 +164,10 @@ static bool validServiceType(const QByteArray &in) static QByteArray escapeDomainPart(const QByteArray &in) { QByteArray out; - for(int n = 0; n < in.length(); ++n) - { - if(in[n] == '\\') + for (int n = 0; n < in.length(); ++n) { + if (in[n] == '\\') out += "\\\\"; - else if(in[n] == '.') + else if (in[n] == '.') out += "\\."; else out += in[n]; @@ -165,47 +178,38 @@ static QByteArray escapeDomainPart(const QByteArray &in) static QByteArray unescapeDomainPart(const QByteArray &in) { QByteArray out; - for(int n = 0; n < in.length(); ++n) - { - if(in[n] == '\\') - { - if(n + 1 >= in.length()) + for (int n = 0; n < in.length(); ++n) { + if (in[n] == '\\') { + if (n + 1 >= in.length()) return QByteArray(); out += in[n + 1]; - } - else + } else out += in[n]; } return out; } -class IdManager -{ +class IdManager { private: QSet set; - int at; + int at; inline void bump_at() { - if(at == 0x7fffffff) + if (at == 0x7fffffff) at = 0; else ++at; } public: - IdManager() : - at(0) - { - } + IdManager() : at(0) { } int reserveId() { - while(1) - { - if(!set.contains(at)) - { + while (1) { + if (!set.contains(at)) { int id = at; set.insert(id); bump_at(); @@ -216,10 +220,7 @@ class IdManager } } - void releaseId(int id) - { - set.remove(id); - } + void releaseId(int id) { set.remove(id); } void clear() { @@ -231,23 +232,22 @@ class IdManager //---------------------------------------------------------------------------- // JDnsGlobal //---------------------------------------------------------------------------- -class JDnsGlobal : public QObject -{ +class JDnsGlobal : public QObject { Q_OBJECT public: - QJDnsSharedDebug db; - QJDnsShared *uni_net, *uni_local, *mul; - QHostAddress mul_addr4, mul_addr6; - NetInterfaceManager netman; - QList ifaces; - QTimer *updateTimer; + QJDnsSharedDebug db; + QJDnsShared *uni_net, *uni_local, *mul; + QHostAddress mul_addr4, mul_addr6; + NetInterfaceManager netman; + QList ifaces; + QTimer *updateTimer; JDnsGlobal() { - uni_net = 0; + uni_net = 0; uni_local = 0; - mul = 0; + mul = 0; qRegisterMetaType(); qRegisterMetaType(); @@ -270,12 +270,12 @@ class JDnsGlobal : public QObject qDeleteAll(ifaces); - QList list; - if(uni_net) + QList list; + if (uni_net) list += uni_net; - if(uni_local) + if (uni_local) list += uni_local; - if(mul) + if (mul) list += mul; // calls shutdown on the list, waits for shutdownFinished, deletes @@ -287,14 +287,12 @@ class JDnsGlobal : public QObject QJDnsShared *ensure_uni_net() { - if(!uni_net) - { + if (!uni_net) { uni_net = new QJDnsShared(QJDnsShared::UnicastInternet, this); uni_net->setDebug(&db, "U"); bool ok4 = uni_net->addInterface(QHostAddress::Any); bool ok6 = uni_net->addInterface(QHostAddress::AnyIPv6); - if(!ok4 && !ok6) - { + if (!ok4 && !ok6) { delete uni_net; uni_net = 0; } @@ -304,14 +302,12 @@ class JDnsGlobal : public QObject QJDnsShared *ensure_uni_local() { - if(!uni_local) - { + if (!uni_local) { uni_local = new QJDnsShared(QJDnsShared::UnicastLocal, this); uni_local->setDebug(&db, "L"); bool ok4 = uni_local->addInterface(QHostAddress::Any); bool ok6 = uni_local->addInterface(QHostAddress::AnyIPv6); - if(!ok4 && !ok6) - { + if (!ok4 && !ok6) { delete uni_local; uni_local = 0; } @@ -321,8 +317,7 @@ class JDnsGlobal : public QObject QJDnsShared *ensure_mul() { - if(!mul) - { + if (!mul) { mul = new QJDnsShared(QJDnsShared::Multicast, this); mul->setDebug(&db, "M"); @@ -332,8 +327,7 @@ class JDnsGlobal : public QObject // fetching should not trigger any calls to // updateMulticastInterfaces(). only future // activity should do that. - foreach(const QString &id, netman.interfaces()) - { + for (const QString &id : netman.interfaces()) { NetInterface *iface = new NetInterface(id, &netman); connect(iface, SIGNAL(unavailable()), SLOT(iface_unavailable())); ifaces += iface; @@ -344,15 +338,9 @@ class JDnsGlobal : public QObject return mul; } - bool haveMulticast4() const - { - return !mul_addr4.isNull(); - } + bool haveMulticast4() const { return !mul_addr4.isNull(); } - bool haveMulticast6() const - { - return !mul_addr6.isNull(); - } + bool haveMulticast6() const { return !mul_addr6.isNull(); } signals: void interfacesChanged(); @@ -362,7 +350,7 @@ private slots: { QStringList lines = db.readDebugLines(); #ifdef JDNS_DEBUG - for(int n = 0; n < lines.count(); ++n) + for (int n = 0; n < lines.count(); ++n) qDebug("jdns: %s\n", qPrintable(lines[n])); #else Q_UNUSED(lines); @@ -387,10 +375,7 @@ private slots: updateTimer->start(100); } - void doUpdateMulticastInterfaces() - { - updateMulticastInterfaces(true); - } + void doUpdateMulticastInterfaces() { updateMulticastInterfaces(true); } private: void updateMulticastInterfaces(bool useSignals) @@ -408,23 +393,21 @@ private slots: bool have6 = !mul_addr6.isNull(); // did we gain/lose something? - if(had4 != have4 || had6 != have6) - { - if(useSignals) + if (had4 != have4 || had6 != have6) { + if (useSignals) emit interfacesChanged(); } } void updateMulticastInterface(QHostAddress *curaddr, const QHostAddress &newaddr) { - if(!(newaddr == *curaddr)) // QHostAddress doesn't have operator!= + if (!(newaddr == *curaddr)) // QHostAddress doesn't have operator!= { - if(!curaddr->isNull()) + if (!curaddr->isNull()) mul->removeInterface(*curaddr); *curaddr = newaddr; - if(!curaddr->isNull()) - { - if(!mul->addInterface(*curaddr)) + if (!curaddr->isNull()) { + if (!mul->addInterface(*curaddr)) *curaddr = QHostAddress(); } } @@ -434,81 +417,61 @@ private slots: //---------------------------------------------------------------------------- // JDnsNameProvider //---------------------------------------------------------------------------- -class JDnsNameProvider : public NameProvider -{ +class JDnsNameProvider : public NameProvider { Q_OBJECT Q_INTERFACES(XMPP::NameProvider) public: enum Mode { Internet, Local }; - JDnsGlobal *global; - Mode mode; - IdManager idman; + JDnsGlobal *global; + Mode mode; + IdManager idman; ObjectSession sess; - class Item - { + class Item { public: - int id; + int id; QJDnsSharedRequest *req; - int type; - bool longLived; - ObjectSession sess; - bool useLocal; - bool localResult; + int type; + bool longLived; + ObjectSession sess; + bool useLocal; + bool localResult; NameResolver::Error error; NameResolver::Error localError; - Item(QObject *parent = 0) : - id(-1), - req(0), - sess(parent), - useLocal(false), - localResult(false) - { - } + Item(QObject *parent = 0) : id(-1), req(0), sess(parent), useLocal(false), localResult(false) { } - ~Item() - { - delete req; - } + ~Item() { delete req; } }; - QList items; + QList items; static JDnsNameProvider *create(JDnsGlobal *global, Mode mode, QObject *parent = 0) { - if(mode == Internet) - { - if(!global->ensure_uni_net()) + if (mode == Internet) { + if (!global->ensure_uni_net()) return 0; - } - else - { - if(!global->ensure_uni_local()) + } else { + if (!global->ensure_uni_local()) return 0; } return new JDnsNameProvider(global, mode, parent); } - JDnsNameProvider(JDnsGlobal *_global, Mode _mode, QObject *parent = 0) : - NameProvider(parent) + JDnsNameProvider(JDnsGlobal *_global, Mode _mode, QObject *parent = 0) : NameProvider(parent) { global = _global; - mode = _mode; + mode = _mode; } - ~JDnsNameProvider() - { - qDeleteAll(items); - } + ~JDnsNameProvider() { qDeleteAll(items); } Item *getItemById(int id) { - for(int n = 0; n < items.count(); ++n) - { - if(items[n]->id == id) + for (int n = 0; n < items.count(); ++n) { + if (items[n]->id == id) return items[n]; } @@ -517,9 +480,8 @@ class JDnsNameProvider : public NameProvider Item *getItemByReq(QJDnsSharedRequest *req) { - for(int n = 0; n < items.count(); ++n) - { - if(items[n]->req == req) + for (int n = 0; n < items.count(); ++n) { + if (items[n]->req == req) return items[n]; } @@ -536,24 +498,21 @@ class JDnsNameProvider : public NameProvider void tryError(Item *i) { // if we are doing dual resolves, make sure both are done - if(!i->longLived && (i->req || (i->useLocal && !i->localResult))) + if (!i->longLived && (i->req || (i->useLocal && !i->localResult))) return; - int id = i->id; + int id = i->id; NameResolver::Error error = i->error; releaseItem(i); emit resolve_error(id, error); } - virtual bool supportsSingle() const - { - return true; - } + virtual bool supportsSingle() const { return true; } virtual bool supportsLongLived() const { - if(mode == Local) - return true; // we support long-lived local queries + if (mode == Local) + return true; // we support long-lived local queries else return false; // we do NOT support long-lived internet queries } @@ -567,10 +526,9 @@ class JDnsNameProvider : public NameProvider virtual int resolve_start(const QByteArray &name, int qType, bool longLived) { - if(mode == Internet) - { + if (mode == Internet) { bool isLocalName = false; - if(name.right(6) == ".local" || name.right(7) == ".local.") + if (name.right(6) == ".local" || name.right(7) == ".local.") isLocalName = true; // if query ends in .local, switch to local resolver @@ -585,65 +543,57 @@ class JDnsNameProvider : public NameProvider }*/ // we don't support long-lived internet queries - if(longLived) - { + if (longLived) { // but we do support long-lived local queries - if(isLocalName) - { - Item *i = new Item(this); - i->id = idman.reserveId(); + if (isLocalName) { + Item *i = new Item(this); + i->id = idman.reserveId(); i->longLived = longLived; - i->useLocal = true; + i->useLocal = true; items += i; i->sess.defer(this, "do_local", Q_ARG(int, i->id), Q_ARG(QByteArray, name)); return i->id; } Item *i = new Item(this); - i->id = idman.reserveId(); + i->id = idman.reserveId(); items += i; i->sess.defer(this, "do_error", Q_ARG(int, i->id), - Q_ARG(XMPP::NameResolver::Error, NameResolver::ErrorNoLongLived)); + Q_ARG(XMPP::NameResolver::Error, NameResolver::ErrorNoLongLived)); return i->id; } // perform the query Item *i = new Item(this); - i->id = idman.reserveId(); - i->req = new QJDnsSharedRequest(global->uni_net); + i->id = idman.reserveId(); + i->req = new QJDnsSharedRequest(global->uni_net); connect(i->req, SIGNAL(resultsReady()), SLOT(req_resultsReady())); - i->type = qType; + i->type = qType; i->longLived = false; - if(isLocalName) + if (isLocalName) i->useLocal = true; items += i; i->req->query(name, qType); // if query ends in .local, simultaneously do local resolve - if(isLocalName) + if (isLocalName) i->sess.defer(this, "do_local", Q_ARG(int, i->id), Q_ARG(QByteArray, name)); return i->id; - } - else - { + } else { Item *i = new Item(this); - i->id = idman.reserveId(); + i->id = idman.reserveId(); i->type = qType; - if(longLived) - { - if(!global->ensure_mul()) - { + if (longLived) { + if (!global->ensure_mul()) { items += i; i->sess.defer(this, "do_error", Q_ARG(int, i->id), - Q_ARG(XMPP::NameResolver::Error, NameResolver::ErrorNoLocal)); + Q_ARG(XMPP::NameResolver::Error, NameResolver::ErrorNoLocal)); return i->id; } - i->req = new QJDnsSharedRequest(global->mul); + i->req = new QJDnsSharedRequest(global->mul); i->longLived = true; - } - else - { - i->req = new QJDnsSharedRequest(global->uni_local); + } else { + i->req = new QJDnsSharedRequest(global->uni_local); i->longLived = false; } connect(i->req, SIGNAL(resultsReady()), SLOT(req_resultsReady())); @@ -658,7 +608,7 @@ class JDnsNameProvider : public NameProvider Item *i = getItemById(id); Q_ASSERT(i); - if(i->req) + if (i->req) i->req->cancel(); releaseItem(i); } @@ -670,8 +620,7 @@ class JDnsNameProvider : public NameProvider Q_ASSERT(!i->localResult); i->localResult = true; - i->sess.defer(this, "do_local_ready", Q_ARG(int, id), - Q_ARG(QList, results)); + i->sess.defer(this, "do_local_ready", Q_ARG(int, id), Q_ARG(QList, results)); } virtual void resolve_localError(int id, XMPP::NameResolver::Error e) @@ -681,71 +630,62 @@ class JDnsNameProvider : public NameProvider Q_ASSERT(!i->localResult); i->localResult = true; - i->sess.defer(this, "do_local_error", Q_ARG(int, id), - Q_ARG(XMPP::NameResolver::Error, e)); + i->sess.defer(this, "do_local_error", Q_ARG(int, id), Q_ARG(XMPP::NameResolver::Error, e)); } private slots: void req_resultsReady() { QJDnsSharedRequest *req = static_cast(sender()); - Item *i = getItemByReq(req); + Item *i = getItemByReq(req); Q_ASSERT(i); int id = i->id; NameResolver::Error error; - if(req->success()) - { + if (req->success()) { QList out; - foreach(const QJDns::Record &r, req->results()) - { + for (const QJDns::Record &r : req->results()) { // unless we are asking for all types, only // accept the type we asked for - if(i->type == QJDns::Any || r.type == i->type) - { + if (i->type == QJDns::Any || r.type == i->type) { NameRecord rec = importJDNSRecord(r); - if(!rec.isNull()) + if (!rec.isNull()) out += rec; } } // don't report anything if long-lived gives no results - if(i->longLived && out.isEmpty()) + if (i->longLived && out.isEmpty()) return; // only emit success if we have at least 1 result - if(!out.isEmpty()) - { + if (!out.isEmpty()) { // FIXME: need a way to cancel related local // query if still active - if(!i->longLived) + if (!i->longLived) releaseItem(i); emit resolve_resultsReady(id, out); return; - } - else - { + } else { error = NameResolver::ErrorGeneric; } - } - else - { + } else { QJDnsSharedRequest::Error e = req->error(); error = NameResolver::ErrorGeneric; - if(e == QJDnsSharedRequest::ErrorNXDomain) + if (e == QJDnsSharedRequest::ErrorNXDomain) error = NameResolver::ErrorNoName; - else if(e == QJDnsSharedRequest::ErrorTimeout) + else if (e == QJDnsSharedRequest::ErrorTimeout) error = NameResolver::ErrorTimeout; else // ErrorGeneric or ErrorNoNet error = NameResolver::ErrorGeneric; } delete i->req; - i->req = 0; + i->req = 0; i->error = error; tryError(i); } @@ -764,8 +704,8 @@ private slots: void do_local(int id, const QByteArray &name) { - //Item *i = getItemById(id); - //Q_ASSERT(i); + // Item *i = getItemById(id); + // Q_ASSERT(i); /*// resolve_useLocal has two behaviors: // - if longlived, then it indicates a hand-off @@ -781,10 +721,9 @@ private slots: Item *i = getItemById(id); Q_ASSERT(i); - if(!i->longLived) - { + if (!i->longLived) { // stop any simultaneous internet resolve - if(i->req) + if (i->req) i->req->cancel(); // for non-longlived, we're done @@ -807,17 +746,14 @@ private slots: //---------------------------------------------------------------------------- // JDnsBrowse //---------------------------------------------------------------------------- -class JDnsBrowse : public QObject -{ +class JDnsBrowse : public QObject { Q_OBJECT public: - QByteArray type, typeAndDomain; + QByteArray type, typeAndDomain; QJDnsSharedRequest req; - JDnsBrowse(QJDnsShared *_jdns, QObject *parent = 0) : - QObject(parent), - req(_jdns, this) + JDnsBrowse(QJDnsShared *_jdns, QObject *parent = 0) : QObject(parent), req(_jdns, this) { connect(&req, SIGNAL(resultsReady()), SLOT(jdns_resultsReady())); } @@ -838,19 +774,19 @@ class JDnsBrowse : public QObject QByteArray parseInstanceName(const QByteArray &name) { // needs to be at least X + '.' + typeAndDomain - if(name.length() < typeAndDomain.length() + 2) + if (name.length() < typeAndDomain.length() + 2) return QByteArray(); // index of the '.' character int at = name.length() - typeAndDomain.length() - 1; - if(name[at] != '.') + if (name[at] != '.') return QByteArray(); - if(name.mid(at + 1) != typeAndDomain) + if (name.mid(at + 1) != typeAndDomain) return QByteArray(); QByteArray friendlyName = unescapeDomainPart(name.mid(0, at)); - if(friendlyName.isEmpty()) + if (friendlyName.isEmpty()) return QByteArray(); return friendlyName; @@ -860,20 +796,19 @@ private slots: void jdns_resultsReady() { // ignore errors - if(!req.success()) + if (!req.success()) return; QJDns::Record rec = req.results().first(); Q_ASSERT(rec.type == QJDns::Ptr); - QByteArray name = rec.name; + QByteArray name = rec.name; QByteArray instance = parseInstanceName(name); - if(instance.isEmpty()) + if (instance.isEmpty()) return; - if(rec.ttl == 0) - { + if (rec.ttl == 0) { emit unavailable(instance); return; } @@ -888,37 +823,28 @@ private slots: // 5 second timeout waiting for both A and AAAA // 8 second timeout waiting for at least one record -class JDnsServiceResolve : public QObject -{ +class JDnsServiceResolve : public QObject { Q_OBJECT public: - enum SrvState - { - Srv = 0, - AddressWait = 1, - AddressFirstCome = 2 - }; + enum SrvState { Srv = 0, AddressWait = 1, AddressFirstCome = 2 }; QJDnsSharedRequest reqtxt; // for TXT QJDnsSharedRequest req; // for SRV/A QJDnsSharedRequest req6; // for AAAA - bool haveTxt; - SrvState srvState; - QTimer *opTimer; + bool haveTxt; + SrvState srvState; + QTimer *opTimer; // out QList attribs; - QByteArray host; - int port; - bool have4, have6; - QHostAddress addr4, addr6; + QByteArray host; + int port; + bool have4, have6; + QHostAddress addr4, addr6; JDnsServiceResolve(QJDnsShared *_jdns, QObject *parent = 0) : - QObject(parent), - reqtxt(_jdns, this), - req(_jdns, this), - req6(_jdns, this) + QObject(parent), reqtxt(_jdns, this), req(_jdns, this), req6(_jdns, this) { connect(&reqtxt, SIGNAL(resultsReady()), SLOT(reqtxt_ready())); connect(&req, SIGNAL(resultsReady()), SLOT(req_ready())); @@ -938,10 +864,10 @@ class JDnsServiceResolve : public QObject void start(const QByteArray name) { - haveTxt = false; + haveTxt = false; srvState = Srv; - have4 = false; - have6 = false; + have4 = false; + have6 = false; opTimer->start(8000); @@ -956,21 +882,20 @@ class JDnsServiceResolve : public QObject private: void cleanup() { - if(opTimer->isActive()) + if (opTimer->isActive()) opTimer->stop(); - if(!haveTxt) + if (!haveTxt) reqtxt.cancel(); - if(srvState == Srv || !have4) + if (srvState == Srv || !have4) req.cancel(); - if(srvState >= AddressWait && !have6) + if (srvState >= AddressWait && !have6) req6.cancel(); } bool tryDone() { // we're done when we have txt and addresses - if(haveTxt && ( (have4 && have6) || (srvState == AddressFirstCome && (have4 || have6)) )) - { + if (haveTxt && ((have4 && have6) || (srvState == AddressFirstCome && (have4 || have6)))) { cleanup(); emit finished(); return true; @@ -982,8 +907,7 @@ class JDnsServiceResolve : public QObject private slots: void reqtxt_ready() { - if(!reqtxt.success()) - { + if (!reqtxt.success()) { cleanup(); emit error(reqtxt.error()); return; @@ -995,11 +919,10 @@ private slots: Q_ASSERT(rec.type == QJDns::Txt); attribs.clear(); - if(!rec.texts.isEmpty()) - { + if (!rec.texts.isEmpty()) { // if there is only 1 text, it needs to be // non-empty for us to care - if(rec.texts.count() != 1 || !rec.texts[0].isEmpty()) + if (rec.texts.count() != 1 || !rec.texts[0].isEmpty()) attribs = rec.texts; } @@ -1010,8 +933,7 @@ private slots: void req_ready() { - if(!req.success()) - { + if (!req.success()) { cleanup(); emit error(req.error()); return; @@ -1020,8 +942,7 @@ private slots: QJDns::Record rec = req.results().first(); req.cancel(); - if(srvState == Srv) - { + if (srvState == Srv) { // in Srv state, req is used for SRV records Q_ASSERT(rec.type == QJDns::Srv); @@ -1034,9 +955,7 @@ private slots: req.query(host, QJDns::A); req6.query(host, QJDns::Aaaa); - } - else - { + } else { // in the other states, req is used for A records Q_ASSERT(rec.type == QJDns::A); @@ -1050,8 +969,7 @@ private slots: void req6_ready() { - if(!req6.success()) - { + if (!req6.success()) { cleanup(); emit error(req6.error()); return; @@ -1070,17 +988,14 @@ private slots: void op_timeout() { - if(srvState == Srv) - { + if (srvState == Srv) { // timeout getting SRV. it is possible that we could // have obtained the TXT record, but if SRV times // out then we consider the whole job to have // failed. cleanup(); emit error(QJDnsSharedRequest::ErrorTimeout); - } - else if(srvState == AddressWait) - { + } else if (srvState == AddressWait) { // timeout while waiting for both A and AAAA. we now // switch to the AddressFirstCome state, where an // answer for either will do @@ -1088,11 +1003,10 @@ private slots: srvState = AddressFirstCome; // if we have at least one of these, we're done - if(have4 || have6) - { + if (have4 || have6) { // well, almost. we might still be waiting // for the TXT record - if(tryDone()) + if (tryDone()) return; } @@ -1101,12 +1015,10 @@ private slots: // wait 3 more seconds opTimer->start(3000); - } - else // AddressFirstCome + } else // AddressFirstCome { // last chance! - if(!tryDone()) - { + if (!tryDone()) { cleanup(); emit error(QJDnsSharedRequest::ErrorTimeout); } @@ -1119,27 +1031,20 @@ private slots: //---------------------------------------------------------------------------- // helper class for JDnsPublishAddresses. publishes A+PTR or AAAA+PTR pair. -class JDnsPublishAddress : public QObject -{ +class JDnsPublishAddress : public QObject { Q_OBJECT public: - enum Type - { - IPv4, - IPv6 - }; + enum Type { IPv4, IPv6 }; - Type type; - QByteArray host; + Type type; + QByteArray host; QJDnsSharedRequest pub_addr; QJDnsSharedRequest pub_ptr; - bool success_; + bool success_; JDnsPublishAddress(QJDnsShared *_jdns, QObject *parent = 0) : - QObject(parent), - pub_addr(_jdns, this), - pub_ptr(_jdns, this) + QObject(parent), pub_addr(_jdns, this), pub_ptr(_jdns, this) { connect(&pub_addr, SIGNAL(resultsReady()), SLOT(pub_addr_ready())); connect(&pub_ptr, SIGNAL(resultsReady()), SLOT(pub_ptr_ready())); @@ -1147,19 +1052,19 @@ class JDnsPublishAddress : public QObject void start(Type _type, const QByteArray &_host) { - type = _type; - host = _host; + type = _type; + host = _host; success_ = false; QJDns::Record rec; - if(type == IPv6) + if (type == IPv6) rec.type = QJDns::Aaaa; else rec.type = QJDns::A; - rec.owner = host; - rec.ttl = 120; + rec.owner = host; + rec.ttl = 120; rec.haveKnown = true; - rec.address = QHostAddress(); // null address, will be filled in + rec.address = QHostAddress(); // null address, will be filled in pub_addr.publish(QJDns::Unique, rec); } @@ -1169,10 +1074,7 @@ class JDnsPublishAddress : public QObject pub_ptr.cancel(); } - bool success() const - { - return success_; - } + bool success() const { return success_; } signals: void resultsReady(); @@ -1180,21 +1082,18 @@ class JDnsPublishAddress : public QObject private slots: void pub_addr_ready() { - if(pub_addr.success()) - { + if (pub_addr.success()) { QJDns::Record rec; rec.type = QJDns::Ptr; - if(type == IPv6) + if (type == IPv6) rec.owner = ".ip6.arpa."; else rec.owner = ".in-addr.arpa."; - rec.ttl = 120; + rec.ttl = 120; rec.haveKnown = true; - rec.name = host; + rec.name = host; pub_ptr.publish(QJDns::Shared, rec); - } - else - { + } else { pub_ptr.cancel(); // needed if addr fails during or after ptr success_ = false; emit resultsReady(); @@ -1203,12 +1102,9 @@ private slots: void pub_ptr_ready() { - if(pub_ptr.success()) - { + if (pub_ptr.success()) { success_ = true; - } - else - { + } else { pub_addr.cancel(); success_ = false; } @@ -1228,29 +1124,22 @@ private slots: // once the conflict is resolved. A missing hostname is considered a // temporary problem, and so other publish operations that depend on a // hostname (SRV, etc) should block until a hostname is available. -class JDnsPublishAddresses : public QObject -{ +class JDnsPublishAddresses : public QObject { Q_OBJECT public: - bool started; - bool use6, use4; + bool started; + bool use6, use4; JDnsPublishAddress pub6; JDnsPublishAddress pub4; - int counter; - QByteArray host; - bool success; - bool have6, have4; - ObjectSession sess; + int counter; + QByteArray host; + bool success; + bool have6, have4; + ObjectSession sess; JDnsPublishAddresses(QJDnsShared *_jdns, QObject *parent = 0) : - QObject(parent), - started(false), - use6(false), - use4(false), - pub6(_jdns, this), - pub4(_jdns, this), - sess(this) + QObject(parent), started(false), use6(false), use4(false), pub6(_jdns, this), pub4(_jdns, this), sess(this) { connect(&pub6, SIGNAL(resultsReady()), SLOT(pub6_ready())); connect(&pub4, SIGNAL(resultsReady()), SLOT(pub4_ready())); @@ -1260,25 +1149,22 @@ class JDnsPublishAddresses : public QObject { counter = 1; success = false; - have6 = false; - have4 = false; + have6 = false; + have4 = false; started = true; tryPublish(); } - bool isStarted() const - { - return started; - } + bool isStarted() const { return started; } // comments in this method apply to setUseIPv4 as well. void setUseIPv6(bool b) { - if(b == use6) + if (b == use6) return; use6 = b; - if(!started) + if (!started) return; // a "deferred call to doDisable" and "publish operations" @@ -1287,58 +1173,46 @@ class JDnsPublishAddresses : public QObject // deferred call is canceled if any of the publishes are // reinstantiated. - if(use6) - { - if(use4) - { + if (use6) { + if (use4) { // if the other is already active, then // just activate this one without // recomputing the hostname tryPublish6(); - } - else - { + } else { sess.reset(); // otherwise, recompute the hostname tryPublish(); } - } - else - { + } else { pub6.cancel(); have6 = false; - if(!use4) + if (!use4) sess.defer(this, "doDisable"); } } void setUseIPv4(bool b) { - if(b == use4) + if (b == use4) return; use4 = b; - if(!started) + if (!started) return; - if(use4) - { - if(use6) - { + if (use4) { + if (use6) { tryPublish4(); - } - else - { + } else { sess.reset(); tryPublish(); } - } - else - { + } else { pub4.cancel(); have4 = false; - if(!use6) + if (!use6) sess.defer(this, "doDisable"); } } @@ -1352,44 +1226,37 @@ class JDnsPublishAddresses : public QObject QString me = QHostInfo::localHostName(); // some hosts may already have ".local" in their name - if(me.endsWith(".local")) + if (me.endsWith(".local")) me.truncate(me.length() - 6); // prefix our hostname so we don't conflict with a system // mdns daemon me.prepend("jdns-"); - if(counter > 1) + if (counter > 1) me += QString("-%1").arg(counter); host = escapeDomainPart(me.toUtf8()) + ".local."; - if(use6) + if (use6) tryPublish6(); - if(use4) + if (use4) tryPublish4(); } - void tryPublish6() - { - pub6.start(JDnsPublishAddress::IPv6, host); - } + void tryPublish6() { pub6.start(JDnsPublishAddress::IPv6, host); } - void tryPublish4() - { - pub4.start(JDnsPublishAddress::IPv4, host); - } + void tryPublish4() { pub4.start(JDnsPublishAddress::IPv4, host); } void tryDone() { bool done = true; - if(use6 && !have6) + if (use6 && !have6) done = false; - if(use4 && !have4) + if (use4 && !have4) done = false; - if(done) - { + if (done) { success = true; emit hostName(host); } @@ -1402,11 +1269,11 @@ class JDnsPublishAddresses : public QObject // latter case it means we "lost" our host records. bool lostHost = success; // as in earlier publish success - success = false; + success = false; // if we lost a hostname with a suffix, or counter is // at 99, then start counter over at 1 (no suffix). - if((lostHost && counter > 1) || counter >= 99) + if ((lostHost && counter > 1) || counter >= 99) counter = 1; else ++counter; @@ -1414,7 +1281,7 @@ class JDnsPublishAddresses : public QObject tryPublish(); // only emit lost host signal once - if(lostHost) + if (lostHost) emit hostName(QByteArray()); } @@ -1422,21 +1289,18 @@ private slots: void doDisable() { bool lostHost = success; - success = false; + success = false; - if(lostHost) + if (lostHost) emit hostName(QByteArray()); } void pub6_ready() { - if(pub6.success()) - { + if (pub6.success()) { have6 = true; tryDone(); - } - else - { + } else { have6 = false; have4 = false; pub4.cancel(); @@ -1446,13 +1310,10 @@ private slots: void pub4_ready() { - if(pub4.success()) - { + if (pub4.success()) { have4 = true; tryDone(); - } - else - { + } else { have4 = false; have6 = false; pub6.cancel(); @@ -1466,8 +1327,7 @@ private slots: //---------------------------------------------------------------------------- class JDnsPublish; -class JDnsPublishExtra : public QObject -{ +class JDnsPublishExtra : public QObject { Q_OBJECT public: @@ -1484,12 +1344,12 @@ class JDnsPublishExtra : public QObject private: friend class JDnsPublish; - JDnsPublish *jdnsPub; - bool started; + JDnsPublish *jdnsPub; + bool started; QJDnsSharedRequest pub; - QJDns::Record rec; - bool have; - bool need_update; + QJDns::Record rec; + bool have; + bool need_update; }; // This class publishes SRV/TXT/PTR for a service. if a hostName is not @@ -1505,12 +1365,11 @@ class JDnsPublishExtra : public QObject // It's important to note that published() is only emitted once ever, even // if a hostName change causes a republishing. this way, hostName changes // are completely transparent. -class JDnsPublish : public QObject -{ +class JDnsPublish : public QObject { Q_OBJECT public: - QJDnsShared *jdns; + QJDnsShared *jdns; QJDnsSharedRequest pub_srv; QJDnsSharedRequest pub_txt; QJDnsSharedRequest pub_ptr; @@ -1518,62 +1377,55 @@ class JDnsPublish : public QObject bool have_srv, have_txt, have_ptr; bool need_update_txt; - QByteArray fullname; - QByteArray instance; - QByteArray type; - QByteArray host; - int port; + QByteArray fullname; + QByteArray instance; + QByteArray type; + QByteArray host; + int port; QList attribs; - QSet extraList; + QSet extraList; JDnsPublish(QJDnsShared *_jdns, QObject *parent = 0) : - QObject(parent), - jdns(_jdns), - pub_srv(_jdns, this), - pub_txt(_jdns, this), - pub_ptr(_jdns, this) + QObject(parent), jdns(_jdns), pub_srv(_jdns, this), pub_txt(_jdns, this), pub_ptr(_jdns, this) { connect(&pub_srv, SIGNAL(resultsReady()), SLOT(pub_srv_ready())); connect(&pub_txt, SIGNAL(resultsReady()), SLOT(pub_txt_ready())); connect(&pub_ptr, SIGNAL(resultsReady()), SLOT(pub_ptr_ready())); } - ~JDnsPublish() - { - qDeleteAll(extraList); - } + ~JDnsPublish() { qDeleteAll(extraList); } - void start(const QString &_instance, const QByteArray &_type, const QByteArray &localHost, int _port, const QMap &attributes) + void start(const QString &_instance, const QByteArray &_type, const QByteArray &localHost, int _port, + const QMap &attributes) { type = _type; Q_ASSERT(validServiceType(type)); instance = escapeDomainPart(_instance.toUtf8()); fullname = instance + '.' + type + ".local."; - host = localHost; - port = _port; - attribs = makeTxtList(attributes); + host = localHost; + port = _port; + attribs = makeTxtList(attributes); - have_srv = false; - have_txt = false; - have_ptr = false; + have_srv = false; + have_txt = false; + have_ptr = false; need_update_txt = false; // no host? defer publishing till we have one - if(host.isEmpty()) + if (host.isEmpty()) return; doPublish(); } - void update(const QMap &attributes) + void update(const QMap &attributes) { attribs = makeTxtList(attributes); // still publishing the initial txt? - if(!have_txt) - { + if (!have_txt) { // flag that we want to update once the publish // succeeds. need_update_txt = true; @@ -1582,8 +1434,7 @@ class JDnsPublish : public QObject // no SRV, but have TXT? this means we lost SRV due to // a hostname change. - if(!have_srv) - { + if (!have_srv) { // in that case, revoke the TXT. it'll get // republished after SRV then. have_txt = false; @@ -1600,18 +1451,14 @@ public slots: { bool changed = (host != _host); - if(changed) - { + if (changed) { host = _host; - if(host.isEmpty()) - { + if (host.isEmpty()) { // cancel srv record momentarily have_srv = false; pub_srv.cancel(); - } - else - { + } else { // we now have a host, publish doPublish(); } @@ -1625,17 +1472,16 @@ public slots: private: friend class JDnsPublishExtra; - static QList makeTxtList(const QMap &attributes) + static QList makeTxtList(const QMap &attributes) { QList out; - QMapIterator it(attributes); - while(it.hasNext()) - { + QMapIterator it(attributes); + while (it.hasNext()) { it.next(); out += it.key().toLatin1() + '=' + it.value(); } - if(out.isEmpty()) + if (out.isEmpty()) out += QByteArray(); return out; @@ -1645,25 +1491,24 @@ public slots: { // SRV QJDns::Record rec; - rec.type = QJDns::Srv; - rec.owner = fullname; - rec.ttl = 120; + rec.type = QJDns::Srv; + rec.owner = fullname; + rec.ttl = 120; rec.haveKnown = true; - rec.name = host; - rec.port = port; - rec.priority = 0; - rec.weight = 0; + rec.name = host; + rec.port = port; + rec.priority = 0; + rec.weight = 0; pub_srv.publish(QJDns::Unique, rec); // if we're just republishing SRV after losing/regaining // our hostname, then TXT is already published - if(!have_txt) + if (!have_txt) doPublishTxt(); // publish extra records as needed - foreach(JDnsPublishExtra *extra, extraList) - { - if(!extra->have) + for (JDnsPublishExtra *extra : extraList) { + if (!extra->have) doPublishExtra(extra); } } @@ -1672,13 +1517,13 @@ public slots: { // TXT QJDns::Record rec; - rec.type = QJDns::Txt; - rec.owner = fullname; - rec.ttl = 4500; + rec.type = QJDns::Txt; + rec.owner = fullname; + rec.ttl = 4500; rec.haveKnown = true; - rec.texts = attribs; + rec.texts = attribs; - if(!have_txt) + if (!have_txt) pub_txt.publish(QJDns::Unique, rec); else pub_txt.publishUpdate(rec); @@ -1686,22 +1531,21 @@ public slots: void tryDone() { - if(have_srv && have_txt) - { + if (have_srv && have_txt) { // PTR QJDns::Record rec; - rec.type = QJDns::Ptr; - rec.owner = type + ".local."; - rec.ttl = 4500; + rec.type = QJDns::Ptr; + rec.owner = type + ".local."; + rec.ttl = 4500; rec.haveKnown = true; - rec.name = fullname; + rec.name = fullname; pub_ptr.publish(QJDns::Shared, rec); } } void cleanup() { - foreach(JDnsPublishExtra *extra, extraList) + for (JDnsPublishExtra *extra : extraList) cleanupExtra(extra); qDeleteAll(extraList); extraList.clear(); @@ -1722,7 +1566,7 @@ public slots: extraList += extra; // defer publishing until SRV is ready - if(!have_srv) + if (!have_srv) return; doPublishExtra(extra); @@ -1730,14 +1574,12 @@ public slots: void publishExtraUpdate(JDnsPublishExtra *extra) { - if(!extra->have) - { + if (!extra->have) { extra->need_update = true; return; } - if(!have_srv) - { + if (!have_srv) { extra->have = false; extra->pub.cancel(); return; @@ -1746,14 +1588,11 @@ public slots: doPublishExtra(extra); } - void unpublishExtra(JDnsPublishExtra *extra) - { - extraList.remove(extra); - } + void unpublishExtra(JDnsPublishExtra *extra) { extraList.remove(extra); } void doPublishExtra(JDnsPublishExtra *extra) { - if(!extra->have) + if (!extra->have) extra->pub.publish(QJDns::Unique, extra->rec); else extra->pub.publishUpdate(extra->rec); @@ -1764,19 +1603,16 @@ public slots: extra->pub.cancel(); extra->disconnect(this); extra->started = false; - extra->have = false; + extra->have = false; } private slots: void pub_srv_ready() { - if(pub_srv.success()) - { + if (pub_srv.success()) { have_srv = true; tryDone(); - } - else - { + } else { QJDnsSharedRequest::Error e = pub_srv.error(); cleanup(); emit error(e); @@ -1785,20 +1621,16 @@ private slots: void pub_txt_ready() { - if(pub_txt.success()) - { + if (pub_txt.success()) { have_txt = true; - if(need_update_txt) - { + if (need_update_txt) { need_update_txt = false; doPublishTxt(); } tryDone(); - } - else - { + } else { QJDnsSharedRequest::Error e = pub_txt.error(); cleanup(); emit error(e); @@ -1807,13 +1639,10 @@ private slots: void pub_ptr_ready() { - if(pub_ptr.success()) - { + if (pub_ptr.success()) { have_ptr = true; emit published(); - } - else - { + } else { QJDnsSharedRequest::Error e = pub_ptr.error(); cleanup(); emit error(e); @@ -1822,32 +1651,26 @@ private slots: void pub_extra_ready() { - QJDnsSharedRequest *req = static_cast(sender()); - JDnsPublishExtra *extra = 0; - foreach(JDnsPublishExtra *e, extraList) - { - if(&e->pub == req) - { + QJDnsSharedRequest *req = static_cast(sender()); + JDnsPublishExtra *extra = 0; + for (JDnsPublishExtra *e : extraList) { + if (&e->pub == req) { extra = e; break; } } Q_ASSERT(extra); - if(extra->pub.success()) - { + if (extra->pub.success()) { extra->have = true; - if(extra->need_update) - { + if (extra->need_update) { extra->need_update = false; doPublishExtra(extra); } emit extra->published(); - } - else - { + } else { QJDnsSharedRequest::Error e = extra->pub.error(); cleanupExtra(extra); emit extra->error(e); @@ -1856,24 +1679,21 @@ private slots: }; JDnsPublishExtra::JDnsPublishExtra(JDnsPublish *_jdnsPub) : - QObject(_jdnsPub), - jdnsPub(_jdnsPub), - started(false), - pub(_jdnsPub->jdns, this) + QObject(_jdnsPub), jdnsPub(_jdnsPub), started(false), pub(_jdnsPub->jdns, this) { } JDnsPublishExtra::~JDnsPublishExtra() { - if(started) + if (started) jdnsPub->unpublishExtra(this); } void JDnsPublishExtra::start(const QJDns::Record &_rec) { - rec = _rec; - started = true; - have = false; + rec = _rec; + started = true; + have = false; need_update = false; jdnsPub->publishExtra(this); } @@ -1887,19 +1707,13 @@ void JDnsPublishExtra::update(const QJDns::Record &_rec) //---------------------------------------------------------------------------- // JDnsServiceProvider //---------------------------------------------------------------------------- -class BrowseItem -{ +class BrowseItem { public: - const int id; - JDnsBrowse * const browse; - ObjectSession *sess; + const int id; + JDnsBrowse *const browse; + ObjectSession *sess; - BrowseItem(int _id, JDnsBrowse *_browse) : - id(_id), - browse(_browse), - sess(0) - { - } + BrowseItem(int _id, JDnsBrowse *_browse) : id(_id), browse(_browse), sess(0) { } ~BrowseItem() { @@ -1908,24 +1722,17 @@ class BrowseItem } }; -class BrowseItemList -{ +class BrowseItemList { private: - QSet items; - QHash indexById; - QHash indexByBrowse; - IdManager idman; + QSet items; + QHash indexById; + QHash indexByBrowse; + IdManager idman; public: - ~BrowseItemList() - { - qDeleteAll(items); - } + ~BrowseItemList() { qDeleteAll(items); } - int reserveId() - { - return idman.reserveId(); - } + int reserveId() { return idman.reserveId(); } void insert(BrowseItem *item) { @@ -1939,35 +1746,23 @@ class BrowseItemList indexById.remove(item->id); indexByBrowse.remove(item->browse); items.remove(item); - if(item->id != -1) + if (item->id != -1) idman.releaseId(item->id); delete item; } - BrowseItem *itemById(int id) const - { - return indexById.value(id); - } + BrowseItem *itemById(int id) const { return indexById.value(id); } - BrowseItem *itemByBrowse(JDnsBrowse *browse) const - { - return indexByBrowse.value(browse); - } + BrowseItem *itemByBrowse(JDnsBrowse *browse) const { return indexByBrowse.value(browse); } }; -class ResolveItem -{ +class ResolveItem { public: - const int id; - JDnsServiceResolve * const resolve; - ObjectSession *sess; + const int id; + JDnsServiceResolve *const resolve; + ObjectSession *sess; - ResolveItem(int _id, JDnsServiceResolve *_resolve) : - id(_id), - resolve(_resolve), - sess(0) - { - } + ResolveItem(int _id, JDnsServiceResolve *_resolve) : id(_id), resolve(_resolve), sess(0) { } ~ResolveItem() { @@ -1976,24 +1771,17 @@ class ResolveItem } }; -class ResolveItemList -{ +class ResolveItemList { private: - QSet items; - QHash indexById; - QHash indexByResolve; - IdManager idman; + QSet items; + QHash indexById; + QHash indexByResolve; + IdManager idman; public: - ~ResolveItemList() - { - qDeleteAll(items); - } + ~ResolveItemList() { qDeleteAll(items); } - int reserveId() - { - return idman.reserveId(); - } + int reserveId() { return idman.reserveId(); } void insert(ResolveItem *item) { @@ -2007,35 +1795,23 @@ class ResolveItemList indexById.remove(item->id); indexByResolve.remove(item->resolve); items.remove(item); - if(item->id != -1) + if (item->id != -1) idman.releaseId(item->id); delete item; } - ResolveItem *itemById(int id) const - { - return indexById.value(id); - } + ResolveItem *itemById(int id) const { return indexById.value(id); } - ResolveItem *itemByResolve(JDnsServiceResolve *resolve) const - { - return indexByResolve.value(resolve); - } + ResolveItem *itemByResolve(JDnsServiceResolve *resolve) const { return indexByResolve.value(resolve); } }; -class PublishItem -{ +class PublishItem { public: - const int id; - JDnsPublish * const publish; - ObjectSession *sess; + const int id; + JDnsPublish *const publish; + ObjectSession *sess; - PublishItem(int _id, JDnsPublish *_publish) : - id(_id), - publish(_publish), - sess(0) - { - } + PublishItem(int _id, JDnsPublish *_publish) : id(_id), publish(_publish), sess(0) { } ~PublishItem() { @@ -2044,26 +1820,19 @@ class PublishItem } }; -class PublishItemList -{ +class PublishItemList { public: - QSet items; + QSet items; private: - QHash indexById; - QHash indexByPublish; - IdManager idman; + QHash indexById; + QHash indexByPublish; + IdManager idman; public: - ~PublishItemList() - { - qDeleteAll(items); - } + ~PublishItemList() { qDeleteAll(items); } - int reserveId() - { - return idman.reserveId(); - } + int reserveId() { return idman.reserveId(); } void insert(PublishItem *item) { @@ -2077,35 +1846,23 @@ class PublishItemList indexById.remove(item->id); indexByPublish.remove(item->publish); items.remove(item); - if(item->id != -1) + if (item->id != -1) idman.releaseId(item->id); delete item; } - PublishItem *itemById(int id) const - { - return indexById.value(id); - } + PublishItem *itemById(int id) const { return indexById.value(id); } - PublishItem *itemByPublish(JDnsPublish *publish) const - { - return indexByPublish.value(publish); - } + PublishItem *itemByPublish(JDnsPublish *publish) const { return indexByPublish.value(publish); } }; -class PublishExtraItem -{ +class PublishExtraItem { public: - const int id; - JDnsPublishExtra * const publish; - ObjectSession *sess; + const int id; + JDnsPublishExtra *const publish; + ObjectSession *sess; - PublishExtraItem(int _id, JDnsPublishExtra *_publish) : - id(_id), - publish(_publish), - sess(0) - { - } + PublishExtraItem(int _id, JDnsPublishExtra *_publish) : id(_id), publish(_publish), sess(0) { } ~PublishExtraItem() { @@ -2114,21 +1871,17 @@ class PublishExtraItem } }; -class PublishExtraItemList -{ +class PublishExtraItemList { public: - QSet items; + QSet items; private: - QHash indexById; - QHash indexByPublish; - IdManager idman; + QHash indexById; + QHash indexByPublish; + IdManager idman; public: - ~PublishExtraItemList() - { - qDeleteAll(items); - } + ~PublishExtraItemList() { qDeleteAll(items); } void clear() { @@ -2139,10 +1892,7 @@ class PublishExtraItemList idman.clear(); } - int reserveId() - { - return idman.reserveId(); - } + int reserveId() { return idman.reserveId(); } void insert(PublishExtraItem *item) { @@ -2156,50 +1906,41 @@ class PublishExtraItemList indexById.remove(item->id); indexByPublish.remove(item->publish); items.remove(item); - if(item->id != -1) + if (item->id != -1) idman.releaseId(item->id); delete item; } - PublishExtraItem *itemById(int id) const - { - return indexById.value(id); - } + PublishExtraItem *itemById(int id) const { return indexById.value(id); } - PublishExtraItem *itemByPublish(JDnsPublishExtra *publish) const - { - return indexByPublish.value(publish); - } + PublishExtraItem *itemByPublish(JDnsPublishExtra *publish) const { return indexByPublish.value(publish); } }; -class JDnsServiceProvider : public ServiceProvider -{ +class JDnsServiceProvider : public ServiceProvider { Q_OBJECT public: JDnsGlobal *global; // browse - BrowseItemList browseItemList; - QHash items; + BrowseItemList browseItemList; + QHash items; // resolve ResolveItemList resolveItemList; // publish JDnsPublishAddresses *pub_addresses; - QByteArray localHost; - PublishItemList publishItemList; - PublishExtraItemList publishExtraItemList; + QByteArray localHost; + PublishItemList publishItemList; + PublishExtraItemList publishExtraItemList; static JDnsServiceProvider *create(JDnsGlobal *global, QObject *parent = 0) { return new JDnsServiceProvider(global, parent); } - JDnsServiceProvider(JDnsGlobal *_global, QObject *parent = 0) : - ServiceProvider(parent), - pub_addresses(0) + JDnsServiceProvider(JDnsGlobal *_global, QObject *parent = 0) : ServiceProvider(parent), pub_addresses(0) { global = _global; connect(global, SIGNAL(interfacesChanged()), SLOT(interfacesChanged())); @@ -2214,12 +1955,12 @@ class JDnsServiceProvider : public ServiceProvider virtual int browse_start(const QString &_type, const QString &_domain) { QString domain; - if(_domain.isEmpty() || _domain == ".") + if (_domain.isEmpty() || _domain == ".") domain = "local."; else domain = _domain; - if(domain[domain.length() - 1] != '.') + if (domain[domain.length() - 1] != '.') domain += '.'; Q_ASSERT(domain.length() >= 2 && domain[domain.length() - 1] == '.'); @@ -2227,34 +1968,31 @@ class JDnsServiceProvider : public ServiceProvider int id = browseItemList.reserveId(); // no support for non-local domains - if(domain != "local.") - { + if (domain != "local.") { BrowseItem *i = new BrowseItem(id, 0); - i->sess = new ObjectSession(this); + i->sess = new ObjectSession(this); browseItemList.insert(i); i->sess->defer(this, "do_browse_error", Q_ARG(int, i->id), - Q_ARG(XMPP::ServiceBrowser::Error, ServiceBrowser::ErrorNoWide)); + Q_ARG(XMPP::ServiceBrowser::Error, ServiceBrowser::ErrorNoWide)); return i->id; } - if(!global->ensure_mul()) - { + if (!global->ensure_mul()) { BrowseItem *i = new BrowseItem(id, 0); - i->sess = new ObjectSession(this); + i->sess = new ObjectSession(this); browseItemList.insert(i); i->sess->defer(this, "do_browse_error", Q_ARG(int, i->id), - Q_ARG(XMPP::ServiceBrowser::Error, ServiceBrowser::ErrorNoLocal)); + Q_ARG(XMPP::ServiceBrowser::Error, ServiceBrowser::ErrorNoLocal)); return i->id; } QByteArray type = _type.toUtf8(); - if(!validServiceType(type)) - { + if (!validServiceType(type)) { BrowseItem *i = new BrowseItem(id, 0); - i->sess = new ObjectSession(this); + i->sess = new ObjectSession(this); browseItemList.insert(i); i->sess->defer(this, "do_browse_error", Q_ARG(int, i->id), - Q_ARG(XMPP::ServiceBrowser::Error, ServiceBrowser::ErrorGeneric)); + Q_ARG(XMPP::ServiceBrowser::Error, ServiceBrowser::ErrorGeneric)); return i->id; } @@ -2278,13 +2016,12 @@ class JDnsServiceProvider : public ServiceProvider { int id = resolveItemList.reserveId(); - if(!global->ensure_mul()) - { + if (!global->ensure_mul()) { ResolveItem *i = new ResolveItem(id, 0); - i->sess = new ObjectSession(this); + i->sess = new ObjectSession(this); resolveItemList.insert(i); i->sess->defer(this, "do_resolve_error", Q_ARG(int, i->id), - Q_ARG(XMPP::ServiceResolver::Error, ServiceResolver::ErrorNoLocal)); + Q_ARG(XMPP::ServiceResolver::Error, ServiceResolver::ErrorNoLocal)); return i->id; } @@ -2304,34 +2041,32 @@ class JDnsServiceProvider : public ServiceProvider resolveItemList.remove(i); } - virtual int publish_start(const QString &instance, const QString &_type, int port, const QMap &attributes) + virtual int publish_start(const QString &instance, const QString &_type, int port, + const QMap &attributes) { int id = publishItemList.reserveId(); - if(!global->ensure_mul()) - { + if (!global->ensure_mul()) { PublishItem *i = new PublishItem(id, 0); - i->sess = new ObjectSession(this); + i->sess = new ObjectSession(this); publishItemList.insert(i); i->sess->defer(this, "do_publish_error", Q_ARG(int, i->id), - Q_ARG(XMPP::ServiceLocalPublisher::Error, ServiceLocalPublisher::ErrorNoLocal)); + Q_ARG(XMPP::ServiceLocalPublisher::Error, ServiceLocalPublisher::ErrorNoLocal)); return i->id; } QByteArray type = _type.toUtf8(); - if(!validServiceType(type)) - { + if (!validServiceType(type)) { PublishItem *i = new PublishItem(id, 0); - i->sess = new ObjectSession(this); + i->sess = new ObjectSession(this); publishItemList.insert(i); i->sess->defer(this, "do_publish_error", Q_ARG(int, i->id), - Q_ARG(XMPP::ServiceLocalPublisher::Error, ServiceLocalPublisher::ErrorGeneric)); + Q_ARG(XMPP::ServiceLocalPublisher::Error, ServiceLocalPublisher::ErrorGeneric)); return i->id; } // make sure A/AAAA records are published - if(!pub_addresses) - { + if (!pub_addresses) { pub_addresses = new JDnsPublishAddresses(global->mul, this); connect(pub_addresses, SIGNAL(hostName(QByteArray)), SLOT(pub_addresses_hostName(QByteArray))); pub_addresses->setUseIPv6(global->haveMulticast6()); @@ -2350,13 +2085,13 @@ class JDnsServiceProvider : public ServiceProvider return i->id; } - virtual void publish_update(int id, const QMap &attributes) + virtual void publish_update(int id, const QMap &attributes) { PublishItem *i = publishItemList.itemById(id); Q_ASSERT(i); // if we already have an error queued, do nothing - if(i->sess->isDeferred(this, "do_publish_error")) + if (i->sess->isDeferred(this, "do_publish_error")) return; i->publish->update(attributes); @@ -2379,22 +2114,21 @@ class JDnsServiceProvider : public ServiceProvider int id = publishItemList.reserveId(); QJDns::Record rec = exportJDNSRecord(name); - if(rec.type == -1) - { + if (rec.type == -1) { PublishExtraItem *i = new PublishExtraItem(id, 0); - i->sess = new ObjectSession(this); + i->sess = new ObjectSession(this); publishExtraItemList.insert(i); i->sess->defer(this, "do_publish_extra_error", Q_ARG(int, i->id), - Q_ARG(XMPP::ServiceLocalPublisher::Error, ServiceLocalPublisher::ErrorGeneric)); + Q_ARG(XMPP::ServiceLocalPublisher::Error, ServiceLocalPublisher::ErrorGeneric)); return i->id; } // fill in owner if necessary - if(rec.owner.isEmpty()) + if (rec.owner.isEmpty()) rec.owner = pi->publish->fullname; // fill in the ttl if necessary - if(rec.ttl == 0) + if (rec.ttl == 0) rec.ttl = 4500; PublishExtraItem *i = new PublishExtraItem(id, new JDnsPublishExtra(pi->publish)); @@ -2411,24 +2145,23 @@ class JDnsServiceProvider : public ServiceProvider Q_ASSERT(i); // if we already have an error queued, do nothing - if(i->sess->isDeferred(this, "do_publish_extra_error")) + if (i->sess->isDeferred(this, "do_publish_extra_error")) return; QJDns::Record rec = exportJDNSRecord(name); - if(rec.type == -1) - { + if (rec.type == -1) { i->sess = new ObjectSession(this); i->sess->defer(this, "do_publish_extra_error", Q_ARG(int, i->id), - Q_ARG(XMPP::ServiceLocalPublisher::Error, ServiceLocalPublisher::ErrorGeneric)); + Q_ARG(XMPP::ServiceLocalPublisher::Error, ServiceLocalPublisher::ErrorGeneric)); return; } // fill in owner if necessary - if(rec.owner.isEmpty()) - rec.owner = static_cast(i->publish->parent())->fullname; + if (rec.owner.isEmpty()) + rec.owner = static_cast(i->publish->parent())->fullname; // fill in the ttl if necessary - if(rec.ttl == 0) + if (rec.ttl == 0) rec.ttl = 4500; i->publish->update(rec); @@ -2447,22 +2180,20 @@ class JDnsServiceProvider : public ServiceProvider { // remove all extra publishes associated with this publish. // the association can be checked via QObject parenting. - QSet remove; - foreach(PublishExtraItem *i, publishExtraItemList.items) - { - if(static_cast(i->publish->parent()) == pi->publish) + QSet remove; + for (PublishExtraItem *i : publishExtraItemList.items) { + if (static_cast(i->publish->parent()) == pi->publish) remove += i; } - foreach(PublishExtraItem *i, remove) + for (PublishExtraItem *i : remove) publishExtraItemList.remove(i); } private slots: void interfacesChanged() { - if(pub_addresses) - { + if (pub_addresses) { pub_addresses->setUseIPv6(global->haveMulticast6()); pub_addresses->setUseIPv4(global->haveMulticast4()); } @@ -2471,11 +2202,12 @@ private slots: void jb_available(const QByteArray &instance) { JDnsBrowse *jb = static_cast(sender()); - BrowseItem *i = browseItemList.itemByBrowse(jb); + BrowseItem *i = browseItemList.itemByBrowse(jb); Q_ASSERT(i); - QByteArray name = instance + '.' + jb->typeAndDomain; - ServiceInstance si(QString::fromLatin1(instance), QString::fromLatin1(jb->type), "local.", QMap()); + QByteArray name = instance + '.' + jb->typeAndDomain; + ServiceInstance si(QString::fromLatin1(instance), QString::fromLatin1(jb->type), "local.", + QMap()); items.insert(name, si); emit browse_instanceAvailable(i->id, si); @@ -2484,7 +2216,7 @@ private slots: void jb_unavailable(const QByteArray &instance) { JDnsBrowse *jb = static_cast(sender()); - BrowseItem *i = browseItemList.itemByBrowse(jb); + BrowseItem *i = browseItemList.itemByBrowse(jb); Q_ASSERT(i); QByteArray name = instance + '.' + jb->typeAndDomain; @@ -2508,24 +2240,20 @@ private slots: void jr_finished() { JDnsServiceResolve *jr = static_cast(sender()); - ResolveItem *i = resolveItemList.itemByResolve(jr); + ResolveItem *i = resolveItemList.itemByResolve(jr); Q_ASSERT(i); // parse TXT list into attribute map - QMap attribs; - for(int n = 0; n < jr->attribs.count(); ++n) - { + QMap attribs; + for (int n = 0; n < jr->attribs.count(); ++n) { const QByteArray &a = jr->attribs[n]; - QString key; - QByteArray value; - int x = a.indexOf('='); - if(x != -1) - { - key = QString::fromLatin1(a.mid(0, x)); + QString key; + QByteArray value; + int x = a.indexOf('='); + if (x != -1) { + key = QString::fromLatin1(a.mid(0, x)); value = a.mid(x + 1); - } - else - { + } else { key = QString::fromLatin1(a); } @@ -2536,22 +2264,20 @@ private slots: Q_ASSERT(jr->have4 || jr->have6); QList results; - if(jr->have6) - { + if (jr->have6) { ResolveResult r; r.attributes = attribs; - r.address = jr->addr6; - r.port = jr->port; - r.hostName = jr->host; + r.address = jr->addr6; + r.port = jr->port; + r.hostName = jr->host; results += r; } - if(jr->have4) - { + if (jr->have4) { ResolveResult r; r.attributes = attribs; - r.address = jr->addr4; - r.port = jr->port; - r.hostName = jr->host; + r.address = jr->addr4; + r.port = jr->port; + r.hostName = jr->host; results += r; } @@ -2563,11 +2289,11 @@ private slots: void jr_error(QJDnsSharedRequest::Error e) { JDnsServiceResolve *jr = static_cast(sender()); - ResolveItem *i = resolveItemList.itemByResolve(jr); + ResolveItem *i = resolveItemList.itemByResolve(jr); Q_ASSERT(i); ServiceResolver::Error err; - if(e == QJDnsSharedRequest::ErrorTimeout) + if (e == QJDnsSharedRequest::ErrorTimeout) err = ServiceResolver::ErrorTimeout; else err = ServiceResolver::ErrorGeneric; @@ -2589,14 +2315,14 @@ private slots: void pub_addresses_hostName(const QByteArray &name) { // tell all active publishes about the change - foreach(PublishItem *item, publishItemList.items) + for (PublishItem *item : publishItemList.items) item->publish->hostChanged(name); } void jp_published() { JDnsPublish *jp = static_cast(sender()); - PublishItem *i = publishItemList.itemByPublish(jp); + PublishItem *i = publishItemList.itemByPublish(jp); Q_ASSERT(i); emit publish_published(i->id); @@ -2605,11 +2331,11 @@ private slots: void jp_error(QJDnsSharedRequest::Error e) { JDnsPublish *jp = static_cast(sender()); - PublishItem *i = publishItemList.itemByPublish(jp); + PublishItem *i = publishItemList.itemByPublish(jp); Q_ASSERT(i); ServiceLocalPublisher::Error err; - if(e == QJDnsSharedRequest::ErrorConflict) + if (e == QJDnsSharedRequest::ErrorConflict) err = ServiceLocalPublisher::ErrorConflict; else err = ServiceLocalPublisher::ErrorGeneric; @@ -2633,7 +2359,7 @@ private slots: void jpe_published() { JDnsPublishExtra *jp = static_cast(sender()); - PublishExtraItem *i = publishExtraItemList.itemByPublish(jp); + PublishExtraItem *i = publishExtraItemList.itemByPublish(jp); Q_ASSERT(i); emit publish_extra_published(i->id); @@ -2642,11 +2368,11 @@ private slots: void jpe_error(QJDnsSharedRequest::Error e) { JDnsPublishExtra *jp = static_cast(sender()); - PublishExtraItem *i = publishExtraItemList.itemByPublish(jp); + PublishExtraItem *i = publishExtraItemList.itemByPublish(jp); Q_ASSERT(i); ServiceLocalPublisher::Error err; - if(e == QJDnsSharedRequest::ErrorConflict) + if (e == QJDnsSharedRequest::ErrorConflict) err = ServiceLocalPublisher::ErrorConflict; else err = ServiceLocalPublisher::ErrorGeneric; @@ -2669,27 +2395,20 @@ private slots: //---------------------------------------------------------------------------- // JDnsProvider //---------------------------------------------------------------------------- -class JDnsProvider : public IrisNetProvider -{ +class JDnsProvider : public IrisNetProvider { Q_OBJECT Q_INTERFACES(XMPP::IrisNetProvider) public: JDnsGlobal *global; - JDnsProvider() - { - global = 0; - } + JDnsProvider() { global = 0; } - ~JDnsProvider() - { - delete global; - } + ~JDnsProvider() { delete global; } void ensure_global() { - if(!global) + if (!global) global = new JDnsGlobal; } @@ -2712,11 +2431,7 @@ class JDnsProvider : public IrisNetProvider } }; -IrisNetProvider *irisnet_createJDnsProvider() -{ - return new JDnsProvider; -} - -} +IrisNetProvider *irisnet_createJDnsProvider() { return new JDnsProvider; } +} // namespace XMPP #include "netnames_jdns.moc" diff --git a/src/irisnet/corelib/objectsession.cpp b/src/irisnet/corelib/objectsession.cpp index e3218e4e..ade6f717 100644 --- a/src/irisnet/corelib/objectsession.cpp +++ b/src/irisnet/corelib/objectsession.cpp @@ -18,102 +18,81 @@ #include "objectsession.h" -#include -#include #include +#include #include #include #include +#include namespace XMPP { - -class ObjectSessionWatcherPrivate -{ +class ObjectSessionWatcherPrivate { public: ObjectSession *sess; }; -class ObjectSessionPrivate : public QObject -{ +class ObjectSessionPrivate : public QObject { Q_OBJECT public: ObjectSession *q; - class MethodCall - { + class MethodCall { public: - QObject *obj; + QObject *obj; QByteArray method; - class Argument - { + class Argument { public: - int type; + int type; void *data; }; QList args; - MethodCall(QObject *_obj, const char *_method) : - obj(_obj), - method(_method) - { - } + MethodCall(QObject *_obj, const char *_method) : obj(_obj), method(_method) { } - ~MethodCall() - { - clearArgs(); - } + ~MethodCall() { clearArgs(); } void clearArgs() { - for(int n = 0; n < args.count(); ++n) + for (int n = 0; n < args.count(); ++n) +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) QMetaType::destroy(args[n].type, args[n].data); +#else + QMetaType(args[n].type).destroy(args[n].data); +#endif args.clear(); } - bool setArgs(QGenericArgument val0 = QGenericArgument(), - QGenericArgument val1 = QGenericArgument(), - QGenericArgument val2 = QGenericArgument(), - QGenericArgument val3 = QGenericArgument(), - QGenericArgument val4 = QGenericArgument(), - QGenericArgument val5 = QGenericArgument(), - QGenericArgument val6 = QGenericArgument(), - QGenericArgument val7 = QGenericArgument(), - QGenericArgument val8 = QGenericArgument(), - QGenericArgument val9 = QGenericArgument()) + bool setArgs(QGenericArgument val0 = QGenericArgument(), QGenericArgument val1 = QGenericArgument(), + QGenericArgument val2 = QGenericArgument(), QGenericArgument val3 = QGenericArgument(), + QGenericArgument val4 = QGenericArgument(), QGenericArgument val5 = QGenericArgument(), + QGenericArgument val6 = QGenericArgument(), QGenericArgument val7 = QGenericArgument(), + QGenericArgument val8 = QGenericArgument(), QGenericArgument val9 = QGenericArgument()) { - const char *arg_name[] = - { - val0.name(), val1.name(), val2.name(), - val3.name(), val4.name(), val5.name(), - val6.name(), val7.name(), val8.name(), - val9.name() - }; - - void *arg_data[] = - { - val0.data(), val1.data(), val2.data(), - val3.data(), val4.data(), val5.data(), - val6.data(), val7.data(), val8.data(), - val9.data() - }; + const char *arg_name[] = { val0.name(), val1.name(), val2.name(), val3.name(), val4.name(), + val5.name(), val6.name(), val7.name(), val8.name(), val9.name() }; + + void *arg_data[] = { val0.data(), val1.data(), val2.data(), val3.data(), val4.data(), + val5.data(), val6.data(), val7.data(), val8.data(), val9.data() }; clearArgs(); - for(int n = 0; n < 10; ++n) - { - if(arg_name[n] == 0) + for (int n = 0; n < 10; ++n) { + if (arg_name[n] == nullptr) break; Argument arg; +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) arg.type = QMetaType::type(arg_name[n]); - if(!arg.type) - { +#else + arg.type = QMetaType::fromName(arg_name[n]).id(); +#endif + if (!arg.type) { clearArgs(); return false; } - arg.data = QMetaType::create(arg.type, arg_data[n]); + arg.data = QMetaType(arg.type).create(arg_data[n]); args += arg; } @@ -121,15 +100,12 @@ class ObjectSessionPrivate : public QObject } }; - QList pendingCalls; - QTimer *callTrigger; - bool paused; - QList watchers; + QList pendingCalls; + QTimer *callTrigger; + bool paused; + QList watchers; - ObjectSessionPrivate(ObjectSession *_q) : - QObject(_q), - q(_q), - paused(false) + ObjectSessionPrivate(ObjectSession *_q) : QObject(_q), q(_q), paused(false) { callTrigger = new QTimer(this); connect(callTrigger, SIGNAL(timeout()), SLOT(doCall())); @@ -141,7 +117,7 @@ class ObjectSessionPrivate : public QObject invalidateWatchers(); callTrigger->disconnect(this); - callTrigger->setParent(0); + callTrigger->setParent(nullptr); callTrigger->deleteLater(); qDeleteAll(pendingCalls); pendingCalls.clear(); @@ -150,15 +126,14 @@ class ObjectSessionPrivate : public QObject void addPendingCall(MethodCall *call) { pendingCalls += call; - if(!paused && !callTrigger->isActive()) + if (!paused && !callTrigger->isActive()) callTrigger->start(); } bool havePendingCall(QObject *obj, const char *method) const { - foreach(const MethodCall *call, pendingCalls) - { - if(call->obj == obj && qstrcmp(call->method.data(), method) == 0) + for (const MethodCall *call : pendingCalls) { + if (call->obj == obj && qstrcmp(call->method.data(), method) == 0) return true; } return false; @@ -166,8 +141,8 @@ class ObjectSessionPrivate : public QObject void invalidateWatchers() { - for(int n = 0; n < watchers.count(); ++n) - watchers[n]->sess = 0; + for (int n = 0; n < watchers.count(); ++n) + watchers[n]->sess = nullptr; watchers.clear(); } @@ -175,22 +150,24 @@ private slots: void doCall() { MethodCall *call = pendingCalls.takeFirst(); - if(!pendingCalls.isEmpty()) + if (!pendingCalls.isEmpty()) callTrigger->start(); Q_ASSERT(call->args.count() <= 10); QGenericArgument arg[10]; - for(int n = 0; n < call->args.count(); ++n) - arg[n] = QGenericArgument(QMetaType::typeName(call->args[n].type), call->args[n].data); + for (int n = 0; n < call->args.count(); ++n) +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) + arg[n] = QGenericArgument(QMetaType::typeName(call->args[n].type), call->args[n].data); +#else + arg[n] = QGenericArgument(QMetaType(call->args[n].type).name(), call->args[n].data); +#endif bool ok; - ok = QMetaObject::invokeMethod(call->obj, call->method.data(), - Qt::DirectConnection, - arg[0], arg[1], arg[2], arg[3], arg[4], - arg[5], arg[6], arg[7], arg[8], arg[9]); + ok = QMetaObject::invokeMethod(call->obj, call->method.data(), Qt::DirectConnection, arg[0], arg[1], arg[2], + arg[3], arg[4], arg[5], arg[6], arg[7], arg[8], arg[9]); Q_ASSERT(ok); - if(!ok) + if (!ok) abort(); delete call; @@ -199,83 +176,51 @@ private slots: ObjectSessionWatcher::ObjectSessionWatcher(ObjectSession *sess) { - d = new ObjectSessionWatcherPrivate; + d = new ObjectSessionWatcherPrivate; d->sess = sess; - if(d->sess) + if (d->sess) d->sess->d->watchers += d; } ObjectSessionWatcher::~ObjectSessionWatcher() { - if(d->sess) + if (d->sess) d->sess->d->watchers.removeAll(d); delete d; } -bool ObjectSessionWatcher::isValid() const -{ - if(d->sess) - return true; - else - return false; -} +bool ObjectSessionWatcher::isValid() const { return d->sess != nullptr; } +ObjectSession::ObjectSession(QObject *parent) : QObject(parent) { d = new ObjectSessionPrivate(this); } -ObjectSession::ObjectSession(QObject *parent) : - QObject(parent) -{ - d = new ObjectSessionPrivate(this); -} - -ObjectSession::~ObjectSession() -{ - delete d; -} +ObjectSession::~ObjectSession() { delete d; } void ObjectSession::reset() { d->invalidateWatchers(); - if(d->callTrigger->isActive()) + if (d->callTrigger->isActive()) d->callTrigger->stop(); qDeleteAll(d->pendingCalls); d->pendingCalls.clear(); } -bool ObjectSession::isDeferred(QObject *obj, const char *method) -{ - return d->havePendingCall(obj, method); -} +bool ObjectSession::isDeferred(QObject *obj, const char *method) { return d->havePendingCall(obj, method); } -void ObjectSession::defer(QObject *obj, const char *method, - QGenericArgument val0, - QGenericArgument val1, - QGenericArgument val2, - QGenericArgument val3, - QGenericArgument val4, - QGenericArgument val5, - QGenericArgument val6, - QGenericArgument val7, - QGenericArgument val8, - QGenericArgument val9) +void ObjectSession::defer(QObject *obj, const char *method, QGenericArgument val0, QGenericArgument val1, + QGenericArgument val2, QGenericArgument val3, QGenericArgument val4, QGenericArgument val5, + QGenericArgument val6, QGenericArgument val7, QGenericArgument val8, QGenericArgument val9) { ObjectSessionPrivate::MethodCall *call = new ObjectSessionPrivate::MethodCall(obj, method); call->setArgs(val0, val1, val2, val3, val4, val5, val6, val7, val8, val9); d->addPendingCall(call); } -void ObjectSession::deferExclusive(QObject *obj, const char *method, - QGenericArgument val0, - QGenericArgument val1, - QGenericArgument val2, - QGenericArgument val3, - QGenericArgument val4, - QGenericArgument val5, - QGenericArgument val6, - QGenericArgument val7, - QGenericArgument val8, - QGenericArgument val9) +void ObjectSession::deferExclusive(QObject *obj, const char *method, QGenericArgument val0, QGenericArgument val1, + QGenericArgument val2, QGenericArgument val3, QGenericArgument val4, + QGenericArgument val5, QGenericArgument val6, QGenericArgument val7, + QGenericArgument val8, QGenericArgument val9) { - if(d->havePendingCall(obj, method)) + if (d->havePendingCall(obj, method)) return; ObjectSessionPrivate::MethodCall *call = new ObjectSessionPrivate::MethodCall(obj, method); @@ -287,7 +232,7 @@ void ObjectSession::pause() { Q_ASSERT(!d->paused); - if(d->callTrigger->isActive()) + if (d->callTrigger->isActive()) d->callTrigger->stop(); d->paused = true; } @@ -297,10 +242,9 @@ void ObjectSession::resume() Q_ASSERT(d->paused); d->paused = false; - if(!d->pendingCalls.isEmpty()) + if (!d->pendingCalls.isEmpty()) d->callTrigger->start(); } - -} +} // namespace XMPP #include "objectsession.moc" diff --git a/src/irisnet/corelib/objectsession.h b/src/irisnet/corelib/objectsession.h index bcae574b..b68d06d6 100644 --- a/src/irisnet/corelib/objectsession.h +++ b/src/irisnet/corelib/objectsession.h @@ -22,44 +22,32 @@ #include namespace XMPP { - class ObjectSessionPrivate; class ObjectSessionWatcherPrivate; -class ObjectSession : public QObject -{ +class ObjectSession : public QObject { Q_OBJECT public: - ObjectSession(QObject *parent = 0); + ObjectSession(QObject *parent = nullptr); ~ObjectSession(); // clear all deferred requests, invalidate watchers void reset(); bool isDeferred(QObject *obj, const char *method); - void defer(QObject *obj, const char *method, - QGenericArgument val0 = QGenericArgument(), - QGenericArgument val1 = QGenericArgument(), - QGenericArgument val2 = QGenericArgument(), - QGenericArgument val3 = QGenericArgument(), - QGenericArgument val4 = QGenericArgument(), - QGenericArgument val5 = QGenericArgument(), - QGenericArgument val6 = QGenericArgument(), - QGenericArgument val7 = QGenericArgument(), - QGenericArgument val8 = QGenericArgument(), - QGenericArgument val9 = QGenericArgument()); - void deferExclusive(QObject *obj, const char *method, - QGenericArgument val0 = QGenericArgument(), - QGenericArgument val1 = QGenericArgument(), - QGenericArgument val2 = QGenericArgument(), - QGenericArgument val3 = QGenericArgument(), - QGenericArgument val4 = QGenericArgument(), - QGenericArgument val5 = QGenericArgument(), - QGenericArgument val6 = QGenericArgument(), - QGenericArgument val7 = QGenericArgument(), - QGenericArgument val8 = QGenericArgument(), - QGenericArgument val9 = QGenericArgument()); + void defer(QObject *obj, const char *method, QGenericArgument val0 = QGenericArgument(), + QGenericArgument val1 = QGenericArgument(), QGenericArgument val2 = QGenericArgument(), + QGenericArgument val3 = QGenericArgument(), QGenericArgument val4 = QGenericArgument(), + QGenericArgument val5 = QGenericArgument(), QGenericArgument val6 = QGenericArgument(), + QGenericArgument val7 = QGenericArgument(), QGenericArgument val8 = QGenericArgument(), + QGenericArgument val9 = QGenericArgument()); + void deferExclusive(QObject *obj, const char *method, QGenericArgument val0 = QGenericArgument(), + QGenericArgument val1 = QGenericArgument(), QGenericArgument val2 = QGenericArgument(), + QGenericArgument val3 = QGenericArgument(), QGenericArgument val4 = QGenericArgument(), + QGenericArgument val5 = QGenericArgument(), QGenericArgument val6 = QGenericArgument(), + QGenericArgument val7 = QGenericArgument(), QGenericArgument val8 = QGenericArgument(), + QGenericArgument val9 = QGenericArgument()); void pause(); void resume(); @@ -69,8 +57,7 @@ class ObjectSession : public QObject ObjectSessionPrivate *d; }; -class ObjectSessionWatcher -{ +class ObjectSessionWatcher { public: ObjectSessionWatcher(ObjectSession *sess); ~ObjectSessionWatcher(); @@ -81,7 +68,6 @@ class ObjectSessionWatcher friend class ObjectSessionPrivate; ObjectSessionWatcherPrivate *d; }; +} // namespace XMPP -} - -#endif +#endif // OBJECTSESSION_H diff --git a/src/irisnet/irisnet.pro b/src/irisnet/irisnet.pro deleted file mode 100644 index 092e139d..00000000 --- a/src/irisnet/irisnet.pro +++ /dev/null @@ -1,15 +0,0 @@ -TEMPLATE = subdirs - -include(../libbase.pri) - -sub_corelib.subdir = corelib -sub_appledns.subdir = appledns -sub_appledns.depends = sub_corelib -sub_noncore.subdir = noncore -!irisnetcore_bundle:sub_noncore.depends = sub_corelib - -!irisnetcore_bundle:SUBDIRS += sub_corelib -appledns:!appledns_bundle:SUBDIRS += sub_appledns -!iris_bundle:SUBDIRS += sub_noncore - -OTHER_FILES += CMakeLists.txt diff --git a/src/irisnet/noncore/cutestuff/bsocket.cpp b/src/irisnet/noncore/cutestuff/bsocket.cpp index d3a366ce..3f677077 100644 --- a/src/irisnet/noncore/cutestuff/bsocket.cpp +++ b/src/irisnet/noncore/cutestuff/bsocket.cpp @@ -18,39 +18,37 @@ * */ -#include +#include "bsocket.h" + +#include "netnames.h" + #include #include +#include +#include #include -#include "bsocket.h" - -//#define BS_DEBUG - +// #define BS_DEBUG #ifdef BS_DEBUG -# define BSDEBUG (qDebug() << this << "#" << __FUNCTION__ << ":") +#define BSDEBUG (qDebug() << this << "#" << __FUNCTION__ << ":") #endif - #define READBUFSIZE 65536 // CS_NAMESPACE_BEGIN - -class QTcpSocketSignalRelay : public QObject -{ +class QTcpSocketSignalRelay : public QObject { Q_OBJECT public: - QTcpSocketSignalRelay(QTcpSocket *sock, QObject *parent = 0) - :QObject(parent) + QTcpSocketSignalRelay(QTcpSocket *sock, QObject *parent = nullptr) : QObject(parent) { qRegisterMetaType("QAbstractSocket::SocketError"); - connect(sock, SIGNAL(hostFound()), SLOT(sock_hostFound()), Qt::QueuedConnection); - connect(sock, SIGNAL(connected()), SLOT(sock_connected()), Qt::QueuedConnection); - connect(sock, SIGNAL(disconnected()), SLOT(sock_disconnected()), Qt::QueuedConnection); - connect(sock, SIGNAL(readyRead()), SLOT(sock_readyRead()), Qt::QueuedConnection); - connect(sock, SIGNAL(bytesWritten(qint64)), SLOT(sock_bytesWritten(qint64)), Qt::QueuedConnection); - connect(sock, SIGNAL(error(QAbstractSocket::SocketError)), SLOT(sock_error(QAbstractSocket::SocketError)), Qt::QueuedConnection); + connect(sock, &QTcpSocket::hostFound, this, &QTcpSocketSignalRelay::sock_hostFound, Qt::QueuedConnection); + connect(sock, &QTcpSocket::connected, this, &QTcpSocketSignalRelay::sock_connected, Qt::QueuedConnection); + connect(sock, &QTcpSocket::disconnected, this, &QTcpSocketSignalRelay::sock_disconnected, Qt::QueuedConnection); + connect(sock, &QTcpSocket::readyRead, this, &QTcpSocketSignalRelay::sock_readyRead, Qt::QueuedConnection); + connect(sock, &QTcpSocket::bytesWritten, this, &QTcpSocketSignalRelay::sock_bytesWritten, Qt::QueuedConnection); + connect(sock, &QTcpSocket::errorOccurred, this, &QTcpSocketSignalRelay::sock_error, Qt::QueuedConnection); } signals: @@ -62,90 +60,64 @@ class QTcpSocketSignalRelay : public QObject void error(QAbstractSocket::SocketError); public slots: - void sock_hostFound() - { - emit hostFound(); - } + void sock_hostFound() { emit hostFound(); } - void sock_connected() - { - emit connected(); - } + void sock_connected() { emit connected(); } - void sock_disconnected() - { - emit disconnected(); - } + void sock_disconnected() { emit disconnected(); } - void sock_readyRead() - { - emit readyRead(); - } + void sock_readyRead() { emit readyRead(); } - void sock_bytesWritten(qint64 x) - { - emit bytesWritten(x); - } + void sock_bytesWritten(qint64 x) { emit bytesWritten(x); } - void sock_error(QAbstractSocket::SocketError x) - { - emit error(x); - } + void sock_error(QAbstractSocket::SocketError x) { emit error(x); } }; - -class HappyEyeballsConnector : public QObject -{ +class HappyEyeballsConnector : public QObject { Q_OBJECT public: - enum State { - Failure, - Created, - Resolve, - Connecting, - Connected - }; + enum State { Failure, Created, Resolve, Connecting, Connected }; struct SockData { - QTcpSocket *sock; + QTcpSocket *sock; QTcpSocketSignalRelay *relay; - State state; + State state; + QString hostname; // last resolved name XMPP::ServiceResolver *resolver; }; /*! source data */ - QString service; - QString transport; - QString domain; - quint16 port = 0; - QHostAddress address; + QString service; + QString transport; + QString domain; + quint16 port = 0; + QHostAddress address; QAbstractSocket::NetworkLayerProtocol fallbackProtocol = QAbstractSocket::IPv4Protocol; /*! runtime data */ - QString lastError; - int lastIndex; + QString lastError; + int lastIndex; QList sockets; - QTimer fallbackTimer; + QTimer fallbackTimer; - HappyEyeballsConnector(QObject *parent) : - QObject(parent) + HappyEyeballsConnector(QObject *parent) : QObject(parent) { fallbackTimer.setSingleShot(true); fallbackTimer.setInterval(250); /* rfc recommends 150-250ms */ connect(&fallbackTimer, SIGNAL(timeout()), SLOT(startFallback())); } - SockData& addSocket() + SockData &addSocket() { SockData sd; sd.state = Created; - sd.sock = new QTcpSocket(this); + sd.sock = new QTcpSocket(this); sd.sock->setProxy(QNetworkProxy::NoProxy); sd.sock->setReadBufferSize(READBUFSIZE); - sd.relay = new QTcpSocketSignalRelay(sd.sock, this); - sd.resolver = 0; - connect(sd.relay, SIGNAL(connected()), SLOT(qs_connected())); - connect(sd.relay, SIGNAL(error(QAbstractSocket::SocketError)), SLOT(qs_error(QAbstractSocket::SocketError))); + sd.relay = new QTcpSocketSignalRelay(sd.sock, this); + sd.resolver = nullptr; + connect(sd.relay, &QTcpSocketSignalRelay::connected, this, &HappyEyeballsConnector::qs_connected); + connect(sd.relay, &QTcpSocketSignalRelay::error, this, &HappyEyeballsConnector::qs_error); sockets.append(sd); return sockets[sockets.count() - 1]; } @@ -158,15 +130,14 @@ class HappyEyeballsConnector : public QObject fallbackTimer.stop(); } - void connectToHost(const QHostAddress &address, quint16 port) { #ifdef BS_DEBUG BSDEBUG << "a:" << address << "p:" << port; #endif this->address = address; - SockData &sd = addSocket(); - sd.state = Connecting; + SockData &sd = addSocket(); + sd.state = Connecting; sd.sock->connectToHost(address, port); } @@ -177,16 +148,19 @@ class HappyEyeballsConnector : public QObject BSDEBUG << "h:" << host << "p:" << port << "pr:" << protocol; #endif this->domain = host; - this->port = port; + this->port = port; SockData &sd = addSocket(); QHostAddress addr(host); if (addr.isNull()) { sd.resolver = new XMPP::ServiceResolver; initResolver(sd.resolver); - sd.resolver->setProtocol(protocol == QAbstractSocket::UnknownNetworkLayerProtocol? - (fallbackProtocol == QAbstractSocket::IPv4Protocol? XMPP::ServiceResolver::IPv6 : XMPP::ServiceResolver::IPv4) : - (protocol== QAbstractSocket::IPv4Protocol? XMPP::ServiceResolver::IPv4 : XMPP::ServiceResolver::IPv6)); + sd.resolver->setProtocol(protocol == QAbstractSocket::UnknownNetworkLayerProtocol + ? (fallbackProtocol == QAbstractSocket::IPv4Protocol + ? XMPP::ServiceResolver::IPv6 + : XMPP::ServiceResolver::IPv4) + : (protocol == QAbstractSocket::IPv4Protocol ? XMPP::ServiceResolver::IPv4 + : XMPP::ServiceResolver::IPv6)); if (protocol == QAbstractSocket::UnknownNetworkLayerProtocol) { addSocket(); fallbackTimer.start(); @@ -196,7 +170,7 @@ class HappyEyeballsConnector : public QObject } else { // connecting by IP. lastIndex = sockets.count() - 1; - sd.state = Connecting; + sd.state = Connecting; sd.sock->connectToHost(addr, port); } } @@ -206,12 +180,12 @@ class HappyEyeballsConnector : public QObject #ifdef BS_DEBUG BSDEBUG << "s:" << service << "t:" << transport << "d:" << domain; #endif - this->service = service; + this->service = service; this->transport = transport; - this->domain = domain; - this->port = port; - SockData &sd = addSocket(); - sd.resolver = new XMPP::ServiceResolver(this); + this->domain = domain; + this->port = port; + SockData &sd = addSocket(); + sd.resolver = new XMPP::ServiceResolver(this); sd.resolver->setProtocol(XMPP::ServiceResolver::HappyEyeballs); connect(sd.resolver, SIGNAL(srvReady()), SLOT(splitSrvResolvers())); // we don't care about special handling of fail. we have fallback host there anyway @@ -223,11 +197,12 @@ class HappyEyeballsConnector : public QObject SockData takeCurrent(QObject *parent) { SockData csd = sockets.takeAt(lastIndex); - lastIndex = -1; + lastIndex = -1; disconnect(csd.relay); csd.relay->setParent(parent); csd.sock->setParent(parent); - delete csd.resolver; // FIME ensure it's accessible only from connected signal. we don't delete resolver from its slot + delete csd + .resolver; // FIME ensure it's accessible only from connected signal. we don't delete resolver from its slot csd.resolver = nullptr; return csd; } @@ -251,8 +226,8 @@ class HappyEyeballsConnector : public QObject void initResolver(XMPP::ServiceResolver *resolver) { resolver->setParent(this); - connect(resolver, SIGNAL(resultReady(QHostAddress,quint16)), this, SLOT(handleDnsReady(QHostAddress,quint16))); - connect(resolver, SIGNAL(error(XMPP::ServiceResolver::Error)), this, SLOT(handleDnsError(XMPP::ServiceResolver::Error))); + connect(resolver, &XMPP::ServiceResolver::resultReady, this, &HappyEyeballsConnector::handleDnsReady); + connect(resolver, &XMPP::ServiceResolver::error, this, &HappyEyeballsConnector::handleDnsError); } void setCurrentByResolver(XMPP::ServiceResolver *resolver) @@ -286,7 +261,8 @@ private slots: #ifdef BS_DEBUG BSDEBUG; #endif - setCurrentByRelay(static_cast(sender())); + QPointer valid(this); + setCurrentByRelay(static_cast(sender())); for (int i = 0; i < sockets.count(); i++) { if (i != lastIndex) { abortSocket(sockets[i]); @@ -295,12 +271,14 @@ private slots: sockets[i].state = Connected; } emit connected(); + if (!valid) + return; } } void qs_error(QAbstractSocket::SocketError errorCode) { - setCurrentByRelay(static_cast(sender())); + setCurrentByRelay(static_cast(sender())); // TODO remember error code lastError = sockets[lastIndex].sock->errorString(); #ifdef BS_DEBUG @@ -321,10 +299,10 @@ private slots: #ifdef BS_DEBUG BSDEBUG << "splitting resolvers"; #endif - setCurrentByResolver(static_cast(sender())); - SockData &sdv4 = sockets[lastIndex]; - SockData &sdv6 = addSocket(); - XMPP::ServiceResolver::ProtoSplit ps = sdv4.resolver->happySplit(); + setCurrentByResolver(static_cast(sender())); + SockData &sdv4 = sockets[lastIndex]; + SockData &sdv6 = addSocket(); + XMPP::ServiceResolver::ProtoSplit ps = sdv4.resolver->happySplit(); initResolver(ps.ipv4); initResolver(ps.ipv6); @@ -332,7 +310,7 @@ private slots: sdv4.resolver->deleteLater(); sdv4.resolver = ps.ipv4; - sdv4.state = Created; + sdv4.state = Created; sdv6.resolver = ps.ipv6; if (fallbackProtocol == QAbstractSocket::IPv4Protocol) { @@ -346,18 +324,20 @@ private slots: } /* host resolved, now try to connect to it */ - void handleDnsReady(const QHostAddress &address, quint16 port) + void handleDnsReady(const QHostAddress &address, quint16 port, const QString &hostname) { #ifdef BS_DEBUG BSDEBUG << "a:" << address << "p:" << port; #endif - setCurrentByResolver(static_cast(sender())); - sockets[lastIndex].state = Connecting; + setCurrentByResolver(static_cast(sender())); + sockets[lastIndex].state = Connecting; + sockets[lastIndex].hostname = hostname; sockets[lastIndex].sock->connectToHost(address, port); } /* resolver failed the dns lookup */ - void handleDnsError(XMPP::ServiceResolver::Error e) { + void handleDnsError(XMPP::ServiceResolver::Error e) + { #ifdef BS_DEBUG BSDEBUG << "e:" << e; #else @@ -373,7 +353,7 @@ private slots: #ifdef BS_DEBUG BSDEBUG; #endif - for(int i = 0; i < sockets.count(); i++) { + for (int i = 0; i < sockets.count(); i++) { SockData &sd = sockets[i]; if (sd.state == Created) { sd.state = Resolve; @@ -382,8 +362,9 @@ private slots: } else { sd.resolver = new XMPP::ServiceResolver; initResolver(sd.resolver); - sd.resolver->setProtocol(fallbackProtocol == QAbstractSocket::IPv4Protocol? - XMPP::ServiceResolver::IPv4 : XMPP::ServiceResolver::IPv6); + sd.resolver->setProtocol(fallbackProtocol == QAbstractSocket::IPv4Protocol + ? XMPP::ServiceResolver::IPv4 + : XMPP::ServiceResolver::IPv6); sd.resolver->start(domain, port); } } @@ -395,29 +376,27 @@ private slots: void error(QAbstractSocket::SocketError); }; -class BSocket::Private -{ +class BSocket::Private { public: Private() { - qsock = nullptr; + qsock = nullptr; qsock_relay = nullptr; } - QTcpSocket *qsock; + QTcpSocket *qsock; QTcpSocketSignalRelay *qsock_relay; - int state; + int state; - QString domain; //!< Domain we are currently connected to - QString host; //!< Hostname we are currently connected to + QString domain; //!< Domain we are currently connected to + QString host; //!< Hostname we are currently connected to QHostAddress address; //!< IP address we are currently connected to - quint16 port; //!< Port we are currently connected to + quint16 port; //!< Port we are currently connected to QPointer connector; }; -BSocket::BSocket(QObject *parent) -:ByteStream(parent) +BSocket::BSocket(QObject *parent) : ByteStream(parent) { d = new Private; resetConnection(); @@ -429,23 +408,25 @@ BSocket::~BSocket() delete d; } - void BSocket::resetConnection(bool clear) { #ifdef BS_DEBUG BSDEBUG << clear; #endif if (d->connector) { - delete d->connector; // fixme: deleteLater? + d->connector->deleteLater(); + disconnect(d->connector); + d->connector = nullptr; } - if(d->qsock) { + if (d->qsock) { delete d->qsock_relay; d->qsock_relay = nullptr; // move remaining into the local queue if (d->qsock->isOpen()) { - QByteArray block(int(d->qsock->bytesAvailable()), 0); // memory won't never be cheap enough to have gigabytes for socket buffer + QByteArray block(int(d->qsock->bytesAvailable()), + 0); // memory won't never be cheap enough to have gigabytes for socket buffer if (block.size()) { d->qsock->read(block.data(), block.size()); appendRead(block); @@ -453,29 +434,28 @@ void BSocket::resetConnection(bool clear) d->qsock->close(); } - //d->sd.deleteLater(d->qsock); + // d->sd.deleteLater(d->qsock); d->qsock->deleteLater(); d->qsock = nullptr; - } - else { - if(clear) + } else { + if (clear) clearReadBuffer(); } - d->state = Idle; - d->domain = ""; - d->host = ""; + d->state = Idle; + d->domain = ""; + d->host = ""; d->address = QHostAddress(); - d->port = 0; + d->port = 0; setOpenMode(QIODevice::NotOpen); } void BSocket::ensureConnector() { - if(!d->connector) { + if (!d->connector) { d->connector = new HappyEyeballsConnector(this); - connect(d->connector, SIGNAL(connected()), SLOT(qs_connected())); - connect(d->connector, SIGNAL(error(QAbstractSocket::SocketError)), SLOT(qs_error(QAbstractSocket::SocketError))); + connect(d->connector, &HappyEyeballsConnector::connected, this, &BSocket::qs_connected); + connect(d->connector, &HappyEyeballsConnector::error, this, &BSocket::qs_error); } } @@ -484,8 +464,8 @@ void BSocket::connectToHost(const QHostAddress &address, quint16 port) { resetConnection(true); d->address = address; - d->port = port; - d->state = Connecting; + d->port = port; + d->state = Connecting; ensureConnector(); d->connector->connectToHost(address, port); @@ -495,8 +475,8 @@ void BSocket::connectToHost(const QHostAddress &address, quint16 port) void BSocket::connectToHost(const QString &host, quint16 port, QAbstractSocket::NetworkLayerProtocol protocol) { resetConnection(true); - d->host = host; - d->port = port; + d->host = host; + d->port = port; d->state = Connecting; ensureConnector(); @@ -508,20 +488,17 @@ void BSocket::connectToHost(const QString &service, const QString &transport, co { resetConnection(true); d->domain = domain; - d->state = Connecting; + d->state = Connecting; ensureConnector(); d->connector->connectToHost(service, transport, domain, port); } -QAbstractSocket* BSocket::abstractSocket() const -{ - return d->qsock; -} +QAbstractSocket *BSocket::abstractSocket() const { return d->qsock; } qintptr BSocket::socket() const { - if(d->qsock) + if (d->qsock) return d->qsock->socketDescriptor(); else return -1; @@ -531,19 +508,18 @@ void BSocket::setSocket(QTcpSocket *s) { resetConnection(true); s->setParent(this); - d->qsock = s; + d->qsock = s; d->qsock_relay = new QTcpSocketSignalRelay(d->qsock, this); qs_connected_step2(false); // we have desriptor already. so it's already known to be connected } -int BSocket::state() const -{ - return d->state; -} +int BSocket::state() const { return d->state; } + +const QString &BSocket::host() const { return d->host; } bool BSocket::isOpen() const { - if(d->state == Connected) + if (d->state == Connected) return true; else return false; @@ -551,10 +527,10 @@ bool BSocket::isOpen() const void BSocket::close() { - if(d->state == Idle) + if (d->state == Idle) return; - if(d->qsock) { + if (d->qsock) { d->state = Closing; d->qsock->close(); if (d->qsock->state() == QAbstractSocket::ClosingState) { @@ -562,15 +538,14 @@ void BSocket::close() } else { resetConnection(); } - } - else { + } else { resetConnection(); } } qint64 BSocket::writeData(const char *data, qint64 maxSize) { - if(d->state != Connected) + if (d->state != Connected) return 0; #ifdef BS_DEBUG_EXTRA BSDEBUG << "- [" << maxSize << "]: {" << QByteArray::fromRawData(data, maxSize) << "}"; @@ -580,13 +555,13 @@ qint64 BSocket::writeData(const char *data, qint64 maxSize) qint64 BSocket::readData(char *data, qint64 maxSize) { - if(!maxSize) { + if (!maxSize) { return 0; } qint64 readSize; - if(d->qsock) { + if (d->qsock) { qint64 max = bytesAvailable(); - if(maxSize <= 0 || maxSize > max) { + if (maxSize <= 0 || maxSize > max) { maxSize = max; } readSize = d->qsock->read(data, maxSize); @@ -602,7 +577,7 @@ qint64 BSocket::readData(char *data, qint64 maxSize) qint64 BSocket::bytesAvailable() const { - if(d->qsock) + if (d->qsock) return d->qsock->bytesAvailable(); else return ByteStream::bytesAvailable(); @@ -610,14 +585,14 @@ qint64 BSocket::bytesAvailable() const qint64 BSocket::bytesToWrite() const { - if(!d->qsock) + if (!d->qsock) return 0; return d->qsock->bytesToWrite(); } QHostAddress BSocket::address() const { - if(d->qsock) + if (d->qsock) return d->qsock->localAddress(); else return QHostAddress(); @@ -625,7 +600,7 @@ QHostAddress BSocket::address() const quint16 BSocket::port() const { - if(d->qsock) + if (d->qsock) return d->qsock->localPort(); else return 0; @@ -633,7 +608,7 @@ quint16 BSocket::port() const QHostAddress BSocket::peerAddress() const { - if(d->qsock) + if (d->qsock) return d->qsock->peerAddress(); else return QHostAddress(); @@ -641,7 +616,7 @@ QHostAddress BSocket::peerAddress() const quint16 BSocket::peerPort() const { - if(d->qsock) + if (d->qsock) return d->qsock->peerPort(); else return 0; @@ -650,8 +625,9 @@ quint16 BSocket::peerPort() const void BSocket::qs_connected() { HappyEyeballsConnector::SockData sd = d->connector->takeCurrent(this); - d->qsock = sd.sock; - d->qsock_relay = sd.relay; + d->qsock = sd.sock; + d->qsock_relay = sd.relay; + d->host = sd.hostname; d->connector->deleteLater(); qs_connected_step2(true); } @@ -668,19 +644,19 @@ void BSocket::qs_connected_step2(bool signalConnected) #ifdef BS_DEBUG BSDEBUG << "Connected"; #endif + QPointer valid(this); if (signalConnected) { emit connected(); } - if (d->qsock->bytesAvailable()) { + if (valid && d->qsock->bytesAvailable()) { qs_readyRead(); } } void BSocket::qs_closed() { - if(d->state == Closing) - { + if (d->state == Closing) { #ifdef BS_DEBUG BSDEBUG << "Delayed Close Finished"; #endif @@ -689,23 +665,19 @@ void BSocket::qs_closed() } } -void BSocket::qs_readyRead() -{ - emit readyRead(); -} +void BSocket::qs_readyRead() { emit readyRead(); } void BSocket::qs_bytesWritten(qint64 x64) { - int x = x64; #ifdef BS_DEBUG_EXTRA - BSDEBUG << "BytesWritten [" << x << "]"; + BSDEBUG << "BytesWritten [" << x64 << "]"; #endif - emit bytesWritten(x); + emit bytesWritten(x64); } void BSocket::qs_error(QAbstractSocket::SocketError x) { - if(x == QTcpSocket::RemoteHostClosedError) { + if (x == QTcpSocket::RemoteHostClosedError) { #ifdef BS_DEBUG BSDEBUG << "Connection Closed"; #endif @@ -718,9 +690,9 @@ void BSocket::qs_error(QAbstractSocket::SocketError x) BSDEBUG << "Error"; #endif resetConnection(); - if(x == QTcpSocket::ConnectionRefusedError) + if (x == QTcpSocket::ConnectionRefusedError) emit error(ErrConnectionRefused); - else if(x == QTcpSocket::HostNotFoundError) + else if (x == QTcpSocket::HostNotFoundError) emit error(ErrHostNotFound); else emit error(ErrRead); diff --git a/src/irisnet/noncore/cutestuff/bsocket.h b/src/irisnet/noncore/cutestuff/bsocket.h index 1a5d08ad..17498585 100644 --- a/src/irisnet/noncore/cutestuff/bsocket.h +++ b/src/irisnet/noncore/cutestuff/bsocket.h @@ -21,41 +21,41 @@ #ifndef CS_BSOCKET_H #define CS_BSOCKET_H +#include "bytestream.h" + #include #include -#include "bytestream.h" -#include "netnames.h" - -class QString; -class QObject; class QByteArray; +class QObject; +class QString; +class QTcpSocket; // CS_NAMESPACE_BEGIN - - /*! Socket with automatic hostname lookups, using SRV, AAAA and A DNS queries. */ -class BSocket : public ByteStream -{ +class BSocket : public ByteStream { Q_OBJECT public: enum Error { ErrConnectionRefused = ErrCustom, ErrHostNotFound }; enum State { Idle, HostLookup, Connecting, Connected, Closing }; - BSocket(QObject *parent=0); + BSocket(QObject *parent = nullptr); ~BSocket(); /*! Connect to an already resolved host */ void connectToHost(const QHostAddress &address, quint16 port); /*! Connect to a host via the specified protocol, or the default protocols if not specified */ - void connectToHost(const QString &host, quint16 port, QAbstractSocket::NetworkLayerProtocol protocol = QAbstractSocket::UnknownNetworkLayerProtocol); + void connectToHost(const QString &host, quint16 port, + QAbstractSocket::NetworkLayerProtocol protocol = QAbstractSocket::UnknownNetworkLayerProtocol); /*! Connect to the hosts for the specified service */ - void connectToHost(const QString &service, const QString &transport, const QString &domain, quint16 port = std::numeric_limits::max()); - virtual QAbstractSocket* abstractSocket() const; - qintptr socket() const; - void setSocket(QTcpSocket *); - int state() const; + void connectToHost(const QString &service, const QString &transport, const QString &domain, + quint16 port = std::numeric_limits::max()); + virtual QAbstractSocket *abstractSocket() const; + qintptr socket() const; + void setSocket(QTcpSocket *); + int state() const; + const QString &host() const; // from ByteStream bool isOpen() const; @@ -66,11 +66,11 @@ class BSocket : public ByteStream // local QHostAddress address() const; - quint16 port() const; + quint16 port() const; // remote QHostAddress peerAddress() const; - quint16 peerPort() const; + quint16 peerPort() const; protected: qint64 writeData(const char *data, qint64 maxSize); @@ -91,15 +91,13 @@ private slots: class Private; Private *d; - void resetConnection(bool clear=false); + void resetConnection(bool clear = false); void ensureConnector(); void recreate_resolver(); bool check_protocol_fallback(); void dns_srv_try_next(); bool connect_host_try_next(); void qs_connected_step2(bool signalConnected = true); -}; - -// CS_NAMESPACE_END +}; // CS_NAMESPACE_END -#endif +#endif // CS_BSOCKET_H diff --git a/src/irisnet/noncore/cutestuff/bytestream.cpp b/src/irisnet/noncore/cutestuff/bytestream.cpp index 8602214e..b9d53ea6 100644 --- a/src/irisnet/noncore/cutestuff/bytestream.cpp +++ b/src/irisnet/noncore/cutestuff/bytestream.cpp @@ -18,6 +18,7 @@ */ #include "bytestream.h" + #include // CS_NAMESPACE_BEGIN @@ -54,41 +55,33 @@ //! Also available are the static convenience functions ByteStream::appendArray() //! and ByteStream::takeArray(), which make dealing with byte queues very easy. -class ByteStream::Private -{ +class ByteStream::Private { public: - Private() {} + Private() { } QByteArray readBuf, writeBuf; - int errorCode; - QString errorText; + int errorCode; + QString errorText; }; //! //! Constructs a ByteStream object with parent \a parent. -ByteStream::ByteStream(QObject *parent) -:QIODevice(parent) -{ - d = new Private; -} +ByteStream::ByteStream(QObject *parent) : QIODevice(parent) { d = new Private; } //! //! Destroys the object and frees allocated resources. -ByteStream::~ByteStream() -{ - delete d; -} +ByteStream::~ByteStream() { delete d; } //! //! Writes array \a a to the stream. qint64 ByteStream::writeData(const char *data, qint64 maxSize) { - if(!isOpen()) - return - 1; + if (!isOpen()) + return -1; - bool doWrite = bytesToWrite() == 0 ? true: false; - d->writeBuf.append(data, maxSize); - if(doWrite) + bool doWrite = bytesToWrite() == 0; + d->writeBuf.append(data, int(maxSize)); + if (doWrite) tryWrite(); return maxSize; } @@ -98,93 +91,60 @@ qint64 ByteStream::writeData(const char *data, qint64 maxSize) //! \a read will return all available data. qint64 ByteStream::readData(char *data, qint64 maxSize) { - maxSize = maxSize > d->readBuf.size()? d->readBuf.size() : maxSize; - memcpy(data, d->readBuf.constData(), maxSize); - d->readBuf.remove(0, maxSize); + maxSize = maxSize > d->readBuf.size() ? d->readBuf.size() : maxSize; + memcpy(data, d->readBuf.constData(), size_t(maxSize)); + d->readBuf.remove(0, int(maxSize)); return maxSize; } //! //! Returns the number of bytes available for reading. -qint64 ByteStream::bytesAvailable() const -{ - return QIODevice::bytesAvailable() + d->readBuf.size(); -} +qint64 ByteStream::bytesAvailable() const { return QIODevice::bytesAvailable() + d->readBuf.size(); } //! //! Returns the number of bytes that are waiting to be written. -qint64 ByteStream::bytesToWrite() const -{ - return d->writeBuf.size(); -} +qint64 ByteStream::bytesToWrite() const { return d->writeBuf.size(); } //! //! Clears the read buffer. -void ByteStream::clearReadBuffer() -{ - d->readBuf.resize(0); -} +void ByteStream::clearReadBuffer() { d->readBuf.resize(0); } //! //! Clears the write buffer. -void ByteStream::clearWriteBuffer() -{ - d->writeBuf.resize(0); -} +void ByteStream::clearWriteBuffer() { d->writeBuf.resize(0); } //! //! Appends \a block to the end of the read buffer. -void ByteStream::appendRead(const QByteArray &block) -{ - d->readBuf += block; -} +void ByteStream::appendRead(const QByteArray &block) { d->readBuf += block; } //! //! Appends \a block to the end of the write buffer. -void ByteStream::appendWrite(const QByteArray &block) -{ - d->writeBuf += block; -} +void ByteStream::appendWrite(const QByteArray &block) { d->writeBuf += block; } //! //! Returns \a size bytes from the start of the read buffer. //! If \a size is 0, then all available data will be returned. //! If \a del is TRUE, then the bytes are also removed. -QByteArray ByteStream::takeRead(int size, bool del) -{ - return takeArray(d->readBuf, size, del); -} +QByteArray ByteStream::takeRead(int size, bool del) { return takeArray(d->readBuf, size, del); } //! //! Returns \a size bytes from the start of the write buffer. //! If \a size is 0, then all available data will be returned. //! If \a del is TRUE, then the bytes are also removed. -QByteArray ByteStream::takeWrite(int size, bool del) -{ - return takeArray(d->writeBuf, size, del); -} +QByteArray ByteStream::takeWrite(int size, bool del) { return takeArray(d->writeBuf, size, del); } //! //! Returns a reference to the read buffer. -QByteArray & ByteStream::readBuf() -{ - return d->readBuf; -} +QByteArray &ByteStream::readBuf() { return d->readBuf; } //! //! Returns a reference to the write buffer. -QByteArray & ByteStream::writeBuf() -{ - return d->writeBuf; -} +QByteArray &ByteStream::writeBuf() { return d->writeBuf; } //! //! Attempts to try and write some bytes from the write buffer, and returns the number //! successfully written or -1 on error. The default implementation returns -1. -int ByteStream::tryWrite() -{ - return -1; -} +int ByteStream::tryWrite() { return -1; } //! //! Returns \a size bytes from the start of the array pointed to by \a from. @@ -193,12 +153,11 @@ int ByteStream::tryWrite() QByteArray ByteStream::takeArray(QByteArray &from, int size, bool del) { QByteArray result; - if(size == 0) { + if (size == 0) { result = from; - if(del) + if (del) from.resize(0); - } - else { + } else { result = from.left(size); if (del) { from.remove(0, size); @@ -209,17 +168,11 @@ QByteArray ByteStream::takeArray(QByteArray &from, int size, bool del) //! //! Returns last error code. -int ByteStream::errorCode() const -{ - return d->errorCode; -} +int ByteStream::errorCode() const { return d->errorCode; } //! //! Returns last error string corresponding to last error code. -QString &ByteStream::errorText() const -{ - return d->errorText; -} +QString &ByteStream::errorText() const { return d->errorText; } //! //! Sets last error with \a code and \a text and emit it @@ -232,11 +185,11 @@ void ByteStream::setError(int code, const QString &text) } } - void connectionClosed(); - void delayedCloseFinished(); - void readyRead(); - void bytesWritten(qint64); - void error(int); +void connectionClosed(); +void delayedCloseFinished(); +void readyRead(); +void bytesWritten(qint64); +void error(int); //! \fn void ByteStream::connectionClosed() //! This signal is emitted when the remote end of the stream closes. diff --git a/src/irisnet/noncore/cutestuff/bytestream.h b/src/irisnet/noncore/cutestuff/bytestream.h index 382dfe6b..c11335cc 100644 --- a/src/irisnet/noncore/cutestuff/bytestream.h +++ b/src/irisnet/noncore/cutestuff/bytestream.h @@ -20,32 +20,31 @@ #ifndef CS_BYTESTREAM_H #define CS_BYTESTREAM_H -#include #include #include +#include class QAbstractSocket; -// CS_NAMESPACE_BEGIN +// CS_NAMESPACE_BEGIN // CS_EXPORT_BEGIN -class ByteStream : public QIODevice -{ +class ByteStream : public QIODevice { Q_OBJECT public: enum Error { ErrOk, ErrRead, ErrWrite, ErrCustom = 10 }; - ByteStream(QObject *parent=nullptr); - ~ByteStream()=0; + ByteStream(QObject *parent = nullptr); + ~ByteStream() = 0; - bool isSequential() const { return true; } + bool isSequential() const { return true; } qint64 bytesAvailable() const; qint64 bytesToWrite() const; - static QByteArray takeArray(QByteArray &from, int size=0, bool del=true); + static QByteArray takeArray(QByteArray &from, int size = 0, bool del = true); - int errorCode() const; + int errorCode() const; QString &errorText() const; - virtual QAbstractSocket* abstractSocket() const { return nullptr; } + virtual QAbstractSocket *abstractSocket() const { return nullptr; } signals: void connectionClosed(); @@ -56,25 +55,24 @@ class ByteStream : public QIODevice qint64 writeData(const char *data, qint64 maxSize); qint64 readData(char *data, qint64 maxSize); - void setError(int code = ErrOk, const QString &text = QString()); - void clearReadBuffer(); - void clearWriteBuffer(); - void appendRead(const QByteArray &); - void appendWrite(const QByteArray &); - QByteArray takeRead(int size=0, bool del=true); - QByteArray takeWrite(int size=0, bool del=true); - QByteArray & readBuf(); - QByteArray & writeBuf(); + void setError(int code = ErrOk, const QString &text = QString()); + void clearReadBuffer(); + void clearWriteBuffer(); + void appendRead(const QByteArray &); + void appendWrite(const QByteArray &); + QByteArray takeRead(int size = 0, bool del = true); + QByteArray takeWrite(int size = 0, bool del = true); + QByteArray &readBuf(); + QByteArray &writeBuf(); virtual int tryWrite(); private: -//! \if _hide_doc_ + //! \if _hide_doc_ class Private; Private *d; -//! \endif + //! \endif }; // CS_EXPORT_END - // CS_NAMESPACE_END -#endif +#endif // CS_BYTESTREAM_H diff --git a/src/irisnet/noncore/cutestuff/cutestuff.pri b/src/irisnet/noncore/cutestuff/cutestuff.pri deleted file mode 100644 index f3fd8606..00000000 --- a/src/irisnet/noncore/cutestuff/cutestuff.pri +++ /dev/null @@ -1,15 +0,0 @@ -INCLUDEPATH += $$PWD - -HEADERS += \ - $$PWD/bytestream.h \ - $$PWD/bsocket.h \ - $$PWD/httpconnect.h \ - $$PWD/httppoll.h \ - $$PWD/socks.h - -SOURCES += \ - $$PWD/bytestream.cpp \ - $$PWD/bsocket.cpp \ - $$PWD/httpconnect.cpp \ - $$PWD/httppoll.cpp \ - $$PWD/socks.cpp diff --git a/src/irisnet/noncore/cutestuff/httpconnect.cpp b/src/irisnet/noncore/cutestuff/httpconnect.cpp index de8f02ea..dbf8c3c8 100644 --- a/src/irisnet/noncore/cutestuff/httpconnect.cpp +++ b/src/irisnet/noncore/cutestuff/httpconnect.cpp @@ -19,33 +19,29 @@ #include "httpconnect.h" -#include -#include -#include - #include "bsocket.h" -//#define PROX_DEBUG +#include +#include +#include +// #define PROX_DEBUG #ifdef PROX_DEBUG #include #endif // CS_NAMESPACE_BEGIN - #ifdef PROX_DEBUG QString escapeOutput(const QByteArray &in) { QString out; - for(int n = 0; n < in.size(); ++n) { - if(in[n] == '\\') { + for (int n = 0; n < in.size(); ++n) { + if (in[n] == '\\') { out += QString("\\\\"); - } - else if(in[n] >= 32 && in[n] < 127) { + } else if (in[n] >= 32 && in[n] < 127) { out += QChar::fromLatin1(in[n]); - } - else { - out += QString().sprintf("\\x%02x", (unsigned char)in[n]); + } else { + out += QString::asprintf("\\x%02x", (unsigned char)in[n]); } } return out; @@ -55,14 +51,13 @@ QString escapeOutput(const QByteArray &in) static QString extractLine(QByteArray *buf, bool *found) { // Scan for newline - int index = buf->indexOf ("\r\n"); + int index = buf->indexOf("\r\n"); if (index == -1) { // Newline not found if (found) *found = false; return ""; - } - else { + } else { // Found newline QString s = QString::fromLatin1(buf->left(index)); buf->remove(0, index + 2); @@ -76,48 +71,43 @@ static QString extractLine(QByteArray *buf, bool *found) static bool extractMainHeader(const QString &line, QString *proto, int *code, QString *msg) { int n = line.indexOf(' '); - if(n == -1) + if (n == -1) return false; - if(proto) + if (proto) *proto = line.mid(0, n); ++n; int n2 = line.indexOf(' ', n); - if(n2 == -1) + if (n2 == -1) return false; - if(code) - *code = line.mid(n, n2-n).toInt(); - n = n2+1; - if(msg) + if (code) + *code = QStringView { line }.mid(n, n2 - n).toInt(); + n = n2 + 1; + if (msg) *msg = line.mid(n); return true; } -class HttpConnect::Private -{ +class HttpConnect::Private { public: - Private(HttpConnect *_q) : - sock(_q) - { - } + Private(HttpConnect *_q) : sock(_q) { } BSocket sock; QString host; - int port; + quint16 port; QString user, pass; QString real_host; - int real_port; + int real_port; QByteArray recvBuf; - bool inHeader; + bool inHeader; QStringList headerLines; - int toWrite; + int toWrite; bool active; }; -HttpConnect::HttpConnect(QObject *parent) -:ByteStream(parent) +HttpConnect::HttpConnect(QObject *parent) : ByteStream(parent) { d = new Private(this); connect(&d->sock, SIGNAL(connected()), SLOT(sock_connected())); @@ -138,9 +128,9 @@ HttpConnect::~HttpConnect() void HttpConnect::resetConnection(bool clear) { - if(d->sock.state() != BSocket::Idle) + if (d->sock.state() != BSocket::Idle) d->sock.close(); - if(clear) { + if (clear) { clearReadBuffer(); d->recvBuf.resize(0); } @@ -154,18 +144,18 @@ void HttpConnect::setAuth(const QString &user, const QString &pass) d->pass = pass; } -void HttpConnect::connectToHost(const QString &proxyHost, int proxyPort, const QString &host, int port) +void HttpConnect::connectToHost(const QString &proxyHost, quint16 proxyPort, const QString &host, int port) { resetConnection(true); - d->host = proxyHost; - d->port = proxyPort; + d->host = proxyHost; + d->port = proxyPort; d->real_host = host; d->real_port = port; #ifdef PROX_DEBUG fprintf(stderr, "HttpConnect: Connecting to %s:%d", qPrintable(proxyHost), proxyPort); - if(d->user.isEmpty()) + if (d->user.isEmpty()) fprintf(stderr, "\n"); else fprintf(stderr, ", auth {%s,%s}\n", qPrintable(d->user), qPrintable(d->pass)); @@ -176,20 +166,20 @@ void HttpConnect::connectToHost(const QString &proxyHost, int proxyPort, const Q void HttpConnect::close() { d->sock.close(); - if(d->sock.bytesToWrite() == 0) + if (d->sock.bytesToWrite() == 0) resetConnection(); } qint64 HttpConnect::writeData(const char *data, qint64 maxSize) { - if(d->active) + if (d->active) return d->sock.write(data, maxSize); return 0; } qint64 HttpConnect::bytesToWrite() const { - if(d->active) + if (d->active) return d->sock.bytesToWrite(); else return 0; @@ -206,7 +196,7 @@ void HttpConnect::sock_connected() // connected, now send the request QString s; s += QString("CONNECT ") + d->real_host + ':' + QString::number(d->real_port) + " HTTP/1.0\r\n"; - if(!d->user.isEmpty()) { + if (!d->user.isEmpty()) { QString str = d->user + ':' + d->pass; s += QString("Proxy-Authorization: Basic ") + QCA::Base64().encodeString(str) + "\r\n"; } @@ -223,20 +213,19 @@ void HttpConnect::sock_connected() void HttpConnect::sock_connectionClosed() { - if(d->active) { + if (d->active) { resetConnection(); - connectionClosed(); - } - else { + emit connectionClosed(); + } else { setError(ErrProxyNeg); } } void HttpConnect::sock_delayedCloseFinished() { - if(d->active) { + if (d->active) { resetConnection(); - delayedCloseFinished(); + emit delayedCloseFinished(); } } @@ -244,17 +233,17 @@ void HttpConnect::sock_readyRead() { QByteArray block = d->sock.readAll(); - if(!d->active) { + if (!d->active) { d->recvBuf += block; - if(d->inHeader) { + if (d->inHeader) { // grab available lines - while(1) { - bool found; + while (1) { + bool found; QString line = extractLine(&d->recvBuf, &found); - if(!found) + if (!found) break; - if(line.isEmpty()) { + if (line.isEmpty()) { d->inHeader = false; break; } @@ -262,65 +251,60 @@ void HttpConnect::sock_readyRead() } // done with grabbing the header? - if(!d->inHeader) { + if (!d->inHeader) { QString str = d->headerLines.first(); d->headerLines.takeFirst(); QString proto; - int code; + int code; QString msg; - if(!extractMainHeader(str, &proto, &code, &msg)) { + if (!extractMainHeader(str, &proto, &code, &msg)) { #ifdef PROX_DEBUG fprintf(stderr, "HttpConnect: invalid header!\n"); #endif resetConnection(true); setError(ErrProxyNeg); return; - } - else { + } else { #ifdef PROX_DEBUG - fprintf(stderr, "HttpConnect: header proto=[%s] code=[%d] msg=[%s]\n", qPrintable(proto), code, qPrintable(msg)); - for(QStringList::ConstIterator it = d->headerLines.begin(); it != d->headerLines.end(); ++it) - fprintf(stderr, "HttpConnect: * [%s]\n", qPrintable(*it)); + fprintf(stderr, "HttpConnect: header proto=[%s] code=[%d] msg=[%s]\n", qPrintable(proto), code, + qPrintable(msg)); + for (const auto &it : std::as_const(d->headerLines)) + fprintf(stderr, "HttpConnect: * [%s]\n", qPrintable(it)); #endif } - if(code == 200) { // OK + if (code == 200) { // OK #ifdef PROX_DEBUG fprintf(stderr, "HttpConnect: << Success >>\n"); #endif d->active = true; setOpenMode(QIODevice::ReadWrite); - connected(); + emit connected(); - if(!d->recvBuf.isEmpty()) { + if (!d->recvBuf.isEmpty()) { appendRead(d->recvBuf); d->recvBuf.resize(0); - readyRead(); + emit readyRead(); return; } - } - else { - int err; + } else { + int err; QString errStr; - if(code == 407) { // Authentication failed - err = ErrProxyAuth; + if (code == 407) { // Authentication failed + err = ErrProxyAuth; errStr = tr("Authentication failed"); - } - else if(code == 404) { // Host not found - err = ErrHostNotFound; + } else if (code == 404) { // Host not found + err = ErrHostNotFound; errStr = tr("Host not found"); - } - else if(code == 403) { // Access denied - err = ErrProxyNeg; + } else if (code == 403) { // Access denied + err = ErrProxyNeg; errStr = tr("Access denied"); - } - else if(code == 503) { // Connection refused - err = ErrConnectionRefused; + } else if (code == 503) { // Connection refused + err = ErrConnectionRefused; errStr = tr("Connection refused"); - } - else { // invalid reply - err = ErrProxyNeg; + } else { // invalid reply + err = ErrProxyNeg; errStr = tr("Invalid reply"); } @@ -333,41 +317,39 @@ void HttpConnect::sock_readyRead() } } } - } - else { + } else { appendRead(block); - readyRead(); + emit readyRead(); return; } } void HttpConnect::sock_bytesWritten(qint64 x) { - if(d->toWrite > 0) { - int size = x; - if(d->toWrite < x) + if (d->toWrite > 0) { + int size = int(x); + if (d->toWrite < x) size = d->toWrite; d->toWrite -= size; x -= size; } - if(d->active && x > 0) - bytesWritten(x); + if (d->active && x > 0) + emit bytesWritten(x); } void HttpConnect::sock_error(int x) { - if(d->active) { + if (d->active) { resetConnection(); setError(ErrRead); - } - else { + } else { resetConnection(true); - if(x == BSocket::ErrHostNotFound) + if (x == BSocket::ErrHostNotFound) setError(ErrProxyConnect); - else if(x == BSocket::ErrConnectionRefused) + else if (x == BSocket::ErrConnectionRefused) setError(ErrProxyConnect); - else if(x == BSocket::ErrRead) + else if (x == BSocket::ErrRead) setError(ErrProxyNeg); } } diff --git a/src/irisnet/noncore/cutestuff/httpconnect.h b/src/irisnet/noncore/cutestuff/httpconnect.h index e3285322..04455323 100644 --- a/src/irisnet/noncore/cutestuff/httpconnect.h +++ b/src/irisnet/noncore/cutestuff/httpconnect.h @@ -23,21 +23,20 @@ #include "bytestream.h" // CS_NAMESPACE_BEGIN - -class HttpConnect : public ByteStream -{ +class HttpConnect : public ByteStream { Q_OBJECT public: enum Error { ErrConnectionRefused = ErrCustom, ErrHostNotFound, ErrProxyConnect, ErrProxyNeg, ErrProxyAuth }; - HttpConnect(QObject *parent=0); + HttpConnect(QObject *parent = nullptr); ~HttpConnect(); - void setAuth(const QString &user, const QString &pass=""); - void connectToHost(const QString &proxyHost, int proxyPort, const QString &host, int port); + void setAuth(const QString &user, const QString &pass = ""); + void connectToHost(const QString &proxyHost, quint16 proxyPort, const QString &host, int port); // from ByteStream - void close(); + void close(); qint64 bytesToWrite() const; + protected: qint64 writeData(const char *data, qint64 maxSize); @@ -56,9 +55,9 @@ private slots: class Private; Private *d; - void resetConnection(bool clear=false); + void resetConnection(bool clear = false); }; // CS_NAMESPACE_END -#endif +#endif // CS_HTTPCONNECT_H diff --git a/src/irisnet/noncore/cutestuff/httppoll.cpp b/src/irisnet/noncore/cutestuff/httppoll.cpp index 133f40d4..84fb8703 100644 --- a/src/irisnet/noncore/cutestuff/httppoll.cpp +++ b/src/irisnet/noncore/cutestuff/httppoll.cpp @@ -19,29 +19,30 @@ #include "httppoll.h" +#include "bsocket.h" + +#include +#include +#include +#include +#include #include -#include -#include -#include #include -#include -#include -#include "bsocket.h" #ifdef PROX_DEBUG #include #endif +#include #define POLL_KEYS 64 // CS_NAMESPACE_BEGIN - static QByteArray randomArray(int size) { QByteArray a; - a.resize(size); - for(int n = 0; n < size; ++n) - a[n] = (char)(256.0*rand()/(RAND_MAX+1.0)); + a.resize(size); + for (int n = 0; n < size; ++n) + a[n] = (char)(256.0 * rand() / (RAND_MAX + 1.0)); return a; } @@ -50,48 +51,43 @@ static QByteArray randomArray(int size) //---------------------------------------------------------------------------- static QString hpk(int n, const QString &s) { - if(n == 0) + if (n == 0) return s; else - return QCA::Base64().arrayToString( QCA::Hash("sha1").hash( hpk(n - 1, s).toLatin1() ).toByteArray() ); + return QCA::Base64().arrayToString(QCA::Hash("sha1").hash(hpk(n - 1, s).toLatin1()).toByteArray()); } -class HttpPoll::Private -{ +class HttpPoll::Private { public: - Private(HttpPoll *_q) : - http(_q) - { - } + Private(HttpPoll *_q) : http(_q) { } HttpProxyPost http; - QString host; - int port; - QString user, pass; - QUrl url; - bool use_proxy; + QString host; + int port; + QString user, pass; + QUrl url; + bool use_proxy; QByteArray out; - int state; - bool closing; + int state; + bool closing; QString ident; QTimer *t; QString key[POLL_KEYS]; - int key_n; + int key_n; int polltime; }; -HttpPoll::HttpPoll(QObject *parent) -:ByteStream(parent) +HttpPoll::HttpPoll(QObject *parent) : ByteStream(parent) { d = new Private(this); d->polltime = 30; - d->t = new QTimer(this); + d->t = new QTimer(this); d->t->setSingleShot(true); connect(d->t, SIGNAL(timeout()), SLOT(do_sync())); @@ -108,20 +104,17 @@ HttpPoll::~HttpPoll() delete d; } -QAbstractSocket* HttpPoll::abstractSocket() const -{ - return d->http.abstractSocket(); -} +QAbstractSocket *HttpPoll::abstractSocket() const { return d->http.abstractSocket(); } void HttpPoll::resetConnection(bool clear) { - if(d->http.isActive()) + if (d->http.isActive()) d->http.stop(); - if(clear) + if (clear) clearReadBuffer(); clearWriteBuffer(); d->out.resize(0); - d->state = 0; + d->state = 0; d->closing = false; d->t->stop(); } @@ -132,50 +125,46 @@ void HttpPoll::setAuth(const QString &user, const QString &pass) d->pass = pass; } -void HttpPoll::connectToUrl(const QUrl &url) -{ - connectToHost("", 0, url); -} +void HttpPoll::connectToUrl(const QUrl &url) { connectToHost("", 0, url); } void HttpPoll::connectToHost(const QString &proxyHost, int proxyPort, const QUrl &url) { resetConnection(true); bool useSsl = false; - d->port = 80; + d->port = 80; // using proxy? - if(!proxyHost.isEmpty()) { - d->host = proxyHost; - d->port = proxyPort; - d->url = url; + if (!proxyHost.isEmpty()) { + d->host = proxyHost; + d->port = proxyPort; + d->url = url; d->use_proxy = true; - } - else { + } else { d->host = url.host(); - if(url.port() != -1) + if (url.port() != -1) d->port = url.port(); else if (url.scheme() == "https") { d->port = 443; - useSsl = true; + useSsl = true; } d->url.setUrl(url.path() + "?" + url.query(QUrl::FullyEncoded), QUrl::StrictMode); d->use_proxy = false; } resetKey(); - bool last; + bool last; QString key = getKey(&last); #ifdef PROX_DEBUG fprintf(stderr, "HttpPoll: Connecting to %s:%d [%s]", d->host.latin1(), d->port, d->url.latin1()); - if(d->user.isEmpty()) + if (d->user.isEmpty()) fprintf(stderr, "\n"); else fprintf(stderr, ", auth {%s,%s}\n", d->user.latin1(), d->pass.latin1()); #endif QPointer self = this; - syncStarted(); - if(!self) + emit syncStarted(); + if (!self) return; d->state = 1; @@ -184,49 +173,41 @@ void HttpPoll::connectToHost(const QString &proxyHost, int proxyPort, const QUrl d->http.post(d->host, d->port, d->url, makePacket("0", key, "", QByteArray()), d->use_proxy); } -QByteArray HttpPoll::makePacket(const QString &ident, const QString &key, const QString &newkey, const QByteArray &block) +QByteArray HttpPoll::makePacket(const QString &ident, const QString &key, const QString &newkey, + const QByteArray &block) { QString str = ident; - if(!key.isEmpty()) { + if (!key.isEmpty()) { str += ';'; str += key; } - if(!newkey.isEmpty()) { + if (!newkey.isEmpty()) { str += ';'; str += newkey; } str += ','; - QByteArray cs = str.toLatin1(); - int len = cs.length(); + QByteArray cs = str.toLatin1(); + int len = cs.length(); QByteArray a; - a.resize(len + block.size()); - memcpy(a.data(), cs.data(), len); - memcpy(a.data() + len, block.data(), block.size()); + a.resize(len + block.size()); + memcpy(a.data(), cs.data(), size_t(len)); + memcpy(a.data() + len, block.data(), size_t(block.size())); return a; } -int HttpPoll::pollInterval() const -{ - return d->polltime; -} +int HttpPoll::pollInterval() const { return d->polltime; } -void HttpPoll::setPollInterval(int seconds) -{ - d->polltime = seconds; -} +void HttpPoll::setPollInterval(int seconds) { d->polltime = seconds; } -bool HttpPoll::isOpen() const -{ - return (d->state == 2 ? true: false); -} +bool HttpPoll::isOpen() const { return d->state == 2; } void HttpPoll::close() { - if(d->state == 0 || d->closing) + if (d->state == 0 || d->closing) return; - if(bytesToWrite() == 0) + if (bytesToWrite() == 0) resetConnection(); else d->closing = true; @@ -236,84 +217,81 @@ void HttpPoll::http_result() { // check for death :) QPointer self = this; - syncFinished(); - if(!self) + emit syncFinished(); + if (!self) return; // get id and packet QString id; QString cookie = d->http.getHeader("Set-Cookie"); - int n = cookie.indexOf("ID="); - if(n == -1) { + int n = cookie.indexOf("ID="); + if (n == -1) { resetConnection(); setError(ErrRead); return; } n += 3; int n2 = cookie.indexOf(';', n); - if(n2 != -1) - id = cookie.mid(n, n2-n); + if (n2 != -1) + id = cookie.mid(n, n2 - n); else id = cookie.mid(n); QByteArray block = d->http.body(); // session error? - if(id.right(2) == ":0") { - if(id == "0:0" && d->state == 2) { + if (id.right(2) == ":0") { + if (id == "0:0" && d->state == 2) { resetConnection(); - connectionClosed(); + emit connectionClosed(); return; - } - else { + } else { resetConnection(); setError(ErrRead); return; } } - d->ident = id; + d->ident = id; bool justNowConnected = false; - if(d->state == 1) { - d->state = 2; + if (d->state == 1) { + d->state = 2; justNowConnected = true; } // sync up again soon - if(bytesToWrite() > 0 || !d->closing) { + if (bytesToWrite() > 0 || !d->closing) { d->t->start(d->polltime * 1000); - } + } // connecting - if(justNowConnected) { - connected(); - } - else { - if(!d->out.isEmpty()) { + if (justNowConnected) { + emit connected(); + } else { + if (!d->out.isEmpty()) { int x = d->out.size(); d->out.resize(0); takeWrite(x); - bytesWritten(x); + emit bytesWritten(x); } } - if(!self) + if (!self) return; - if(!block.isEmpty()) { + if (!block.isEmpty()) { appendRead(block); - readyRead(); + emit readyRead(); } - if(!self) + if (!self) return; - if(bytesToWrite() > 0) { + if (bytesToWrite() > 0) { do_sync(); - } - else { - if(d->closing) { + } else { + if (d->closing) { resetConnection(); - delayedCloseFinished(); + emit delayedCloseFinished(); return; } } @@ -322,46 +300,46 @@ void HttpPoll::http_result() void HttpPoll::http_error(int x) { resetConnection(); - if(x == HttpProxyPost::ErrConnectionRefused) + if (x == HttpProxyPost::ErrConnectionRefused) setError(ErrConnectionRefused); - else if(x == HttpProxyPost::ErrHostNotFound) + else if (x == HttpProxyPost::ErrHostNotFound) setError(ErrHostNotFound); - else if(x == HttpProxyPost::ErrSocket) + else if (x == HttpProxyPost::ErrSocket) setError(ErrRead); - else if(x == HttpProxyPost::ErrProxyConnect) + else if (x == HttpProxyPost::ErrProxyConnect) setError(ErrProxyConnect); - else if(x == HttpProxyPost::ErrProxyNeg) + else if (x == HttpProxyPost::ErrProxyNeg) setError(ErrProxyNeg); - else if(x == HttpProxyPost::ErrProxyAuth) + else if (x == HttpProxyPost::ErrProxyAuth) setError(ErrProxyAuth); } int HttpPoll::tryWrite() { - if(!d->http.isActive()) + if (!d->http.isActive()) do_sync(); return 0; } void HttpPoll::do_sync() { - if(d->http.isActive()) + if (d->http.isActive()) return; d->t->stop(); d->out = takeWrite(0, false); - bool last; + bool last; QString key = getKey(&last); QString newkey; - if(last) { + if (last) { resetKey(); newkey = getKey(&last); } QPointer self = this; - syncStarted(); - if(!self) + emit syncStarted(); + if (!self) return; d->http.post(d->host, d->port, d->url, makePacket(d->ident, key, newkey, d->out), d->use_proxy); @@ -372,24 +350,23 @@ void HttpPoll::resetKey() #ifdef PROX_DEBUG fprintf(stderr, "HttpPoll: reset key!\n"); #endif - QByteArray a = randomArray(64); - QString str = QString::fromLatin1(a.data(), a.size()); + QByteArray a = randomArray(64); + QString str = QString::fromLatin1(a.data(), a.size()); d->key_n = POLL_KEYS; - for(int n = 0; n < POLL_KEYS; ++n) - d->key[n] = hpk(n+1, str); + for (int n = 0; n < POLL_KEYS; ++n) + d->key[n] = hpk(n + 1, str); } -const QString & HttpPoll::getKey(bool *last) +const QString &HttpPoll::getKey(bool *last) { *last = false; --(d->key_n); - if(d->key_n == 0) + if (d->key_n == 0) *last = true; return d->key[d->key_n]; } - //---------------------------------------------------------------------------- // HttpProxyPost //---------------------------------------------------------------------------- @@ -397,24 +374,24 @@ static QString extractLine(QByteArray *buf, bool *found) { // scan for newline int n; - for(n = 0; n < (int)buf->size()-1; ++n) { - if(buf->at(n) == '\r' && buf->at(n+1) == '\n') { + for (n = 0; n < (int)buf->size() - 1; ++n) { + if (buf->at(n) == '\r' && buf->at(n + 1) == '\n') { QByteArray cstr; cstr.resize(n); - memcpy(cstr.data(), buf->data(), n); + memcpy(cstr.data(), buf->data(), size_t(n)); n += 2; // hack off CR/LF - memmove(buf->data(), buf->data() + n, buf->size() - n); + memmove(buf->data(), buf->data() + n, size_t(buf->size() - n)); buf->resize(buf->size() - n); QString s = QString::fromUtf8(cstr); - if(found) + if (found) *found = true; return s; } } - if(found) + if (found) *found = false; return ""; } @@ -422,51 +399,42 @@ static QString extractLine(QByteArray *buf, bool *found) static bool extractMainHeader(const QString &line, QString *proto, int *code, QString *msg) { int n = line.indexOf(' '); - if(n == -1) + if (n == -1) return false; - if(proto) + if (proto) *proto = line.mid(0, n); ++n; int n2 = line.indexOf(' ', n); - if(n2 == -1) + if (n2 == -1) return false; - if(code) - *code = line.mid(n, n2-n).toInt(); - n = n2+1; - if(msg) + if (code) + *code = QStringView { line }.mid(n, n2 - n).toInt(); + n = n2 + 1; + if (msg) *msg = line.mid(n); return true; } -class HttpProxyPost::Private -{ +class HttpProxyPost::Private { public: - Private(HttpProxyPost *_q) : - sock(_q), - tls(0) - { - } + Private(HttpProxyPost *_q) : sock(_q), tls(nullptr) { } - ~Private() - { - delete tls; - } + ~Private() { delete tls; } - BSocket sock; + BSocket sock; QHostAddress lastAddress; - QByteArray postdata, recvBuf, body; - QUrl url; - QString user, pass; - bool inHeader; - QStringList headerLines; - bool asProxy; - bool useSsl; - QString host; - QCA::TLS *tls; + QByteArray postdata, recvBuf, body; + QUrl url; + QString user, pass; + bool inHeader; + QStringList headerLines; + bool asProxy; + bool useSsl; + QString host; + QCA::TLS *tls; }; -HttpProxyPost::HttpProxyPost(QObject *parent) -:QObject(parent) +HttpProxyPost::HttpProxyPost(QObject *parent) : QObject(parent) { d = new Private(this); connect(&d->sock, SIGNAL(connected()), SLOT(sock_connected())); @@ -482,22 +450,16 @@ HttpProxyPost::~HttpProxyPost() delete d; } -void HttpProxyPost::setUseSsl(bool state) -{ - d->useSsl = state; -} +void HttpProxyPost::setUseSsl(bool state) { d->useSsl = state; } -QAbstractSocket* HttpProxyPost::abstractSocket() const -{ - return d->sock.abstractSocket(); -} +QAbstractSocket *HttpProxyPost::abstractSocket() const { return d->sock.abstractSocket(); } void HttpProxyPost::resetConnection(bool clear) { - if(d->sock.state() != BSocket::Idle) + if (d->sock.state() != BSocket::Idle) d->sock.close(); d->recvBuf.resize(0); - if(clear) + if (clear) d->body.resize(0); } @@ -507,23 +469,21 @@ void HttpProxyPost::setAuth(const QString &user, const QString &pass) d->pass = pass; } -bool HttpProxyPost::isActive() const -{ - return (d->sock.state() == BSocket::Idle ? false: true); -} +bool HttpProxyPost::isActive() const { return d->sock.state() != BSocket::Idle; } -void HttpProxyPost::post(const QString &proxyHost, int proxyPort, const QUrl &url, const QByteArray &data, bool asProxy) +void HttpProxyPost::post(const QString &proxyHost, quint16 proxyPort, const QUrl &url, const QByteArray &data, + bool asProxy) { resetConnection(true); - d->host = proxyHost; - d->url = url; + d->host = proxyHost; + d->url = url; d->postdata = data; - d->asProxy = asProxy; + d->asProxy = asProxy; #ifdef PROX_DEBUG fprintf(stderr, "HttpProxyPost: Connecting to %s:%d", proxyHost.latin1(), proxyPort); - if(d->user.isEmpty()) + if (d->user.isEmpty()) fprintf(stderr, "\n"); else fprintf(stderr, ", auth {%s,%s}\n", d->user.latin1(), d->pass.latin1()); @@ -537,25 +497,19 @@ void HttpProxyPost::post(const QString &proxyHost, int proxyPort, const QUrl &ur } } -void HttpProxyPost::stop() -{ - resetConnection(); -} +void HttpProxyPost::stop() { resetConnection(); } -QByteArray HttpProxyPost::body() const -{ - return d->body; -} +QByteArray HttpProxyPost::body() const { return d->body; } QString HttpProxyPost::getHeader(const QString &var) const { - foreach (const QString &s, d->headerLines) { + for (const QString &s : std::as_const(d->headerLines)) { int n = s.indexOf(": "); - if(n == -1) + if (n == -1) continue; QString v = s.mid(0, n); - if(v.toLower() == var.toLower()) - return s.mid(n+2); + if (v.toLower() == var.toLower()) + return s.mid(n + 2); } return ""; } @@ -565,7 +519,7 @@ void HttpProxyPost::sock_connected() #ifdef PROX_DEBUG fprintf(stderr, "HttpProxyPost: Connected\n"); #endif - if(d->useSsl) { + if (d->useSsl) { d->tls = new QCA::TLS(this); connect(d->tls, SIGNAL(readyRead()), SLOT(tls_readyRead())); connect(d->tls, SIGNAL(readyReadOutgoing()), SLOT(tls_readyReadOutgoing())); @@ -574,7 +528,7 @@ void HttpProxyPost::sock_connected() } d->lastAddress = d->sock.peerAddress(); - d->inHeader = true; + d->inHeader = true; d->headerLines.clear(); QUrl u = d->url; @@ -582,22 +536,21 @@ void HttpProxyPost::sock_connected() // connected, now send the request QByteArray s; s += QByteArray("POST ") + d->url.toEncoded() + " HTTP/1.1\r\n"; - if(d->asProxy) { - if(!d->user.isEmpty()) { + if (d->asProxy) { + if (!d->user.isEmpty()) { QByteArray str = d->user.toUtf8() + ':' + d->pass.toUtf8(); s += QByteArray("Proxy-Authorization: Basic ") + str.toBase64() + "\r\n"; } s += "Pragma: no-cache\r\n"; s += QByteArray("Host: ") + u.host().toUtf8() + "\r\n"; - } - else { + } else { s += QByteArray("Host: ") + d->host.toUtf8() + "\r\n"; } s += "Content-Type: application/x-www-form-urlencoded\r\n"; s += QByteArray("Content-Length: ") + QByteArray::number(d->postdata.size()) + "\r\n"; s += "\r\n"; - if(d->useSsl) { + if (d->useSsl) { // write request d->tls->write(s); @@ -616,18 +569,18 @@ void HttpProxyPost::sock_connectionClosed() { d->body = d->recvBuf; resetConnection(); - result(); + emit result(); } void HttpProxyPost::tls_readyRead() { - //printf("tls_readyRead\n"); + // printf("tls_readyRead\n"); processData(d->tls->read()); } void HttpProxyPost::tls_readyReadOutgoing() { - //printf("tls_readyReadOutgoing\n"); + // printf("tls_readyReadOutgoing\n"); d->sock.write(d->tls->readOutgoing()); } @@ -637,13 +590,13 @@ void HttpProxyPost::tls_error() fprintf(stderr, "HttpProxyGetStream: ssl error: %d\n", d->tls->errorCode()); #endif resetConnection(true); - error(ErrConnectionRefused); // FIXME: bogus error + emit error(ErrConnectionRefused); // FIXME: bogus error } void HttpProxyPost::sock_readyRead() { QByteArray block = d->sock.readAll(); - if(d->useSsl) + if (d->useSsl) d->tls->writeIncoming(block); else processData(block); @@ -653,14 +606,14 @@ void HttpProxyPost::processData(const QByteArray &block) { d->recvBuf += block; - if(d->inHeader) { + if (d->inHeader) { // grab available lines - while(1) { - bool found; + while (1) { + bool found; QString line = extractLine(&d->recvBuf, &found); - if(!found) + if (!found) break; - if(line.isEmpty()) { + if (line.isEmpty()) { d->inHeader = false; break; } @@ -668,55 +621,50 @@ void HttpProxyPost::processData(const QByteArray &block) } // done with grabbing the header? - if(!d->inHeader) { + if (!d->inHeader) { QString str = d->headerLines.first(); d->headerLines.takeFirst(); QString proto; - int code; + int code; QString msg; - if(!extractMainHeader(str, &proto, &code, &msg)) { + if (!extractMainHeader(str, &proto, &code, &msg)) { #ifdef PROX_DEBUG fprintf(stderr, "HttpProxyPost: invalid header!\n"); #endif resetConnection(true); - error(ErrProxyNeg); + emit error(ErrProxyNeg); return; - } - else { + } else { #ifdef PROX_DEBUG - fprintf(stderr, "HttpProxyPost: header proto=[%s] code=[%d] msg=[%s]\n", proto.latin1(), code, msg.latin1()); - foreach (const QString &s, d->headerLines) + fprintf(stderr, "HttpProxyPost: header proto=[%s] code=[%d] msg=[%s]\n", proto.latin1(), code, + msg.latin1()); + for (const QString &s : d->headerLines) fprintf(stderr, "HttpProxyPost: * [%s]\n", qPrintable(s)); #endif } - if(code == 200) { // OK + if (code == 200) { // OK #ifdef PROX_DEBUG fprintf(stderr, "HttpProxyPost: << Success >>\n"); #endif - } - else { - int err; + } else { + int err; QString errStr; - if(code == 407) { // Authentication failed - err = ErrProxyAuth; + if (code == 407) { // Authentication failed + err = ErrProxyAuth; errStr = tr("Authentication failed"); - } - else if(code == 404) { // Host not found - err = ErrHostNotFound; + } else if (code == 404) { // Host not found + err = ErrHostNotFound; errStr = tr("Host not found"); - } - else if(code == 403) { // Access denied - err = ErrProxyNeg; + } else if (code == 403) { // Access denied + err = ErrProxyNeg; errStr = tr("Access denied"); - } - else if(code == 503) { // Connection refused - err = ErrConnectionRefused; + } else if (code == 503) { // Connection refused + err = ErrConnectionRefused; errStr = tr("Connection refused"); - } - else { // invalid reply - err = ErrProxyNeg; + } else { // invalid reply + err = ErrProxyNeg; errStr = tr("Invalid reply"); } @@ -724,7 +672,7 @@ void HttpProxyPost::processData(const QByteArray &block) fprintf(stderr, "HttpProxyPost: << Error >> [%s]\n", errStr.latin1()); #endif resetConnection(true); - error(err); + emit error(err); return; } } @@ -737,44 +685,39 @@ void HttpProxyPost::sock_error(int x) fprintf(stderr, "HttpProxyPost: socket error: %d\n", x); #endif resetConnection(true); - if(x == BSocket::ErrHostNotFound) - error(ErrProxyConnect); - else if(x == BSocket::ErrConnectionRefused) - error(ErrProxyConnect); - else if(x == BSocket::ErrRead) - error(ErrProxyNeg); + if (x == BSocket::ErrHostNotFound) + emit error(ErrProxyConnect); + else if (x == BSocket::ErrConnectionRefused) + emit error(ErrProxyConnect); + else if (x == BSocket::ErrRead) + emit error(ErrProxyNeg); } //---------------------------------------------------------------------------- // HttpProxyGetStream //---------------------------------------------------------------------------- -class HttpProxyGetStream::Private -{ +class HttpProxyGetStream::Private { public: - Private(HttpProxyGetStream *_q) : - sock(_q) - { - } + Private(HttpProxyGetStream *_q) : sock(_q) { } - BSocket sock; - QByteArray recvBuf; - QString url; - QString user, pass; - bool inHeader; + BSocket sock; + QByteArray recvBuf; + QString url; + QString user, pass; + bool inHeader; QStringList headerLines; - bool use_ssl; - bool asProxy; - QString host; - int length; + bool use_ssl; + bool asProxy; + QString host; + int length; QCA::TLS *tls; }; -HttpProxyGetStream::HttpProxyGetStream(QObject *parent) -:QObject(parent) +HttpProxyGetStream::HttpProxyGetStream(QObject *parent) : QObject(parent) { - d = new Private(this); - d->tls = 0; + d = new Private(this); + d->tls = nullptr; connect(&d->sock, SIGNAL(connected()), SLOT(sock_connected())); connect(&d->sock, SIGNAL(connectionClosed()), SLOT(sock_connectionClosed())); connect(&d->sock, SIGNAL(readyRead()), SLOT(sock_readyRead())); @@ -790,14 +733,14 @@ HttpProxyGetStream::~HttpProxyGetStream() void HttpProxyGetStream::resetConnection(bool /*clear*/) { - if(d->tls) { + if (d->tls) { delete d->tls; - d->tls = 0; + d->tls = nullptr; } - if(d->sock.state() != BSocket::Idle) + if (d->sock.state() != BSocket::Idle) d->sock.close(); d->recvBuf.resize(0); - //if(clear) + // if(clear) // d->body.resize(0); d->length = -1; } @@ -808,59 +751,50 @@ void HttpProxyGetStream::setAuth(const QString &user, const QString &pass) d->pass = pass; } -bool HttpProxyGetStream::isActive() const -{ - return (d->sock.state() == BSocket::Idle ? false: true); -} +bool HttpProxyGetStream::isActive() const { return !(d->sock.state() == BSocket::Idle); } void HttpProxyGetStream::get(const QString &proxyHost, int proxyPort, const QString &url, bool ssl, bool asProxy) { resetConnection(true); - d->host = proxyHost; - d->url = url; + d->host = proxyHost; + d->url = url; d->use_ssl = ssl; d->asProxy = asProxy; #ifdef PROX_DEBUG fprintf(stderr, "HttpProxyGetStream: Connecting to %s:%d", proxyHost.latin1(), proxyPort); - if(d->user.isEmpty()) + if (d->user.isEmpty()) fprintf(stderr, "\n"); else fprintf(stderr, ", auth {%s,%s}\n", d->user.latin1(), d->pass.latin1()); #endif - d->sock.connectToHost(proxyHost, proxyPort); + d->sock.connectToHost(proxyHost, quint16(proxyPort)); } -void HttpProxyGetStream::stop() -{ - resetConnection(); -} +void HttpProxyGetStream::stop() { resetConnection(); } QString HttpProxyGetStream::getHeader(const QString &var) const { - foreach (const QString &s, d->headerLines) { + for (const QString &s : std::as_const(d->headerLines)) { int n = s.indexOf(": "); - if(n == -1) + if (n == -1) continue; QString v = s.mid(0, n); - if(v.toLower() == var.toLower()) - return s.mid(n+2); + if (v.toLower() == var.toLower()) + return s.mid(n + 2); } return ""; } -int HttpProxyGetStream::length() const -{ - return d->length; -} +int HttpProxyGetStream::length() const { return d->length; } void HttpProxyGetStream::sock_connected() { #ifdef PROX_DEBUG fprintf(stderr, "HttpProxyGetStream: Connected\n"); #endif - if(d->use_ssl) { + if (d->use_ssl) { d->tls = new QCA::TLS(this); connect(d->tls, SIGNAL(readyRead()), SLOT(tls_readyRead())); connect(d->tls, SIGNAL(readyReadOutgoing()), SLOT(tls_readyReadOutgoing())); @@ -876,21 +810,20 @@ void HttpProxyGetStream::sock_connected() // connected, now send the request QString s; s += QString("GET ") + d->url + " HTTP/1.0\r\n"; - if(d->asProxy) { - if(!d->user.isEmpty()) { + if (d->asProxy) { + if (!d->user.isEmpty()) { QString str = d->user + ':' + d->pass; s += QString("Proxy-Authorization: Basic ") + QCA::Base64().encodeString(str) + "\r\n"; } s += "Pragma: no-cache\r\n"; s += QString("Host: ") + u.host() + "\r\n"; - } - else { + } else { s += QString("Host: ") + d->host + "\r\n"; } s += "\r\n"; // write request - if(d->use_ssl) + if (d->use_ssl) d->tls->write(s.toUtf8()); else d->sock.write(s.toUtf8()); @@ -898,7 +831,7 @@ void HttpProxyGetStream::sock_connected() void HttpProxyGetStream::sock_connectionClosed() { - //d->body = d->recvBuf; + // d->body = d->recvBuf; resetConnection(); emit finished(); } @@ -907,7 +840,7 @@ void HttpProxyGetStream::sock_readyRead() { QByteArray block = d->sock.readAll(); - if(d->use_ssl) + if (d->use_ssl) d->tls->writeIncoming(block); else processData(block); @@ -915,22 +848,22 @@ void HttpProxyGetStream::sock_readyRead() void HttpProxyGetStream::processData(const QByteArray &block) { - printf("processData: %d bytes\n", block.size()); - if(!d->inHeader) { + printf("processData: %lld bytes\n", qsizetype(block.size())); + if (!d->inHeader) { emit dataReady(block); return; } d->recvBuf += block; - if(d->inHeader) { + if (d->inHeader) { // grab available lines - while(1) { - bool found; + while (1) { + bool found; QString line = extractLine(&d->recvBuf, &found); - if(!found) + if (!found) break; - if(line.isEmpty()) { + if (line.isEmpty()) { printf("empty line\n"); d->inHeader = false; break; @@ -940,65 +873,60 @@ void HttpProxyGetStream::processData(const QByteArray &block) } // done with grabbing the header? - if(!d->inHeader) { + if (!d->inHeader) { QString str = d->headerLines.first(); d->headerLines.takeFirst(); QString proto; - int code; + int code; QString msg; - if(!extractMainHeader(str, &proto, &code, &msg)) { + if (!extractMainHeader(str, &proto, &code, &msg)) { #ifdef PROX_DEBUG fprintf(stderr, "HttpProxyGetStream: invalid header!\n"); #endif resetConnection(true); - error(ErrProxyNeg); + emit error(ErrProxyNeg); return; - } - else { + } else { #ifdef PROX_DEBUG - fprintf(stderr, "HttpProxyGetStream: header proto=[%s] code=[%d] msg=[%s]\n", proto.latin1(), code, msg.latin1()); - foreach (const QString &s, d->headerLines) + fprintf(stderr, "HttpProxyGetStream: header proto=[%s] code=[%d] msg=[%s]\n", proto.latin1(), code, + msg.latin1()); + for (const QString &s : d->headerLines) fprintf(stderr, "HttpProxyGetStream: * [%s]\n", qPrintable(s)); #endif } - if(code == 200) { // OK + if (code == 200) { // OK #ifdef PROX_DEBUG fprintf(stderr, "HttpProxyGetStream: << Success >>\n"); #endif bool ok; - int x = getHeader("Content-Length").toInt(&ok); - if(ok) + int x = getHeader("Content-Length").toInt(&ok); + if (ok) d->length = x; QPointer self = this; - emit handshaken(); - if(!self) + emit handshaken(); + if (!self) return; - } - else { - int err; + } else { + int err; QString errStr; - if(code == 407) { // Authentication failed - err = ErrProxyAuth; + if (code == 407) { // Authentication failed + err = ErrProxyAuth; errStr = tr("Authentication failed"); - } - else if(code == 404) { // Host not found - err = ErrHostNotFound; + } else if (code == 404) { // Host not found + err = ErrHostNotFound; errStr = tr("Host not found"); - } - else if(code == 403) { // Access denied - err = ErrProxyNeg; + } else if (code == 403) { // Access denied + err = ErrProxyNeg; errStr = tr("Access denied"); - } - else if(code == 503) { // Connection refused - err = ErrConnectionRefused; + } else if (code == 503) { // Connection refused + err = ErrConnectionRefused; errStr = tr("Connection refused"); - } - else { // invalid reply - err = ErrProxyNeg; + } else { // invalid reply + err = ErrProxyNeg; errStr = tr("Invalid reply"); } @@ -1006,11 +934,11 @@ void HttpProxyGetStream::processData(const QByteArray &block) fprintf(stderr, "HttpProxyGetStream: << Error >> [%s]\n", errStr.latin1()); #endif resetConnection(true); - error(err); + emit error(err); return; } - if(!d->recvBuf.isEmpty()) { + if (!d->recvBuf.isEmpty()) { QByteArray a = d->recvBuf; d->recvBuf.clear(); emit dataReady(a); @@ -1025,23 +953,23 @@ void HttpProxyGetStream::sock_error(int x) fprintf(stderr, "HttpProxyGetStream: socket error: %d\n", x); #endif resetConnection(true); - if(x == BSocket::ErrHostNotFound) - error(ErrProxyConnect); - else if(x == BSocket::ErrConnectionRefused) - error(ErrProxyConnect); - else if(x == BSocket::ErrRead) - error(ErrProxyNeg); + if (x == BSocket::ErrHostNotFound) + emit error(ErrProxyConnect); + else if (x == BSocket::ErrConnectionRefused) + emit error(ErrProxyConnect); + else if (x == BSocket::ErrRead) + emit error(ErrProxyNeg); } void HttpProxyGetStream::tls_readyRead() { - //printf("tls_readyRead\n"); + // printf("tls_readyRead\n"); processData(d->tls->read()); } void HttpProxyGetStream::tls_readyReadOutgoing() { - //printf("tls_readyReadOutgoing\n"); + // printf("tls_readyReadOutgoing\n"); d->sock.write(d->tls->readOutgoing()); } @@ -1051,7 +979,7 @@ void HttpProxyGetStream::tls_error() fprintf(stderr, "HttpProxyGetStream: ssl error: %d\n", d->tls->errorCode()); #endif resetConnection(true); - error(ErrConnectionRefused); // FIXME: bogus error + emit error(ErrConnectionRefused); // FIXME: bogus error } // CS_NAMESPACE_END diff --git a/src/irisnet/noncore/cutestuff/httppoll.h b/src/irisnet/noncore/cutestuff/httppoll.h index 82bf1f06..a65a5bcc 100644 --- a/src/irisnet/noncore/cutestuff/httppoll.h +++ b/src/irisnet/noncore/cutestuff/httppoll.h @@ -25,22 +25,20 @@ class QUrl; // CS_NAMESPACE_BEGIN - -class HttpPoll : public ByteStream -{ +class HttpPoll : public ByteStream { Q_OBJECT public: enum Error { ErrConnectionRefused = ErrCustom, ErrHostNotFound, ErrProxyConnect, ErrProxyNeg, ErrProxyAuth }; - HttpPoll(QObject *parent=0); + HttpPoll(QObject *parent = nullptr); ~HttpPoll(); - virtual QAbstractSocket* abstractSocket() const; + virtual QAbstractSocket *abstractSocket() const; - void setAuth(const QString &user, const QString &pass=""); + void setAuth(const QString &user, const QString &pass = ""); void connectToUrl(const QUrl &url); void connectToHost(const QString &proxyHost, int proxyPort, const QUrl &url); - int pollInterval() const; + int pollInterval() const; void setPollInterval(int seconds); // from ByteStream @@ -64,29 +62,29 @@ private slots: class Private; Private *d; - void resetConnection(bool clear=false); - QByteArray makePacket(const QString &ident, const QString &key, const QString &newkey, const QByteArray &block); - void resetKey(); - const QString & getKey(bool *); + void resetConnection(bool clear = false); + QByteArray makePacket(const QString &ident, const QString &key, const QString &newkey, const QByteArray &block); + void resetKey(); + const QString &getKey(bool *); }; -class HttpProxyPost : public QObject -{ +class HttpProxyPost : public QObject { Q_OBJECT public: enum Error { ErrConnectionRefused, ErrHostNotFound, ErrSocket, ErrProxyConnect, ErrProxyNeg, ErrProxyAuth }; - HttpProxyPost(QObject *parent=0); + HttpProxyPost(QObject *parent = nullptr); ~HttpProxyPost(); - QAbstractSocket* abstractSocket() const; + QAbstractSocket *abstractSocket() const; - void setUseSsl(bool state); - void setAuth(const QString &user, const QString &pass=""); - bool isActive() const; - void post(const QString &proxyHost, int proxyPort, const QUrl &url, const QByteArray &data, bool asProxy=true); - void stop(); + void setUseSsl(bool state); + void setAuth(const QString &user, const QString &pass = ""); + bool isActive() const; + void post(const QString &proxyHost, quint16 proxyPort, const QUrl &url, const QByteArray &data, + bool asProxy = true); + void stop(); QByteArray body() const; - QString getHeader(const QString &) const; + QString getHeader(const QString &) const; signals: void result(); @@ -105,24 +103,23 @@ private slots: class Private; Private *d; - void resetConnection(bool clear=false); + void resetConnection(bool clear = false); void processData(const QByteArray &block); }; -class HttpProxyGetStream : public QObject -{ +class HttpProxyGetStream : public QObject { Q_OBJECT public: enum Error { ErrConnectionRefused, ErrHostNotFound, ErrSocket, ErrProxyConnect, ErrProxyNeg, ErrProxyAuth }; - HttpProxyGetStream(QObject *parent=0); + HttpProxyGetStream(QObject *parent = nullptr); ~HttpProxyGetStream(); - void setAuth(const QString &user, const QString &pass=""); - bool isActive() const; - void get(const QString &proxyHost, int proxyPort, const QString &url, bool ssl=false, bool asProxy=false); - void stop(); + void setAuth(const QString &user, const QString &pass = ""); + bool isActive() const; + void get(const QString &proxyHost, int proxyPort, const QString &url, bool ssl = false, bool asProxy = false); + void stop(); QString getHeader(const QString &) const; - int length() const; // -1 for unknown + int length() const; // -1 for unknown signals: void handshaken(); @@ -144,10 +141,10 @@ private slots: class Private; Private *d; - void resetConnection(bool clear=false); + void resetConnection(bool clear = false); void processData(const QByteArray &block); }; // CS_NAMESPACE_END -#endif +#endif // CS_HTTPPOLL_H diff --git a/src/irisnet/noncore/cutestuff/socks.cpp b/src/irisnet/noncore/cutestuff/socks.cpp index 7dd8f4f6..0dec764a 100644 --- a/src/irisnet/noncore/cutestuff/socks.cpp +++ b/src/irisnet/noncore/cutestuff/socks.cpp @@ -19,33 +19,35 @@ #include "socks.h" +#include "bsocket.h" + +#include #include -#include -#include #include #include -#include +#include +#include +#include +#include +#include + +// #define PROX_DEBUG +#ifdef PROX_DEBUG +#include +#endif #ifdef Q_OS_UNIX -#include +#include #include +#include +#include #endif #ifdef Q_OS_WIN32 #include +#ifdef _MSC_VER +#include #endif - -#ifdef Q_OS_UNIX -#include -#include -#endif - -#include "bsocket.h" - -//#define PROX_DEBUG - -#ifdef PROX_DEBUG -#include #endif // CS_NAMESPACE_BEGIN @@ -54,26 +56,25 @@ // SocksUDP //---------------------------------------------------------------------------- -class SocksUDP::Private -{ +class SocksUDP::Private { public: - QUdpSocket *sd; + QUdpSocket *sd; SocksClient *sc; QHostAddress routeAddr; - int routePort; - QString host; - int port; + quint16 routePort; + QString host; + int port; }; -SocksUDP::SocksUDP(SocksClient *sc, const QString &host, int port, const QHostAddress &routeAddr, int routePort) -:QObject(sc) +SocksUDP::SocksUDP(SocksClient *sc, const QString &host, int port, const QHostAddress &routeAddr, int routePort) : + QObject(sc) { - d = new Private; + d = new Private; d->sc = sc; d->sd = new QUdpSocket(this); connect(d->sd, SIGNAL(readyRead()), SLOT(sd_readyRead())); - d->host = host; - d->port = port; + d->host = host; + d->port = port; d->routeAddr = routeAddr; d->routePort = routePort; } @@ -90,31 +91,28 @@ void SocksUDP::change(const QString &host, int port) d->port = port; } -void SocksUDP::write(const QByteArray &data) -{ - d->sd->writeDatagram(data, d->routeAddr, d->routePort); -} +void SocksUDP::write(const QByteArray &data) { d->sd->writeDatagram(data, d->routeAddr, d->routePort); } void SocksUDP::sd_activated() { while (d->sd->hasPendingDatagrams()) { QByteArray datagram; - datagram.resize(d->sd->pendingDatagramSize()); + datagram.resize(int(d->sd->pendingDatagramSize())); d->sd->readDatagram(datagram.data(), datagram.size()); - packetReady(datagram); + emit packetReady(datagram); } } //---------------------------------------------------------------------------- // SocksClient //---------------------------------------------------------------------------- -#define REQ_CONNECT 0x01 -#define REQ_BIND 0x02 +#define REQ_CONNECT 0x01 +#define REQ_BIND 0x02 #define REQ_UDPASSOCIATE 0x03 -#define RET_SUCCESS 0x00 -#define RET_UNREACHABLE 0x04 -#define RET_CONNREFUSED 0x05 +#define RET_SUCCESS 0x00 +#define RET_UNREACHABLE 0x04 +#define RET_CONNREFUSED 0x05 // spc = socks packet client // sps = socks packet server @@ -125,7 +123,7 @@ void SocksUDP::sd_activated() static QByteArray spc_set_version(bool hasCreds) { QByteArray ver; - ver.resize(hasCreds? 4 : 3); + ver.resize(hasCreds ? 4 : 3); ver[0] = 0x05; // socks version 5 ver[2] = 0x00; // no-auth if (hasCreds) { @@ -146,46 +144,44 @@ static QByteArray sps_set_version(int method) return ver; } -struct SPCS_VERSION -{ +struct SPCS_VERSION { unsigned char version; - QByteArray methodList; + QByteArray methodList; }; static int spc_get_version(QByteArray &from, SPCS_VERSION *s) { - if(from.size() < 1) + if (from.size() < 1) return 0; - if(from.at(0) != 0x05) // only SOCKS5 supported + if (from.at(0) != 0x05) // only SOCKS5 supported return -1; - if(from.size() < 2) + if (from.size() < 2) return 0; - unsigned char mlen = from.at(1); - int num = mlen; - if(num > 16) // who the heck has over 16 auth methods?? + auto mlen = static_cast(from.at(1)); + int num = mlen; + if (num > 16) // who the heck has over 16 auth methods?? return -1; - if(from.size() < 2 + num) + if (from.size() < 2 + num) return 0; - QByteArray a = ByteStream::takeArray(from, 2+num); - s->version = a[0]; + QByteArray a = ByteStream::takeArray(from, 2 + num); + s->version = static_cast(a[0]); s->methodList.resize(num); - memcpy(s->methodList.data(), a.data() + 2, num); + memcpy(s->methodList.data(), a.data() + 2, size_t(num)); return 1; } -struct SPSS_VERSION -{ +struct SPSS_VERSION { unsigned char version; unsigned char method; }; static int sps_get_version(QByteArray &from, SPSS_VERSION *s) { - if(from.size() < 2) + if (from.size() < 2) return 0; QByteArray a = ByteStream::takeArray(from, 2); - s->version = a[0]; - s->method = a[1]; + s->version = static_cast(a[0]); + s->method = static_cast(a[1]); return 1; } @@ -194,17 +190,17 @@ static QByteArray spc_set_authUsername(const QByteArray &user, const QByteArray { int len1 = user.length(); int len2 = pass.length(); - if(len1 > 255) + if (len1 > 255) len1 = 255; - if(len2 > 255) + if (len2 > 255) len2 = 255; QByteArray a; - a.resize(1+1+len1+1+len2); + a.resize(1 + 1 + len1 + 1 + len2); a[0] = 0x01; // username auth version 1 a[1] = len1; - memcpy(a.data() + 2, user.data(), len1); - a[2+len1] = len2; - memcpy(a.data() + 3 + len1, pass.data(), len2); + memcpy(a.data() + 2, user.data(), size_t(len1)); + a[2 + len1] = len2; + memcpy(a.data() + 3 + len1, pass.data(), size_t(len2)); return a; } @@ -217,80 +213,78 @@ static QByteArray sps_set_authUsername(bool success) return a; } -struct SPCS_AUTHUSERNAME -{ +struct SPCS_AUTHUSERNAME { QString user, pass; }; static int spc_get_authUsername(QByteArray &from, SPCS_AUTHUSERNAME *s) { - if(from.size() < 1) + if (from.size() < 1) return 0; - unsigned char ver = from.at(0); - if(ver != 0x01) + unsigned char ver = static_cast(from.at(0)); + if (ver != 0x01) return -1; - if(from.size() < 2) + if (from.size() < 2) return 0; - unsigned char ulen = from.at(1); - if((int)from.size() < ulen + 3) + unsigned char ulen = static_cast(from.at(1)); + if ((int)from.size() < ulen + 3) return 0; - unsigned char plen = from.at(ulen+2); - if((int)from.size() < ulen + plen + 3) + unsigned char plen = static_cast(from.at(ulen + 2)); + if ((int)from.size() < ulen + plen + 3) return 0; QByteArray a = ByteStream::takeArray(from, ulen + plen + 3); QByteArray user, pass; user.resize(ulen); pass.resize(plen); - memcpy(user.data(), a.data()+2, ulen); - memcpy(pass.data(), a.data()+ulen+3, plen); + memcpy(user.data(), a.data() + 2, ulen); + memcpy(pass.data(), a.data() + ulen + 3, plen); s->user = QString::fromUtf8(user); s->pass = QString::fromUtf8(pass); return 1; } -struct SPSS_AUTHUSERNAME -{ +struct SPSS_AUTHUSERNAME { unsigned char version; - bool success; + bool success; }; static int sps_get_authUsername(QByteArray &from, SPSS_AUTHUSERNAME *s) { - if(from.size() < 2) + if (from.size() < 2) return 0; QByteArray a = ByteStream::takeArray(from, 2); - s->version = a[0]; - s->success = ((char) a[1] == 0 ? true: false); + s->version = static_cast(a[0]); + s->success = (char)a[1] == 0; return 1; } // connectRequest static QByteArray sp_set_request(const QHostAddress &addr, unsigned short port, unsigned char cmd1) { - int at = 0; + int at = 0; QByteArray a; a.resize(4); a[at++] = 0x05; // socks version 5 a[at++] = cmd1; a[at++] = 0x00; // reserved - if(addr.protocol() == QAbstractSocket::IPv4Protocol || addr.protocol() == QAbstractSocket::UnknownNetworkLayerProtocol) { - a[at++] = 0x01; // address type = ipv4 + if (addr.protocol() == QAbstractSocket::IPv4Protocol + || addr.protocol() == QAbstractSocket::UnknownNetworkLayerProtocol) { + a[at++] = 0x01; // address type = ipv4 quint32 ip4 = htonl(addr.toIPv4Address()); - a.resize(at+4); + a.resize(at + 4); memcpy(a.data() + at, &ip4, 4); at += 4; - } - else { - a[at++] = 0x04; + } else { + a[at++] = 0x04; Q_IPV6ADDR ip6 = addr.toIPv6Address(); - a.resize(at+16); - for(int i = 0; i < 16; ++i) + a.resize(at + 16); + for (int i = 0; i < 16; ++i) a[at++] = ip6[i]; } // port - a.resize(at+2); + a.resize(at + 2); quint16 p = htons(port); memcpy(a.data() + at, &p, 2); @@ -301,15 +295,15 @@ static QByteArray sp_set_request(const QString &host, quint16 port, unsigned cha { // detect for IP addresses QHostAddress addr; - if(addr.setAddress(host)) + if (addr.setAddress(host)) return sp_set_request(addr, port, cmd1); QByteArray h = host.toUtf8(); h.truncate(255); - h = QString::fromUtf8(h).toUtf8(); // delete any partial characters? + h = QString::fromUtf8(h).toUtf8(); // delete any partial characters? int hlen = h.length(); - int at = 0; + int at = 0; QByteArray a; a.resize(4); a[at++] = 0x05; // socks version 5 @@ -318,63 +312,60 @@ static QByteArray sp_set_request(const QString &host, quint16 port, unsigned cha a[at++] = 0x03; // address type = domain // host - a.resize(at+hlen+1); + a.resize(at + hlen + 1); a[at++] = hlen; - memcpy(a.data() + at, h.data(), hlen); + memcpy(a.data() + at, h.data(), size_t(hlen)); at += hlen; // port - a.resize(at+2); + a.resize(at + 2); unsigned short p = htons(port); memcpy(a.data() + at, &p, 2); return a; } -struct SPS_CONNREQ -{ +struct SPS_CONNREQ { unsigned char version; unsigned char cmd; - int address_type; - QString host; - QHostAddress addr; - quint16 port; + int address_type; + QString host; + QHostAddress addr; + quint16 port; }; static int sp_get_request(QByteArray &from, SPS_CONNREQ *s) { int full_len = 4; - if((int)from.size() < full_len) + if ((int)from.size() < full_len) return 0; - QString host; + QString host; QHostAddress addr; - unsigned char atype = from.at(3); + auto atype = static_cast(from.at(3)); - if(atype == 0x01) { + if (atype == 0x01) { full_len += 4; - if((int)from.size() < full_len) + if ((int)from.size() < full_len) return 0; quint32 ip4; memcpy(&ip4, from.data() + 4, 4); addr.setAddress(ntohl(ip4)); - } - else if(atype == 0x03) { + } else if (atype == 0x03) { ++full_len; - if((int)from.size() < full_len) + if ((int)from.size() < full_len) return 0; - unsigned char host_len = from.at(4); + auto host_len = static_cast(from.at(4)); full_len += host_len; - if((int)from.size() < full_len) + if ((int)from.size() < full_len) return 0; QByteArray cs; cs.resize(host_len); memcpy(cs.data(), from.data() + 5, host_len); host = QString::fromLatin1(cs); - } - else if(atype == 0x04) { + } else if (atype == 0x04) { full_len += 16; - if((int)from.size() < full_len) + if ((int)from.size() < full_len) return 0; quint8 a6[16]; memcpy(a6, from.data() + 4, 16); @@ -382,7 +373,7 @@ static int sp_get_request(QByteArray &from, SPS_CONNREQ *s) } full_len += 2; - if((int)from.size() < full_len) + if ((int)from.size() < full_len) return 0; QByteArray a = ByteStream::takeArray(from, full_len); @@ -390,63 +381,57 @@ static int sp_get_request(QByteArray &from, SPS_CONNREQ *s) quint16 p; memcpy(&p, a.data() + full_len - 2, 2); - s->version = a[0]; - s->cmd = a[1]; + s->version = static_cast(a[0]); + s->cmd = static_cast(a[1]); s->address_type = atype; - s->host = host; - s->addr = addr; - s->port = ntohs(p); + s->host = host; + s->addr = addr; + s->port = ntohs(p); return 1; } enum { StepVersion, StepAuth, StepRequest }; -class SocksClient::Private -{ +class SocksClient::Private { public: - Private(SocksClient *_q) : - sock(_q) - { - } + Private(SocksClient *_q) : sock(_q) { } BSocket sock; QString host; - int port; + int port; QString user, pass; QString real_host; - int real_port; + quint16 real_port; QByteArray recvBuf; - int step; - int authMethod; - bool incoming, waiting; + int step; + int authMethod; + bool incoming, waiting; QString rhost; - int rport; + quint16 rport; int pending; - bool udp; + bool udp; QString udpAddr; - int udpPort; + quint16 udpPort; }; -SocksClient::SocksClient(QObject *parent) -:ByteStream(parent) +SocksClient::SocksClient(QObject *parent) : ByteStream(parent) { init(); d->incoming = false; } -SocksClient::SocksClient(QTcpSocket *s, QObject *parent) -:ByteStream(parent) +SocksClient::SocksClient(QTcpSocket *s, QObject *parent) : ByteStream(parent) { init(); d->incoming = true; - d->waiting = true; + d->waiting = true; d->sock.setSocket(s); } @@ -469,20 +454,17 @@ SocksClient::~SocksClient() delete d; } -QAbstractSocket* SocksClient::abstractSocket() const -{ - return d->sock.abstractSocket(); -} +QAbstractSocket *SocksClient::abstractSocket() const { return d->sock.abstractSocket(); } void SocksClient::resetConnection(bool clear) { - if(d->sock.state() != BSocket::Idle) + if (d->sock.state() != BSocket::Idle) d->sock.close(); - if(clear) + if (clear) clearReadBuffer(); d->recvBuf.resize(0); d->waiting = false; - d->udp = false; + d->udp = false; d->pending = 0; if (bytesAvailable()) { setOpenMode(QIODevice::ReadOnly); @@ -491,10 +473,7 @@ void SocksClient::resetConnection(bool clear) } } -bool SocksClient::isIncoming() const -{ - return d->incoming; -} +bool SocksClient::isIncoming() const { return d->incoming; } void SocksClient::setAuth(const QString &user, const QString &pass) { @@ -506,26 +485,26 @@ void SocksClient::connectToHost(const QString &proxyHost, int proxyPort, const Q { resetConnection(true); - d->host = proxyHost; - d->port = proxyPort; + d->host = proxyHost; + d->port = proxyPort; d->real_host = host; d->real_port = port; - d->udp = udpMode; + d->udp = udpMode; #ifdef PROX_DEBUG fprintf(stderr, "SocksClient: Connecting to %s:%d", qPrintable(proxyHost), proxyPort); - if(d->user.isEmpty()) + if (d->user.isEmpty()) fprintf(stderr, "\n"); else fprintf(stderr, ", auth {%s,%s}\n", qPrintable(d->user), qPrintable(d->pass)); #endif - d->sock.connectToHost(d->host, d->port); + d->sock.connectToHost(d->host, quint16(d->port)); } void SocksClient::close() { d->sock.close(); - if(d->sock.bytesToWrite() == 0) + if (d->sock.bytesToWrite() == 0) resetConnection(); } @@ -534,7 +513,7 @@ void SocksClient::writeData(const QByteArray &buf) #ifdef PROX_DEBUG // show hex fprintf(stderr, "SocksClient: client write { "); - for(int n = 0; n < (int)buf.size(); ++n) + for (int n = 0; n < (int)buf.size(); ++n) fprintf(stderr, "%02X ", (unsigned char)buf[n]); fprintf(stderr, " } \n"); #endif @@ -544,7 +523,7 @@ void SocksClient::writeData(const QByteArray &buf) qint64 SocksClient::writeData(const char *data, qint64 maxSize) { - if(isOpen() && !d->udp) + if (isOpen() && !d->udp) return d->sock.write(data, maxSize); return 0; } @@ -558,14 +537,11 @@ qint64 SocksClient::readData(char *data, qint64 maxSize) return ret; } -qint64 SocksClient::bytesAvailable() const -{ - return ByteStream::bytesAvailable(); -} +qint64 SocksClient::bytesAvailable() const { return ByteStream::bytesAvailable(); } qint64 SocksClient::bytesToWrite() const { - if(isOpen()) + if (isOpen()) return d->sock.bytesToWrite(); else return 0; @@ -583,20 +559,19 @@ void SocksClient::sock_connected() void SocksClient::sock_connectionClosed() { - if(isOpen()) { + if (isOpen()) { resetConnection(); emit connectionClosed(); - } - else { + } else { setError(ErrProxyNeg); } } void SocksClient::sock_delayedCloseFinished() { - if(isOpen()) { + if (isOpen()) { resetConnection(); - delayedCloseFinished(); + emit delayedCloseFinished(); } } @@ -604,16 +579,15 @@ void SocksClient::sock_readyRead() { QByteArray block = d->sock.readAll(); - //qDebug() << this << "::sock_readyRead " << block.size() << " bytes." << + // qDebug() << this << "::sock_readyRead " << block.size() << " bytes." << // "udp=" << d->udp << openMode(); - if(!isOpen()) { - if(d->incoming) + if (!isOpen()) { + if (d->incoming) processIncoming(block); else processOutgoing(block); - } - else { - if(!d->udp) { + } else { + if (!d->udp) { appendRead(block); emit readyRead(); } @@ -625,22 +599,21 @@ void SocksClient::processOutgoing(const QByteArray &block) #ifdef PROX_DEBUG // show hex fprintf(stderr, "SocksClient: client recv { "); - for(int n = 0; n < (int)block.size(); ++n) + for (int n = 0; n < (int)block.size(); ++n) fprintf(stderr, "%02X ", (unsigned char)block[n]); fprintf(stderr, " } \n"); #endif d->recvBuf += block; - if(d->step == StepVersion) { + if (d->step == StepVersion) { SPSS_VERSION s; - int r = sps_get_version(d->recvBuf, &s); - if(r == -1) { + int r = sps_get_version(d->recvBuf, &s); + if (r == -1) { resetConnection(true); setError(ErrProxyNeg); return; - } - else if(r == 1) { - if(s.version != 0x05 || s.method == 0xff) { + } else if (r == 1) { + if (s.version != 0x05 || s.method == 0xff) { #ifdef PROX_DEBUG fprintf(stderr, "SocksClient: Method selection failed\n"); #endif @@ -650,15 +623,13 @@ void SocksClient::processOutgoing(const QByteArray &block) } QString str; - if(s.method == 0x00) { - str = "None"; + if (s.method == 0x00) { + str = "None"; d->authMethod = AuthNone; - } - else if(s.method == 0x02) { - str = "Username/Password"; + } else if (s.method == 0x02) { + str = "Username/Password"; d->authMethod = AuthUsername; - } - else { + } else { #ifdef PROX_DEBUG fprintf(stderr, "SocksClient: Server wants to use unknown method '%02x'\n", s.method); #endif @@ -667,11 +638,10 @@ void SocksClient::processOutgoing(const QByteArray &block) return; } - if(d->authMethod == AuthNone) { + if (d->authMethod == AuthNone) { // no auth, go straight to the request do_request(); - } - else if(d->authMethod == AuthUsername) { + } else if (d->authMethod == AuthUsername) { d->step = StepAuth; #ifdef PROX_DEBUG fprintf(stderr, "SocksClient: Authenticating [Username] ...\n"); @@ -680,22 +650,21 @@ void SocksClient::processOutgoing(const QByteArray &block) } } } - if(d->step == StepAuth) { - if(d->authMethod == AuthUsername) { + if (d->step == StepAuth) { + if (d->authMethod == AuthUsername) { SPSS_AUTHUSERNAME s; - int r = sps_get_authUsername(d->recvBuf, &s); - if(r == -1) { + int r = sps_get_authUsername(d->recvBuf, &s); + if (r == -1) { resetConnection(true); setError(ErrProxyNeg); return; - } - else if(r == 1) { - if(s.version != 0x01) { + } else if (r == 1) { + if (s.version != 0x01) { resetConnection(true); setError(ErrProxyNeg); return; } - if(!s.success) { + if (!s.success) { resetConnection(true); setError(ErrProxyAuth); return; @@ -704,24 +673,22 @@ void SocksClient::processOutgoing(const QByteArray &block) do_request(); } } - } - else if(d->step == StepRequest) { + } else if (d->step == StepRequest) { SPS_CONNREQ s; - int r = sp_get_request(d->recvBuf, &s); - if(r == -1) { + int r = sp_get_request(d->recvBuf, &s); + if (r == -1) { resetConnection(true); setError(ErrProxyNeg); return; - } - else if(r == 1) { - if(s.cmd != RET_SUCCESS) { + } else if (r == 1) { + if (s.cmd != RET_SUCCESS) { #ifdef PROX_DEBUG fprintf(stderr, "SocksClient: client << Error >> [%02x]\n", s.cmd); #endif resetConnection(true); - if(s.cmd == RET_UNREACHABLE) + if (s.cmd == RET_UNREACHABLE) setError(ErrHostNotFound); - else if(s.cmd == RET_CONNREFUSED) + else if (s.cmd == RET_CONNREFUSED) setError(ErrConnectionRefused); else setError(ErrProxyNeg); @@ -731,8 +698,8 @@ void SocksClient::processOutgoing(const QByteArray &block) #ifdef PROX_DEBUG fprintf(stderr, "SocksClient: client << Success >>\n"); #endif - if(d->udp) { - if(s.address_type == 0x03) + if (d->udp) { + if (s.address_type == 0x03) d->udpAddr = s.host; else d->udpAddr = s.addr.toString(); @@ -744,13 +711,13 @@ void SocksClient::processOutgoing(const QByteArray &block) QPointer self = this; setOpenMode(QIODevice::ReadWrite); emit connected(); - if(!self) + if (!self) return; - if(!d->recvBuf.isEmpty()) { + if (!d->recvBuf.isEmpty()) { appendRead(d->recvBuf); d->recvBuf.resize(0); - readyRead(); + emit readyRead(); } } } @@ -761,10 +728,10 @@ void SocksClient::do_request() #ifdef PROX_DEBUG fprintf(stderr, "SocksClient: Requesting ...\n"); #endif - d->step = StepRequest; - int cmd = d->udp ? REQ_UDPASSOCIATE : REQ_CONNECT; + d->step = StepRequest; + auto cmd = d->udp ? REQ_UDPASSOCIATE : REQ_CONNECT; QByteArray buf; - if(!d->real_host.isEmpty()) + if (!d->real_host.isEmpty()) buf = sp_set_request(d->real_host, d->real_port, cmd); else buf = sp_set_request(QHostAddress(), 0, cmd); @@ -773,32 +740,30 @@ void SocksClient::do_request() void SocksClient::sock_bytesWritten(qint64 x) { - int bytes = x; - if(d->pending >= bytes) { + int bytes = int(x); + if (d->pending >= bytes) { d->pending -= bytes; bytes = 0; - } - else { + } else { bytes -= d->pending; d->pending = 0; } - if(bytes > 0) - bytesWritten(bytes); + if (bytes > 0) + emit bytesWritten(bytes); } void SocksClient::sock_error(int x) { - if(isOpen()) { + if (isOpen()) { resetConnection(); setError(ErrRead); - } - else { + } else { resetConnection(true); - if(x == BSocket::ErrHostNotFound) + if (x == BSocket::ErrHostNotFound) setError(ErrProxyConnect); - else if(x == BSocket::ErrConnectionRefused) + else if (x == BSocket::ErrConnectionRefused) setError(ErrProxyConnect); - else if(x == BSocket::ErrRead) + else if (x == BSocket::ErrRead) setError(ErrProxyNeg); } } @@ -806,7 +771,7 @@ void SocksClient::sock_error(int x) void SocksClient::serve() { d->waiting = false; - d->step = StepVersion; + d->step = StepVersion; continueIncoming(); } @@ -815,83 +780,77 @@ void SocksClient::processIncoming(const QByteArray &block) #ifdef PROX_DEBUG // show hex fprintf(stderr, "SocksClient: server recv { "); - for(int n = 0; n < (int)block.size(); ++n) + for (int n = 0; n < (int)block.size(); ++n) fprintf(stderr, "%02X ", (unsigned char)block[n]); fprintf(stderr, " } \n"); #endif d->recvBuf += block; - if(!d->waiting) + if (!d->waiting) continueIncoming(); } void SocksClient::continueIncoming() { - if(d->recvBuf.isEmpty()) + if (d->recvBuf.isEmpty()) return; - if(d->step == StepVersion) { + if (d->step == StepVersion) { SPCS_VERSION s; - int r = spc_get_version(d->recvBuf, &s); - if(r == -1) { + int r = spc_get_version(d->recvBuf, &s); + if (r == -1) { resetConnection(true); setError(ErrProxyNeg); return; - } - else if(r == 1) { - if(s.version != 0x05) { + } else if (r == 1) { + if (s.version != 0x05) { resetConnection(true); setError(ErrProxyNeg); return; } int methods = 0; - for(int n = 0; n < (int)s.methodList.size(); ++n) { - unsigned char c = s.methodList[n]; - if(c == 0x00) + for (int n = 0; n < (int)s.methodList.size(); ++n) { + unsigned char c = static_cast(s.methodList[n]); + if (c == 0x00) methods |= AuthNone; - else if(c == 0x02) + else if (c == 0x02) methods |= AuthUsername; } d->waiting = true; emit incomingMethods(methods); } - } - else if(d->step == StepAuth) { + } else if (d->step == StepAuth) { SPCS_AUTHUSERNAME s; - int r = spc_get_authUsername(d->recvBuf, &s); - if(r == -1) { + int r = spc_get_authUsername(d->recvBuf, &s); + if (r == -1) { resetConnection(true); setError(ErrProxyNeg); return; - } - else if(r == 1) { + } else if (r == 1) { d->waiting = true; - incomingAuth(s.user, s.pass); + emit incomingAuth(s.user, s.pass); } - } - else if(d->step == StepRequest) { + } else if (d->step == StepRequest) { SPS_CONNREQ s; - int r = sp_get_request(d->recvBuf, &s); - if(r == -1) { + int r = sp_get_request(d->recvBuf, &s); + if (r == -1) { resetConnection(true); setError(ErrProxyNeg); return; - } - else if(r == 1) { + } else if (r == 1) { d->waiting = true; - if(s.cmd == REQ_CONNECT) { - if(!s.host.isEmpty()) + if (s.cmd == REQ_CONNECT) { + if (!s.host.isEmpty()) d->rhost = s.host; else d->rhost = s.addr.toString(); d->rport = s.port; - incomingConnectRequest(d->rhost, d->rport); - } - else if(s.cmd == REQ_UDPASSOCIATE) { - incomingUDPAssociateRequest(); - } - else { + QIODevice::open(QIODevice::ReadWrite); + emit incomingConnectRequest(d->rhost, d->rport); + } else if (s.cmd == REQ_UDPASSOCIATE) { + emit incomingUDPAssociateRequest(); + } else { requestDeny(); return; } @@ -901,17 +860,16 @@ void SocksClient::continueIncoming() void SocksClient::chooseMethod(int method) { - if(d->step != StepVersion || !d->waiting) + if (d->step != StepVersion || !d->waiting) return; unsigned char c; - if(method == AuthNone) { + if (method == AuthNone) { d->step = StepRequest; - c = 0x00; - } - else { + c = 0x00; + } else { d->step = StepAuth; - c = 0x02; + c = 0x02; } // version response @@ -922,16 +880,16 @@ void SocksClient::chooseMethod(int method) void SocksClient::authGrant(bool b) { - if(d->step != StepAuth || !d->waiting) + if (d->step != StepAuth || !d->waiting) return; - if(b) + if (b) d->step = StepRequest; // auth response d->waiting = false; writeData(sps_set_authUsername(b)); - if(!b) { + if (!b) { resetConnection(true); return; } @@ -940,7 +898,7 @@ void SocksClient::authGrant(bool b) void SocksClient::requestDeny() { - if(d->step != StepRequest || !d->waiting) + if (d->step != StepRequest || !d->waiting) return; // response @@ -951,7 +909,7 @@ void SocksClient::requestDeny() void SocksClient::grantConnect() { - if(d->step != StepRequest || !d->waiting) + if (d->step != StepRequest || !d->waiting) return; // response @@ -962,16 +920,16 @@ void SocksClient::grantConnect() fprintf(stderr, "SocksClient: server << Success >>\n"); #endif - if(!d->recvBuf.isEmpty()) { + if (!d->recvBuf.isEmpty()) { appendRead(d->recvBuf); d->recvBuf.resize(0); - readyRead(); + emit readyRead(); } } -void SocksClient::grantUDPAssociate(const QString &relayHost, int relayPort) +void SocksClient::grantUDPAssociate(const QString &relayHost, quint16 relayPort) { - if(d->step != StepRequest || !d->waiting) + if (d->step != StepRequest || !d->waiting) return; // response @@ -983,29 +941,17 @@ void SocksClient::grantUDPAssociate(const QString &relayHost, int relayPort) fprintf(stderr, "SocksClient: server << Success >>\n"); #endif - if(!d->recvBuf.isEmpty()) + if (!d->recvBuf.isEmpty()) d->recvBuf.resize(0); } -QHostAddress SocksClient::peerAddress() const -{ - return d->sock.peerAddress(); -} +QHostAddress SocksClient::peerAddress() const { return d->sock.peerAddress(); } -quint16 SocksClient::peerPort() const -{ - return d->sock.peerPort(); -} +quint16 SocksClient::peerPort() const { return d->sock.peerPort(); } -QString SocksClient::udpAddress() const -{ - return d->udpAddr; -} +QString SocksClient::udpAddress() const { return d->udpAddr; } -quint16 SocksClient::udpPort() const -{ - return d->udpPort; -} +quint16 SocksClient::udpPort() const { return d->udpPort; } SocksUDP *SocksClient::createUDP(const QString &host, int port, const QHostAddress &routeAddr, int routePort) { @@ -1015,26 +961,14 @@ SocksUDP *SocksClient::createUDP(const QString &host, int port, const QHostAddre //---------------------------------------------------------------------------- // SocksServer //---------------------------------------------------------------------------- -class SocksServer::Private -{ +class SocksServer::Private { public: - Private(SocksServer *_q) : - serv(_q) - { - } - - QTcpServer serv; - QList incomingConns; - QUdpSocket *sd; + QTcpServer *serv = nullptr; + QList incomingConns; + QUdpSocket *sd = nullptr; }; -SocksServer::SocksServer(QObject *parent) -:QObject(parent) -{ - d = new Private(this); - d->sd = 0; - connect(&d->serv, SIGNAL(newConnection()), SLOT(newConnection())); -} +SocksServer::SocksServer(QObject *parent) : QObject(parent) { d = new Private; } SocksServer::~SocksServer() { @@ -1045,22 +979,29 @@ SocksServer::~SocksServer() delete d; } -bool SocksServer::isActive() const +void SocksServer::setServerSocket(QTcpServer *server) { - return d->serv.isListening(); + d->serv = server; + connect(d->serv, SIGNAL(newConnection()), SLOT(newConnection())); } +bool SocksServer::isActive() const { return d->serv->isListening(); } + bool SocksServer::listen(quint16 port, bool udp) { stop(); - if(!d->serv.listen(QHostAddress::Any, port)) + if (!d->serv) { + setServerSocket(new QTcpServer(this)); + } + if (!d->serv->listen(QHostAddress::Any, port)) return false; - if(udp) { + if (udp) { d->sd = new QUdpSocket(this); - if(!d->sd->bind(QHostAddress::LocalHost, port)) { + if (!d->sd->bind(QHostAddress::LocalHost, port)) { delete d->sd; - d->sd = 0; - d->serv.close(); + d->sd = nullptr; + delete d->serv; + d->serv = nullptr; return false; } connect(d->sd, SIGNAL(readyRead()), SLOT(sd_activated())); @@ -1071,24 +1012,19 @@ bool SocksServer::listen(quint16 port, bool udp) void SocksServer::stop() { delete d->sd; - d->sd = 0; - d->serv.close(); + d->sd = nullptr; + delete d->serv; + d->serv = nullptr; } -int SocksServer::port() const -{ - return d->serv.serverPort(); -} +int SocksServer::port() const { return d->serv ? d->serv->serverPort() : 0; } -QHostAddress SocksServer::address() const -{ - return d->serv.serverAddress(); -} +QHostAddress SocksServer::address() const { return d->serv ? d->serv->serverAddress() : QHostAddress(); } SocksClient *SocksServer::takeIncoming() { - if(d->incomingConns.isEmpty()) - return 0; + if (d->incomingConns.isEmpty()) + return nullptr; SocksClient *c = d->incomingConns.takeFirst(); @@ -1101,19 +1037,19 @@ SocksClient *SocksServer::takeIncoming() return c; } -void SocksServer::writeUDP(const QHostAddress &addr, int port, const QByteArray &data) +void SocksServer::writeUDP(const QHostAddress &addr, quint16 port, const QByteArray &data) { - if(d->sd) { + if (d->sd) { d->sd->writeDatagram(data.data(), data.size(), addr, port); } } void SocksServer::newConnection() { - SocksClient *c = new SocksClient(d->serv.nextPendingConnection(), this); + SocksClient *c = new SocksClient(d->serv->nextPendingConnection(), this); connect(c, SIGNAL(error(int)), this, SLOT(connectionError())); d->incomingConns.append(c); - incomingReady(); + emit incomingReady(); } void SocksServer::connectionError() @@ -1126,12 +1062,14 @@ void SocksServer::connectionError() void SocksServer::sd_activated() { while (d->sd->hasPendingDatagrams()) { - QByteArray datagram; + QByteArray datagram(int(d->sd->pendingDatagramSize()), Qt::Uninitialized); QHostAddress sender; - quint16 senderPort; - datagram.resize(d->sd->pendingDatagramSize()); - d->sd->readDatagram(datagram.data(), datagram.size(), &sender, &senderPort); - incomingUDP(sender.toString(), senderPort, d->sd->peerAddress(), d->sd->peerPort(), datagram); + quint16 senderPort; + auto sz = d->sd->readDatagram(datagram.data(), datagram.size(), &sender, &senderPort); + if (sz >= 0) { + datagram.truncate(int(sz)); + emit incomingUDP(sender.toString(), senderPort, d->sd->peerAddress(), d->sd->peerPort(), datagram); + } } } diff --git a/src/irisnet/noncore/cutestuff/socks.h b/src/irisnet/noncore/cutestuff/socks.h index 6ca5efd6..b7a190ec 100644 --- a/src/irisnet/noncore/cutestuff/socks.h +++ b/src/irisnet/noncore/cutestuff/socks.h @@ -23,14 +23,13 @@ #include "bytestream.h" // CS_NAMESPACE_BEGIN - class QHostAddress; +class QTcpServer; class QTcpSocket; class SocksClient; class SocksServer; -class SocksUDP : public QObject -{ +class SocksUDP : public QObject { Q_OBJECT public: ~SocksUDP(); @@ -52,44 +51,43 @@ private slots: SocksUDP(SocksClient *sc, const QString &host, int port, const QHostAddress &routeAddr, int routePort); }; -class SocksClient : public ByteStream -{ +class SocksClient : public ByteStream { Q_OBJECT public: enum Error { ErrConnectionRefused = ErrCustom, ErrHostNotFound, ErrProxyConnect, ErrProxyNeg, ErrProxyAuth }; - enum Method { AuthNone=0x0001, AuthUsername=0x0002 }; + enum Method { AuthNone = 0x0001, AuthUsername = 0x0002 }; enum Request { ReqConnect, ReqUDPAssociate }; - SocksClient(QObject *parent=0); - SocksClient(QTcpSocket *, QObject *parent=0); + SocksClient(QObject *parent = nullptr); + SocksClient(QTcpSocket *, QObject *parent = nullptr); ~SocksClient(); - virtual QAbstractSocket* abstractSocket() const; + virtual QAbstractSocket *abstractSocket() const; bool isIncoming() const; // outgoing - void setAuth(const QString &user, const QString &pass=""); - void connectToHost(const QString &proxyHost, int proxyPort, const QString &host, int port, bool udpMode=false); + void setAuth(const QString &user, const QString &pass = ""); + void connectToHost(const QString &proxyHost, int proxyPort, const QString &host, int port, bool udpMode = false); // incoming void chooseMethod(int); void authGrant(bool); void requestDeny(); void grantConnect(); - void grantUDPAssociate(const QString &relayHost, int relayPort); + void grantUDPAssociate(const QString &relayHost, quint16 relayPort); // from ByteStream - void close(); + void close(); qint64 bytesAvailable() const; qint64 bytesToWrite() const; // remote address QHostAddress peerAddress() const; - quint16 peerPort() const; + quint16 peerPort() const; // udp - QString udpAddress() const; - quint16 udpPort() const; + QString udpAddress() const; + quint16 udpPort() const; SocksUDP *createUDP(const QString &host, int port, const QHostAddress &routeAddr, int routePort); protected: @@ -120,7 +118,7 @@ private slots: Private *d; void init(); - void resetConnection(bool clear=false); + void resetConnection(bool clear = false); void do_request(); void processOutgoing(const QByteArray &); void processIncoming(const QByteArray &); @@ -128,21 +126,21 @@ private slots: void writeData(const QByteArray &a); }; -class SocksServer : public QObject -{ +class SocksServer : public QObject { Q_OBJECT public: - SocksServer(QObject *parent=0); + SocksServer(QObject *parent = nullptr); ~SocksServer(); - bool isActive() const; - bool listen(quint16 port, bool udp=false); - void stop(); - int port() const; + void setServerSocket(QTcpServer *server); + bool isActive() const; + bool listen(quint16 port, bool udp = false); + void stop(); + int port() const; QHostAddress address() const; SocksClient *takeIncoming(); - void writeUDP(const QHostAddress &addr, int port, const QByteArray &data); + void writeUDP(const QHostAddress &addr, quint16 port, const QByteArray &data); signals: void incomingReady(); @@ -160,4 +158,4 @@ private slots: // CS_NAMESPACE_END -#endif +#endif // CS_SOCKS_H diff --git a/src/irisnet/noncore/dtls.cpp b/src/irisnet/noncore/dtls.cpp new file mode 100644 index 00000000..5816dd12 --- /dev/null +++ b/src/irisnet/noncore/dtls.cpp @@ -0,0 +1,406 @@ +/* + * Copyright (C) 2021 Sergey Ilinykh + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library. If not, see . + * + */ + +#include "dtls.h" +#include "xmpp_xmlcommon.h" + +#include + +#include +#if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0) +#include +#endif +#include + +#define DTLS_DEBUG(msg, ...) qDebug("dtls: " msg, ##__VA_ARGS__) + +/* +Connection flow + + juliet | romeo +--------------------------------------------------------------------------------------- +1 | setup:NotSet | setup:NotSet +2 | generate cert + compute fingerprint | +3 | setup:actpass | +4 | ----send fingerprint----> validate +5 | <-------iq result-------- +5 | | setup:active +6 | | generate cert + compute fingerprint +7 | validate <---send fingerprint----- +8 | setup:passive | +9 | start dtls server | +10| --------iq result-------> +11| | start dtls client +12|================================= DTLS HANDSHAKE =================================== +*/ + +namespace XMPP { + +static std::array fpRoles { { "active", "passive", "actpass", "holdconn" } }; + +QString Dtls::FingerPrint::ns() { return QStringLiteral("urn:xmpp:jingle:apps:dtls:0"); } + +bool Dtls::FingerPrint::parse(const QDomElement &el) +{ + if (el.namespaceURI() != ns()) { + qWarning("Unrecognized DTLS xmlns: %s. Parse it as if it were %s", qPrintable(el.namespaceURI()), + qPrintable(ns())); + } + auto ht = el.attribute(QLatin1String("hash")); + hash = QStringView { ht }; + hash.setData(QByteArray::fromHex(el.text().toLatin1())); + auto setupIt = std::find(fpRoles.begin(), fpRoles.end(), el.attribute(QLatin1String("setup")).toLatin1()); + setup = Setup(setupIt == fpRoles.end() ? NotSet : std::distance(fpRoles.begin(), setupIt) + 1); + return isValid(); +} + +QDomElement Dtls::FingerPrint::toXml(QDomDocument *doc) const +{ + Q_ASSERT(setup != NotSet); + auto binToHex = [](const QByteArray &in) { +#if QT_VERSION >= QT_VERSION_CHECK(5, 9, 0) + return in.toHex(':'); +#else + QByteArray out = in.toHex(); + int size = out.size(); + for (int k = 2; k < size; k += 3, ++size) { + out.insert(k, ':'); + } + return out; +#endif + }; + auto fingerprint + = XMLHelper::textTagNS(doc, ns(), QLatin1String("fingerprint"), QString::fromLatin1(binToHex(hash.data()))); + fingerprint.setAttribute(QLatin1String("hash"), hash.stringType()); + fingerprint.setAttribute(QLatin1String("setup"), QLatin1String(fpRoles[setup - 1])); + return fingerprint; +} + +class Dtls::Private : public QObject { + Q_OBJECT +public: + Dtls *q; + QCA::TLS *tls = nullptr; + QCA::PrivateKey pkey; + QCA::Certificate cert; + + QString localJid; + QString remoteJid; + // original fingerprints + FingerPrint localFingerprint; + FingerPrint remoteFingerprint; + + QAbstractSocket::SocketError lastError = QAbstractSocket::UnknownSocketError; + + Private(Dtls *q) : QObject(q), q(q) { } + + static Hash computeFingerprint(const QCA::Certificate &cert, Hash::Type hashType) + { + if (cert.isNull()) { + return Hash(); + } + return Hash::from(hashType, cert.toDER()); + } + + void tls_handshaken() + { + DTLS_DEBUG("tls handshaken"); + auto peerIdentity = tls->peerIdentityResult(); + if (peerIdentity == QCA::TLS::Valid || peerIdentity == QCA::TLS::InvalidCertificate) { + const auto chain = tls->peerCertificateChain(); + const auto &cert = chain.first(); + if (computeFingerprint(cert, remoteFingerprint.hash.type()) == remoteFingerprint.hash) { + DTLS_DEBUG("valid"); + tls->continueAfterStep(); + emit q->connected(); + return; + } else { + qWarning("dtls fingerprints do not match: %d", int(tls->peerIdentityResult())); + } + } else { + qWarning("dtls peerIdentity failure: %d", int(tls->peerIdentityResult())); + } + + lastError = QAbstractSocket::SslHandshakeFailedError; + tls->reset(); + emit q->errorOccurred(lastError); + } + + void tls_error() + { + DTLS_DEBUG("tls error: %d", tls->errorCode()); + switch (tls->errorCode()) { + case QCA::TLS::ErrorSignerExpired: + case QCA::TLS::ErrorSignerInvalid: + case QCA::TLS::ErrorCertKeyMismatch: + lastError = QAbstractSocket::SocketError::SslInvalidUserDataError; + break; + case QCA::TLS::ErrorInit: + lastError = QAbstractSocket::SocketError::SslInternalError; + break; + case QCA::TLS::ErrorHandshake: + lastError = QAbstractSocket::SocketError::SslHandshakeFailedError; + break; + case QCA::TLS::ErrorCrypt: + default: + lastError = QAbstractSocket::SocketError::UnknownSocketError; + break; + } + emit q->errorOccurred(lastError); + } + + void setRemoteFingerprint(const FingerPrint &fp) + { + bool needRestart = false; + if (tls) { + if (remoteFingerprint == fp) + return; + // need to restart dtls. see rfc8842 (todo: but in fact we need more checks) + needRestart = true; + localFingerprint.setup = Dtls::NotSet; + } + remoteFingerprint = fp; + if (needRestart) + emit q->needRestart(); + if (localFingerprint.setup == Dtls::NotSet) + return; // will be handled in acceptIncoming + + bool remoteActiveOrPassive + = remoteFingerprint.setup == Dtls::Passive || remoteFingerprint.setup == Dtls::Active; + if (localFingerprint.setup == Dtls::ActPass) { // response fingerprint + if (!remoteActiveOrPassive) { + qWarning("Unexpected remote fingerprint setup. Assume remote setup=active"); + remoteFingerprint.setup = Dtls::Active; + } + localFingerprint.setup = remoteFingerprint.setup == Dtls::Active ? Dtls::Passive : Dtls::Active; + if (localFingerprint.setup == Dtls::Passive) + negotiate(); + return; + } + // local is active or passive already, no idea in what scenario. probably something custom + bool roleConflict = remoteFingerprint.setup == localFingerprint.setup; + if (!roleConflict && !remoteActiveOrPassive) { + if (localFingerprint.setup == Dtls::Passive) + negotiate(); + return; // looks valid + } + if (roleConflict) + qWarning("setRemoteFingerprint: dtls role conflict"); + if (!remoteActiveOrPassive) + qWarning("setRemoteFingerprint: expected active or passive remote fingerprint but got something else"); + lastError = QAbstractSocket::OperationError; + emit q->errorOccurred(lastError); + } + + void acceptIncoming() + { + if (cert.isNull()) { + generateCertificate(); + } + Q_ASSERT(localFingerprint.setup == Dtls::NotSet); + if (remoteFingerprint.setup == Dtls::ActPass) { + localFingerprint.setup = Dtls::Active; + remoteFingerprint.setup = Dtls::Passive; + } else { + localFingerprint.setup = remoteFingerprint.setup == Dtls::Active ? Dtls::Passive : Dtls::Active; + } + if (localFingerprint.setup == Dtls::Passive) { + negotiate(); // start server + } + } + + void negotiate() + { + if (tls) { + delete tls; + } + + if (!remoteFingerprint.isValid()) { + qWarning("remote fingerprint is not set"); + lastError = QAbstractSocket::SocketError::OperationError; + emit q->errorOccurred(lastError); + return; + } + + tls = new QCA::TLS(QCA::TLS::Datagram); + tls->setCertificate(cert, pkey); + + connect(tls, &QCA::TLS::certificateRequested, tls, &QCA::TLS::continueAfterStep); + connect(tls, &QCA::TLS::handshaken, this, &Dtls::Private::tls_handshaken); + connect(tls, &QCA::TLS::readyRead, q, &Dtls::readyRead); + connect(tls, &QCA::TLS::readyReadOutgoing, q, &Dtls::readyReadOutgoing); + connect(tls, &QCA::TLS::closed, q, &Dtls::closed); + connect(tls, &QCA::TLS::error, this, &Dtls::Private::tls_error); + + if (localFingerprint.setup == Dtls::Passive) { + qDebug("Starting DTLS server"); + tls->startServer(); + } else { + qDebug("Starting DTLS client"); + tls->startClient(); + } + } + + void generateCertificate() + { + QCA::CertificateOptions opts; + + QCA::CertificateInfo info; + info.insert(QCA::CommonName, QStringLiteral("iris.psi-im.org")); + if (!localJid.isEmpty()) + info.insert(QCA::XMPP, localJid); + opts.setInfo(info); + +#if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0) + QCA::BigInteger sn(QRandomGenerator::global()->generate()); +#else + QCA::BigInteger sn(qrand()); +#endif + opts.setSerialNumber(sn); + + auto nowUTC = QDateTime::currentDateTimeUtc(); + opts.setValidityPeriod(nowUTC, nowUTC.addDays(30)); + + QCA::Constraints constraints = { { QCA::DigitalSignature, QCA::KeyEncipherment, QCA::DataEncipherment, + QCA::ClientAuth, QCA::ServerAuth } }; + opts.setConstraints(constraints); + opts.setAsCA(); + + pkey = QCA::KeyGenerator().createRSA(2048); + cert = QCA::Certificate(opts, pkey); + localFingerprint.hash = Private::computeFingerprint(cert, Hash::Sha256); + } +}; + +Dtls::Dtls(QObject *parent, const QString &localJid, const QString &remoteJid) : QObject(parent), d(new Private(this)) +{ + d->localJid = localJid; + d->remoteJid = remoteJid; + if (!isSupported()) { + qWarning("DTLS is not supported by your version of QCA"); + } +} + +void Dtls::setLocalCertificate(const QCA::Certificate &cert, const QCA::PrivateKey &pkey) +{ + Hash::Type hashType = Hash::Sha256; + switch (cert.signatureAlgorithm()) { + case QCA::EMSA1_SHA1: + case QCA::EMSA3_SHA1: + hashType = Hash::Sha1; + break; + case QCA::EMSA3_SHA256: + hashType = Hash::Sha256; + break; + case QCA::EMSA3_SHA512: + hashType = Hash::Sha512; + break; + default: + break; + } + + d->tls->setCertificate(cert, pkey); + d->localFingerprint.hash = Private::computeFingerprint(cert, hashType); +} + +QCA::Certificate Dtls::localCertificate() const { return d->cert; } + +QCA::Certificate Dtls::remoteCertificate() const +{ + if (!d->tls || d->tls->peerCertificateChain().isEmpty()) + return {}; + auto const &chain = d->tls->peerCertificateChain(); + return chain.first(); +} + +void Dtls::initOutgoing() +{ + if (d->cert.isNull()) { + d->generateCertificate(); + } + d->localFingerprint.setup = ActPass; +} + +void Dtls::acceptIncoming() { d->acceptIncoming(); } + +void Dtls::onRemoteAcceptedFingerprint() +{ + if (d->localFingerprint.setup == Active) { + d->negotiate(); + } +} + +const Dtls::FingerPrint &Dtls::localFingerprint() const { return d->localFingerprint; } + +const Dtls::FingerPrint &Dtls::remoteFingerprint() const { return d->remoteFingerprint; } + +void Dtls::setRemoteFingerprint(const FingerPrint &fp) { d->setRemoteFingerprint(fp); } + +QAbstractSocket::SocketError Dtls::error() const { return d->lastError; } + +void Dtls::negotiate() { d->negotiate(); } + +bool Dtls::isStarted() const { return d->tls != nullptr; } + +bool Dtls::isSupported() { return QCA::isSupported("dtls"); } + +QByteArray Dtls::readDatagram() +{ + if (!d->tls) { + DTLS_DEBUG("negotiation hasn't started yet. ignore readDatagram"); + return {}; + } + QByteArray a = d->tls->read(); + // DTLS_DEBUG("read %d bytes of decrypted data", a.size()); + return a; +} + +QByteArray Dtls::readOutgoingDatagram() +{ + if (!d->tls) { + DTLS_DEBUG("negotiation hasn't started yet. ignore readOutgoingDatagram"); + return {}; + } + auto ba = d->tls->readOutgoing(); + // DTLS_DEBUG("read outgoing packet of %d bytes", ba.size()); + return ba; +} + +void Dtls::writeDatagram(const QByteArray &data) +{ + // DTLS_DEBUG("write %d bytes for encryption\n", data.size()); + if (!d->tls) { + DTLS_DEBUG("negotiation hasn't started yet. ignore writeDatagram"); + return; + } + d->tls->write(data); +} + +void Dtls::writeIncomingDatagram(const QByteArray &data) +{ + // DTLS_DEBUG("write incoming %d bytes for decryption\n", data.size()); + if (!d->tls) { + DTLS_DEBUG("negotiation hasn't started yet. ignore incoming datagram"); + return; + } + d->tls->writeIncoming(data); +} + +} // namespace XMPP + +#include "dtls.moc" diff --git a/src/irisnet/noncore/dtls.h b/src/irisnet/noncore/dtls.h new file mode 100644 index 00000000..3e7db21d --- /dev/null +++ b/src/irisnet/noncore/dtls.h @@ -0,0 +1,109 @@ +/* + * Copyright (C) 2021 Sergey Ilinykh + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library. If not, see . + * + */ + +#ifndef XMPP_DTLS_H +#define XMPP_DTLS_H + +#include + +#include "xmpp_hash.h" + +namespace QCA { +class Certificate; +class PrivateKey; +} + +namespace XMPP { + +/* +Calls order: +juliet: startOutgoing() -> localFingerprint() -> network + network (remote fingerpring) -> setRemoteFingerprint() -> negotiate(start server) -> network (iq result) +romeo: setRemoteFingerprint() -> acceptIncoming() -> localFingerprint() -> network + network (iq result) -> negotiate(start client) +*/ + +class Dtls : public QObject { + Q_OBJECT +public: + enum Setup { NotSet, Active, Passive, ActPass, HoldConn }; + + struct FingerPrint { + Hash hash; + Setup setup = Setup(-1); + + FingerPrint() : setup(NotSet) { } + inline FingerPrint(const QDomElement &el) { parse(el); } + FingerPrint(const Hash &hash, Setup setup) : hash(hash), setup(setup) { } + + static QString ns(); + + inline bool operator==(const FingerPrint &other) const { return setup == other.setup || hash == other.hash; } + inline bool operator!=(const FingerPrint &other) const { return !(*this == other); } + + bool parse(const QDomElement &el); + inline bool isValid() const + { + return hash.isValid() && !hash.data().isEmpty() && setup >= Active && setup <= HoldConn; + } + QDomElement toXml(QDomDocument *doc) const; + }; + + explicit Dtls(QObject *parent = nullptr, const QString &localJid = QString(), const QString &remoteJid = QString()); + + // state machine stuff + void initOutgoing(); // when it's our side first send dtls info + void acceptIncoming(); // when we need to respond to the remote dtls info + void onRemoteAcceptedFingerprint(); + + void setLocalCertificate(const QCA::Certificate &cert, const QCA::PrivateKey &pkey); + QCA::Certificate localCertificate() const; + QCA::Certificate remoteCertificate() const; + + const FingerPrint &localFingerprint() const; + const FingerPrint &remoteFingerprint() const; + void setRemoteFingerprint(const FingerPrint &fingerprint); + + QAbstractSocket::SocketError error() const; + + QByteArray readDatagram(); + QByteArray readOutgoingDatagram(); + void writeDatagram(const QByteArray &data); + void writeIncomingDatagram(const QByteArray &data); + + bool isStarted() const; + + static bool isSupported(); +signals: + void needRestart(); + void readyRead(); + void readyReadOutgoing(); + void connected(); + void errorOccurred(QAbstractSocket::SocketError); + void closed(); + +private: + void negotiate(); // yep. it's possible to make it public but not really necessary atm + + class Private; + Private *d; +}; + +} // namespace XMPP + +#endif // XMPP_DTLS_H diff --git a/src/irisnet/noncore/ice176.cpp b/src/irisnet/noncore/ice176.cpp index fb417602..e5870e26 100644 --- a/src/irisnet/noncore/ice176.cpp +++ b/src/irisnet/noncore/ice176.cpp @@ -1,5 +1,6 @@ /* - * Copyright (C) 2009,2010 Barracuda Networks, Inc. + * Copyright (C) 2009-2010 Barracuda Networks, Inc. + * Copyright (C) 2020 Sergey Ilinykh * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -18,264 +19,256 @@ #include "ice176.h" -#include -#include -#include -#include -#include "stuntransaction.h" +#include "iceabstractstundisco.h" +#include "iceagent.h" +#include "icecomponent.h" +#include "icelocaltransport.h" +#include "iceturntransport.h" #include "stunbinding.h" #include "stunmessage.h" +#include "stuntransaction.h" +#include "stuntypes.h" #include "udpportreserver.h" -#include "icelocaltransport.h" -#include "iceturntransport.h" -#include "icecomponent.h" -namespace XMPP { - -enum -{ - Direct, - Relayed -}; +#include +#include +#include +#include +#include +#include +#include +#include +#include -static QChar randomPrintableChar() -{ - // 0-25 = a-z - // 26-51 = A-Z - // 52-61 = 0-9 - - uchar c = QCA::Random::randomChar() % 62; - if(c <= 25) - return 'a' + c; - else if(c <= 51) - return 'A' + (c - 26); - else - return '0' + (c - 52); -} +#define ICE_DEBUG +#ifdef ICE_DEBUG +#define iceDebug qDebug +#else +#define iceDebug(...) +#endif -static QString randomCredential(int len) -{ - QString out; - for(int n = 0; n < len; ++n) - out += randomPrintableChar(); - return out; -} +namespace XMPP { +enum { Direct, Relayed }; static qint64 calc_pair_priority(int a, int b) { qint64 priority = ((qint64)1 << 32) * qMin(a, b); priority += (qint64)2 * qMax(a, b); - if(a > b) + if (a > b) ++priority; return priority; } -// see if candidates are considered the same for pruning purposes -static bool compare_candidates(const IceComponent::CandidateInfo &a, const IceComponent::CandidateInfo &b) -{ - if(a.addr == b.addr && a.componentId == b.componentId) - return true; - else - return false; -} - // scope values: 0 = local, 1 = link-local, 2 = private, 3 = public // FIXME: dry (this is in psi avcall also) static int getAddressScope(const QHostAddress &a) { - if(a.protocol() == QAbstractSocket::IPv6Protocol) - { - if(a == QHostAddress(QHostAddress::LocalHostIPv6)) - return 0; - else if(XMPP::Ice176::isIPv6LinkLocalAddress(a)) + if (a.isLoopback()) + return 0; + if (a.protocol() == QAbstractSocket::IPv6Protocol) { + if (XMPP::Ice176::isIPv6LinkLocalAddress(a)) return 1; - } - else if(a.protocol() == QAbstractSocket::IPv4Protocol) - { + } else if (a.protocol() == QAbstractSocket::IPv4Protocol) { quint32 v4 = a.toIPv4Address(); - quint8 a0 = v4 >> 24; - quint8 a1 = (v4 >> 16) & 0xff; - if(a0 == 127) - return 0; - else if(a0 == 169 && a1 == 254) + quint8 a0 = quint8(v4 >> 24); + quint8 a1 = quint8((v4 >> 16) & 0xff); + if (a0 == 169 && a1 == 254) return 1; - else if(a0 == 10) + else if (a0 == 10) return 2; - else if(a0 == 172 && a1 >= 16 && a1 <= 31) + else if (a0 == 172 && a1 >= 16 && a1 <= 31) return 2; - else if(a0 == 192 && a1 == 168) + else if (a0 == 192 && a1 == 168) return 2; } return 3; } -class Ice176::Private : public QObject +// -1 = a is higher priority, 1 = b is higher priority, 0 = equal +static int comparePriority(const QHostAddress &a, const QHostAddress &b) +{ + // prefer closer scope + int a_scope = getAddressScope(a); + int b_scope = getAddressScope(b); + if (a_scope < b_scope) + return -1; + else if (a_scope > b_scope) + return 1; + + // prefer ipv6 + if (a.protocol() == QAbstractSocket::IPv6Protocol && b.protocol() != QAbstractSocket::IPv6Protocol) + return -1; + else if (b.protocol() == QAbstractSocket::IPv6Protocol && a.protocol() != QAbstractSocket::IPv6Protocol) + return 1; + + return 0; +} + +static QList sortAddrs(const QList &in) { + QList out; + + for (const QHostAddress &a : in) { + int at; + for (at = 0; at < out.count(); ++at) { + if (comparePriority(a, out[at]) < 0) + break; + } + + out.insert(at, a); + } + + return out; +} + +class Ice176::Private : public QObject { Q_OBJECT public: - enum State - { + // note, Nominating state is skipped when aggressive nomination is enabled. + enum State { Stopped, - Starting, - Started, - Stopping + Starting, // preparing local candidates right after start() call + Started, // local candidates ready. ready for pairing with remote + Active, // all components have a nominated pair and media transferred over them + Stopping // when received a command from the user to stop }; - enum CandidatePairState - { - PWaiting, - PInProgress, - PSucceeded, - PFailed, - PFrozen - }; + enum CandidatePairState { PWaiting, PInProgress, PSucceeded, PFailed, PFrozen }; - enum CheckListState - { - LRunning, - LCompleted, - LFailed - }; + enum CheckListState { LRunning, LCompleted, LFailed }; - class CandidatePair - { + class CandidatePair { public: - IceComponent::CandidateInfo local, remote; - bool isDefault; - bool isValid; - bool isNominated; - CandidatePairState state; + using Ptr = QSharedPointer; + + IceComponent::CandidateInfo::Ptr local, remote; + bool isDefault = false; // not used in xmpp + bool isValid = false; // a pair which is also in valid list + bool isNominated = false; - qint64 priority; - QString foundation; + // states for last or comming checks + bool isTriggered = false; // last scheduled check was a triggered check + bool isTriggeredForNominated = false; + bool finalNomination = false; +#ifdef ICE_DEBUG + bool logNew = false; +#endif - StunBinding *binding; + CandidatePairState state = CandidatePairState::PFrozen; + + qint64 priority = 0; + QString foundation; // rfc8445 6.1.2.6 (combination of foundations) + + StunBinding *binding = nullptr; // FIXME: this is wrong i think, it should be in LocalTransport // or such, to multiplex ids - StunTransactionPool *pool; + StunTransactionPool::Ptr pool; - CandidatePair() : - binding(0), - pool(0) + inline bool isNull() const { return local->addr.addr.isNull() || remote->addr.addr.isNull(); } + inline operator QString() const { + if (isNull()) + return QLatin1String("null pair"); + return QString(QLatin1String("L:%1 %2 - R:%3 %4 (prio:%5)")) + .arg(candidateType_to_string(local->type), QString(local->addr), candidateType_to_string(remote->type), + QString(remote->addr), QString::number(priority)); } }; - class CheckList - { + class CheckList { public: - QList pairs; - CheckListState state; + QList> pairs; + QQueue> triggeredPairs; + QList> validPairs; // highest priority and nominated come first + CheckListState state; }; - class Component - { + class Component { public: - int id; - IceComponent *ic; - bool localFinished; - bool stopped; - bool lowOverhead; - - Component() : - localFinished(false), - stopped(false), - lowOverhead(false) - { - } + int id = 0; + IceComponent *ic = nullptr; + std::unique_ptr nominationTimer = std::unique_ptr(); + CandidatePair::Ptr selectedPair; // final selected pair. won't be changed + CandidatePair::Ptr highestPair; // current highest priority pair to send data + bool localFinished = false; + bool hasValidPairs = false; + bool hasNominatedPairs = false; + bool stopped = false; + bool lowOverhead = false; + + // initiator is nominating the final pair (will be set as `selectePair` when ready) + bool nominating = false; // with aggressive nomination it's always false }; - Ice176 *q; - Ice176::Mode mode; - State state; - TurnClient::Proxy proxy; - UdpPortReserver *portReserver; - int componentCount; - QList localAddrs; - QList extAddrs; - QHostAddress stunBindAddr; - int stunBindPort; - QHostAddress stunRelayUdpAddr; - int stunRelayUdpPort; - QString stunRelayUdpUser; - QCA::SecureArray stunRelayUdpPass; - QHostAddress stunRelayTcpAddr; - int stunRelayTcpPort; - QString stunRelayTcpUser; - QCA::SecureArray stunRelayTcpPass; - QString localUser, localPass; - QString peerUser, peerPass; - QList components; - QList localCandidates; - QSet iceTransports; - CheckList checkList; - QList< QList > in; - bool useLocal; - bool useStunBind; - bool useStunRelayUdp; - bool useStunRelayTcp; - bool useTrickle; - QTimer *collectTimer; - - Private(Ice176 *_q) : - QObject(_q), - q(_q), - state(Stopped), - portReserver(0), - componentCount(0), - useLocal(true), - useStunBind(true), - useStunRelayUdp(true), - useStunRelayTcp(true), - useTrickle(false), - collectTimer(0) + Ice176 *q; + Ice176::Mode mode; + State state = Stopped; + QTimer checkTimer; + TurnClient::Proxy proxy; + UdpPortReserver *portReserver = nullptr; + std::unique_ptr pacTimer; + int nominationTimeout = 3000; // 3s + int pacTimeout = 30000; // 30s todo: compute from rto. see draft-ietf-ice-pac-06 + int componentCount = 0; + QList localAddrs; + QList extAddrs; + TransportAddress stunBindAddr; + TransportAddress stunRelayUdpAddr; + QString stunRelayUdpUser; + QCA::SecureArray stunRelayUdpPass; + TransportAddress stunRelayTcpAddr; + QString stunRelayTcpUser; + QCA::SecureArray stunRelayTcpPass; + QPointer stunDiscoverer; + QString localUser, localPass; + QString peerUser, peerPass; + std::vector components; + QList localCandidates; + QList remoteCandidates; + QSet> iceTransports; + CheckList checkList; + QList> in; + Features remoteFeatures; + Features localFeatures; + bool allowIpExposure = true; + bool useLocal = true; + bool useStunBind = true; + bool useStunRelayUdp = true; + bool useStunRelayTcp = true; + bool localHostGatheringFinished = false; + bool localGatheringComplete = false; + bool remoteGatheringComplete = false; + bool readyToSendMedia = false; + bool canStartChecks = false; + + Private(Ice176 *_q) : QObject(_q), q(_q) { + connect(&checkTimer, &QTimer::timeout, this, [this]() { + auto pair = selectNextPairToCheck(); + if (pair) + checkPair(pair); + else + checkTimer.stop(); + }); + checkTimer.setInterval(20); + checkTimer.setSingleShot(false); } ~Private() { - if(collectTimer) - { - collectTimer->disconnect(this); - collectTimer->deleteLater(); - } - - foreach(const Component &c, components) + for (const Component &c : components) delete c.ic; - - // no need to delete pools and bindings since pools already deleted here - // by QObject destructor as children of this(Ice176::Private) object. - // Bindings deleted too as children of pool - // should be reviewed by Justin =) - /*for(int n = 0; n < checkList.pairs.count(); ++n) - { - StunBinding *binding = checkList.pairs[n].binding; - StunTransactionPool *pool = checkList.pairs[n].pool; - - delete binding; - - if(pool) - { - pool->disconnect(this); - pool->setParent(0); - pool->deleteLater(); - } - }*/ } - void reset() - { - // TODO - } + void reset() { checkTimer.stop(); /*TODO*/ } int findLocalAddress(const QHostAddress &addr) { - for(int n = 0; n < localAddrs.count(); ++n) - { - if(localAddrs[n].addr == addr) + for (int n = 0; n < localAddrs.count(); ++n) { + if (localAddrs[n].addr == addr) return n; } @@ -285,14 +278,13 @@ class Ice176::Private : public QObject void updateLocalAddresses(const QList &addrs) { // for now, ignore address changes during operation - if(state != Stopped) + if (state != Stopped) return; localAddrs.clear(); - foreach(const LocalAddress &la, addrs) - { + for (const auto &la : addrs) { int at = findLocalAddress(la.addr); - if(at == -1) + if (at == -1) localAddrs += la; } } @@ -300,66 +292,80 @@ class Ice176::Private : public QObject void updateExternalAddresses(const QList &addrs) { // for now, ignore address changes during operation - if(state != Stopped) + if (state != Stopped) return; extAddrs.clear(); - foreach(const ExternalAddress &ea, addrs) - { + for (const ExternalAddress &ea : addrs) { int at = findLocalAddress(ea.base.addr); - if(at != -1) + if (at != -1) extAddrs += ea; } } + void stunFound(AbstractStunDisco::Service::Ptr service) + { + quint16 port = service->port ? service->port : ((service->flags & AbstractStunDisco::Tls) ? 5349 : 3478); + qDebug("found %s: %s:%hu %s %s", (service->flags & AbstractStunDisco::Relay) ? "TURN" : "STUN", + qPrintable(service->host), port, service->transport == AbstractStunDisco::Tcp ? "tcp" : "udp", + (service->flags & AbstractStunDisco::Tls) ? "tls" : "insecure"); + } + void stunModified(AbstractStunDisco::Service::Ptr) { } + void stunRemoved(AbstractStunDisco::Service::Ptr) { } + void stunDiscoFinished() { } + void start() { Q_ASSERT(state == Stopped); state = Starting; - localUser = randomCredential(4); - localPass = randomCredential(22); + localUser = IceAgent::randomCredential(4); + localPass = IceAgent::randomCredential(22); - QList socketList; - if(portReserver) + if (!useLocal) + useStunBind = false; + + QList socketList; + if (portReserver) + // list size = componentCount * number of interfaces socketList = portReserver->borrowSockets(componentCount, this); - for(int n = 0; n < componentCount; ++n) - { - Component c; - c.id = n + 1; - c.ic = new IceComponent(c.id, this); - c.ic->setDebugLevel(IceComponent::DL_Info); - connect(c.ic, SIGNAL(candidateAdded(XMPP::IceComponent::Candidate)), SLOT(ic_candidateAdded(XMPP::IceComponent::Candidate))); - connect(c.ic, SIGNAL(candidateRemoved(XMPP::IceComponent::Candidate)), SLOT(ic_candidateRemoved(XMPP::IceComponent::Candidate))); - connect(c.ic, SIGNAL(localFinished()), SLOT(ic_localFinished())); - connect(c.ic, SIGNAL(stopped()), SLOT(ic_stopped())); - connect(c.ic, SIGNAL(debugLine(QString)), SLOT(ic_debugLine(QString))); + components.reserve(ulong(componentCount)); + for (int n = 0; n < componentCount; ++n) { + components.emplace_back(); + Component &c = components.back(); + c.id = n + 1; + c.ic = new IceComponent(c.id, this); + c.ic->setDebugLevel(IceComponent::DL_Packet); + connect(c.ic, &IceComponent::candidateAdded, this, &Private::ic_candidateAdded); + connect(c.ic, &IceComponent::candidateRemoved, this, &Private::ic_candidateRemoved); + connect(c.ic, &IceComponent::localFinished, this, &Private::ic_localFinished); + connect(c.ic, &IceComponent::gatheringComplete, this, &Private::ic_gatheringComplete); + connect(c.ic, &IceComponent::stopped, this, &Private::ic_stopped); + connect(c.ic, &IceComponent::debugLine, this, &Private::ic_debugLine); c.ic->setClientSoftwareNameAndVersion("Iris"); c.ic->setProxy(proxy); - if(portReserver) + if (portReserver) c.ic->setPortReserver(portReserver); c.ic->setLocalAddresses(localAddrs); c.ic->setExternalAddresses(extAddrs); - if(!stunBindAddr.isNull()) - c.ic->setStunBindService(stunBindAddr, stunBindPort); - if(!stunRelayUdpAddr.isNull()) - c.ic->setStunRelayUdpService(stunRelayUdpAddr, stunRelayUdpPort, stunRelayUdpUser, stunRelayUdpPass); - if(!stunRelayTcpAddr.isNull()) - c.ic->setStunRelayTcpService(stunRelayTcpAddr, stunRelayTcpPort, stunRelayTcpUser, stunRelayTcpPass); - - c.ic->setUseLocal(useLocal); - c.ic->setUseStunBind(useStunBind); + if (stunBindAddr.isValid()) + c.ic->setStunBindService(stunBindAddr); + if (stunRelayUdpAddr.isValid()) + c.ic->setStunRelayUdpService(stunRelayUdpAddr, stunRelayUdpUser, stunRelayUdpPass); + if (stunRelayTcpAddr.isValid()) + c.ic->setStunRelayTcpService(stunRelayTcpAddr, stunRelayTcpUser, stunRelayTcpPass); + + c.ic->setUseLocal(useLocal && allowIpExposure); + c.ic->setUseStunBind(useStunBind && allowIpExposure); c.ic->setUseStunRelayUdp(useStunRelayUdp); c.ic->setUseStunRelayTcp(useStunRelayTcp); // create an inbound queue for this component in += QList(); - components += c; - c.ic->update(&socketList); } @@ -367,23 +373,40 @@ class Ice176::Private : public QObject // the app provided a different address list to // UdpPortReserver and Ice176. and that would really be // a dumb thing to do but I'm not going to Q_ASSERT it - if(!socketList.isEmpty()) + if (!socketList.isEmpty()) portReserver->returnSockets(socketList); } - void stop() + void startChecks() { - Q_ASSERT(state == Starting || state == Started); + pacTimer.reset(new QTimer(this)); + pacTimer->setSingleShot(true); + pacTimer->setInterval(pacTimeout); + connect(pacTimer.get(), &QTimer::timeout, this, &Ice176::Private::onPacTimeout); + iceDebug("Start Patiently Awaiting Connectivity timer"); + canStartChecks = true; + pacTimer->start(); + checkTimer.start(); + } - state = Stopping; + void stop() + { + if (state == Stopped || state == Stopping) + return; // stopped as a result of previous error? + + canStartChecks = false; + state = Stopping; + pacTimer.reset(); + checkTimer.stop(); + + // will trigger candidateRemoved events and result pairs cleanup. + if (!components.empty()) { + for (auto &c : components) { + c.nominationTimer.reset(); + c.ic->stop(); + } - if(!components.isEmpty()) - { - for(int n = 0; n < components.count(); ++n) - components[n].ic->stop(); - } - else - { + } else { // TODO: hmm, is it possible to have no components? QMetaObject::invokeMethod(this, "postStop", Qt::QueuedConnection); } @@ -391,300 +414,898 @@ class Ice176::Private : public QObject void addRemoteCandidates(const QList &list) { - QList remoteCandidates; - foreach(const Candidate &c, list) - { - IceComponent::CandidateInfo ci; - ci.addr.addr = c.ip; - ci.addr.addr.setScopeId(QString()); - ci.addr.port = c.port; - ci.type = (IceComponent::CandidateType)string_to_candidateType(c.type); // TODO: handle error - ci.componentId = c.component; - ci.priority = c.priority; - ci.foundation = c.foundation; - if(!c.rel_addr.isNull()) - { - ci.base.addr = c.rel_addr; - ci.base.addr.setScopeId(QString()); - ci.base.port = c.rel_port; + QList remoteCandidates; + for (const Candidate &c : list) { + auto ci = IceComponent::CandidateInfo::Ptr::create(); + ci->addr.addr = c.ip; + ci->addr.addr.setScopeId(QString()); + ci->addr.port = c.port; + ci->type = (IceComponent::CandidateType)string_to_candidateType(c.type); // TODO: handle error + ci->componentId = c.component; + ci->priority = c.priority; + ci->foundation = c.foundation; + if (!c.rel_addr.isNull()) { + ci->base.addr = c.rel_addr; + ci->base.addr.setScopeId(QString()); + ci->base.port = c.rel_port; + } + ci->network = c.network; + ci->id = c.id; + + // find remote prflx with same addr. we have to update them instead adding new one. RFC8445 7.3.1.3 + auto it = std::find_if(this->remoteCandidates.begin(), this->remoteCandidates.end(), + [&](IceComponent::CandidateInfo::Ptr rc) { + return ci->addr == rc->addr && ci->componentId == rc->componentId + && rc->type == IceComponent::PeerReflexiveType; + }); + if (it != this->remoteCandidates.end()) { + (*it)->type = ci->type; // RFC8445 5.1.2.1. Recommended Formula (peer-reflexive are preferred) + // B.7. Why Prefer Peer-Reflexive Candidates? + // if srflx == prflx -> set srflx because not secure anyway + (*it)->foundation = ci->foundation; + (*it)->base = ci->base; + (*it)->network = ci->network; + (*it)->id = ci->id; + iceDebug("Previously known remote prflx was updated from signalling: %s", qPrintable((*it)->addr)); + } else { + remoteCandidates += ci; } - ci.network = c.network; - ci.id = c.id; - remoteCandidates += ci; } + this->remoteCandidates += remoteCandidates; - printf("adding %d remote candidates\n", remoteCandidates.count()); - - QList pairs; - foreach(const IceComponent::Candidate &cc, localCandidates) - { - const IceComponent::CandidateInfo &lc = cc.info; + iceDebug("adding %lld remote candidates. total=%lld", qsizetype(remoteCandidates.count()), + qsizetype(this->remoteCandidates.count())); + doPairing(localCandidates, remoteCandidates); + } - foreach(const IceComponent::CandidateInfo &rc, remoteCandidates) - { - if(lc.componentId != rc.componentId) - continue; + void setRemoteGatheringComplete() + { + remoteGatheringComplete = true; + if (!localGatheringComplete || state != Started) + return; - // don't pair ipv4 with ipv6. FIXME: is this right? - if(lc.addr.addr.protocol() != rc.addr.addr.protocol()) - continue; + for (auto &c : components) + tryNominateSelectedPair(c.id); + } - // don't relay to localhost. turnserver - // doesn't like it. i don't know if this - // should qualify as a HACK or not. - // trying to relay to localhost is pretty - // stupid anyway - if(lc.type == IceComponent::RelayedType && getAddressScope(rc.addr.addr) == 0) - continue; + // returns a pair is pairable or null + QSharedPointer makeCandidatesPair(IceComponent::CandidateInfo::Ptr lc, + IceComponent::CandidateInfo::Ptr rc) + { + if (lc->componentId != rc->componentId) + return {}; - CandidatePair pair; - pair.state = PFrozen; // FIXME: setting state here may be wrong - pair.local = lc; - pair.remote = rc; - if(pair.local.addr.addr.protocol() == QAbstractSocket::IPv6Protocol && isIPv6LinkLocalAddress(pair.local.addr.addr)) - pair.remote.addr.addr.setScopeId(pair.local.addr.addr.scopeId()); - pair.isDefault = false; - pair.isValid = false; - pair.isNominated = false; - if(mode == Ice176::Initiator) - pair.priority = calc_pair_priority(lc.priority, rc.priority); - else - pair.priority = calc_pair_priority(rc.priority, lc.priority); - pairs += pair; - } + // don't pair ipv4 with ipv6. FIXME: is this right? + if (lc->addr.addr.protocol() != rc->addr.addr.protocol()) { + iceDebug("Skip building pair: %s - %s (protocol mismatch)", qPrintable(lc->addr), qPrintable(rc->addr)); + return {}; } - printf("%d pairs\n", pairs.count()); + // don't relay to localhost. turnserver + // doesn't like it. i don't know if this + // should qualify as a HACK or not. + // trying to relay to localhost is pretty + // stupid anyway + if (lc->type == IceComponent::RelayedType && getAddressScope(rc->addr.addr) == 0) { + qDebug("Skip building pair: %s - %s (relay to localhost)", qPrintable(lc->addr), qPrintable(rc->addr)); + return {}; + } - // combine pairs with existing, and sort - pairs = checkList.pairs + pairs; - checkList.pairs.clear(); - foreach(const CandidatePair &pair, pairs) - { - int at; - for(at = 0; at < checkList.pairs.count(); ++at) - { - if(compare_pair(pair, checkList.pairs[at]) < 0) - break; - } + auto pair = QSharedPointer::create(); + pair->local = lc; + pair->remote = rc; + if (pair->local->addr.addr.protocol() == QAbstractSocket::IPv6Protocol + && isIPv6LinkLocalAddress(pair->local->addr.addr)) + pair->remote->addr.addr.setScopeId(pair->local->addr.addr.scopeId()); + if (mode == Ice176::Initiator) + pair->priority = calc_pair_priority(lc->priority, rc->priority); + else + pair->priority = calc_pair_priority(rc->priority, lc->priority); - checkList.pairs.insert(at, pair); - } + return pair; + } - // pruning + // adds new pairs, sorts, prunes + void addChecklistPairs(const QList> &pairs) + { +#ifdef ICE_DEBUG + iceDebug("%lld new pairs", qsizetype(pairs.count())); + for (auto &p : pairs) + p->logNew = true; +#endif + if (!pairs.count()) + return; - for(int n = 0; n < checkList.pairs.count(); ++n) - { - CandidatePair &pair = checkList.pairs[n]; - if(pair.local.type == IceComponent::ServerReflexiveType) - pair.local.addr = pair.local.base; - } + // combine pairs with existing, and sort + checkList.pairs += pairs; + std::sort(checkList.pairs.begin(), checkList.pairs.end(), + [&](const QSharedPointer &a, const QSharedPointer &b) { + return a->priority == b->priority ? a->local->componentId < b->local->componentId + : a->priority > b->priority; + }); - for(int n = 0; n < checkList.pairs.count(); ++n) - { - CandidatePair &pair = checkList.pairs[n]; - printf("%d, %s:%d -> %s:%d\n", pair.local.componentId, qPrintable(pair.local.addr.addr.toString()), pair.local.addr.port, qPrintable(pair.remote.addr.addr.toString()), pair.remote.addr.port); - - bool found = false; - for(int i = n - 1; i >= 0; --i) - { - if(compare_candidates(pair.local, checkList.pairs[i].local) && compare_candidates(pair.remote, checkList.pairs[i].remote)) - { - found = true; + // pruning + for (int n = 0; n < checkList.pairs.count(); ++n) { + auto &pair = checkList.pairs[n]; +#ifdef ICE_DEBUG + if (pair->logNew) + iceDebug("C%d, %s", pair->local->componentId, qPrintable(*pair)); +#endif + + for (int i = n - 1; i >= 0; --i) { + // RFC8445 says to use base only for reflexive. but base is set properly for host and relayed too. + if (pair->local->componentId == checkList.pairs[i]->local->componentId + && pair->local->base == checkList.pairs[i]->local->base + && pair->remote->addr == checkList.pairs[i]->remote->addr) { + + checkList.pairs.removeAt(n); + --n; // adjust position break; } } - - if(found ) - { - checkList.pairs.removeAt(n); - --n; // adjust position - } } // max pairs is 100 * number of components - int max_pairs = 100 * components.count(); - while(checkList.pairs.count() > max_pairs) + int max_pairs = 100 * int(components.size()); + while (checkList.pairs.count() > max_pairs) checkList.pairs.removeLast(); +#ifdef ICE_DEBUG + iceDebug("%lld after pruning (just new below):", qsizetype(checkList.pairs.count())); + for (auto &p : checkList.pairs) { + if (p->logNew) + iceDebug("C%d, %s", p->local->componentId, qPrintable(*p)); + p->logNew = false; + } +#endif + } - printf("%d after pruning\n", checkList.pairs.count()); - - // set state - for(int n = 0; n < checkList.pairs.count(); ++n) - { - CandidatePair &pair = checkList.pairs[n]; + QSharedPointer selectNextPairToCheck() + { + // rfc8445 6.1.4.2. Performing Connectivity Checks + QSharedPointer pair; + while (!checkList.triggeredPairs.empty() && !(pair = checkList.triggeredPairs.dequeue().lock())) + ; + + if (pair) { + pair->isTriggered = true; + // according to rfc - check just this one + iceDebug("next check from triggered list: %s", qPrintable(*pair)); + return pair; + } - // only initialize the new pairs - if(pair.state != PFrozen) - continue; + auto it = std::find_if(checkList.pairs.begin(), checkList.pairs.end(), [&](const auto &p) mutable { + if (p->state == PFrozen && !pair) + pair = p; + return p->state == PWaiting; + }); + if (it != checkList.pairs.end()) { // found waiting + // the list was sorted already by priority and componentId. So first one is Ok + iceDebug("next check for already waiting: %s", qPrintable(**it)); + (*it)->isTriggered = false; + return *it; + } - pair.foundation = pair.local.foundation + pair.remote.foundation; + if (pair) { // now it's frozen highest-priority pair + pair->isTriggered = false; + iceDebug("next check for a frozen pair: %s", qPrintable(*pair)); + } - // FIXME: for now we just do checks to everything immediately - pair.state = PInProgress; + // FIXME real algo should be (but is requires significant refactoring) + // 1) go over all knows pair foundations over all checklists + // 2) if for the foundation there is a frozen pair but no (in-progress or waiting) + // 3) - do checks on this pair - int at = findLocalCandidate(pair.local.addr.addr, pair.local.addr.port); - Q_ASSERT(at != -1); + return pair; + } - IceComponent::Candidate &lc = localCandidates[at]; + void checkPair(QSharedPointer pair) + { + pair->foundation = pair->local->foundation + pair->remote->foundation; + pair->state = PInProgress; - Component &c = components[findComponent(lc.info.componentId)]; + int at = findLocalCandidate(pair->local->addr); + Q_ASSERT(at != -1); - pair.pool = new StunTransactionPool(StunTransaction::Udp, this); - connect(pair.pool, SIGNAL(outgoingMessage(QByteArray,QHostAddress,int)), SLOT(pool_outgoingMessage(QByteArray,QHostAddress,int))); - //pair.pool->setUsername(peerUser + ':' + localUser); - //pair.pool->setPassword(peerPass.toUtf8()); + auto &lc = localCandidates[at]; + + Component &c = *findComponent(lc.info->componentId); + + // read comment to the pool member how wrong it is + pair->pool = StunTransactionPool::Ptr::create(StunTransaction::Udp); + connect(pair->pool.data(), &StunTransactionPool::outgoingMessage, this, + [this, weakPair = pair.toWeakRef()](const QByteArray &packet, const TransportAddress &) { + auto pair = weakPair.toStrongRef(); + if (!pair) + return; + int at = findLocalCandidate(pair->local->addr); + if (at == -1) { // FIXME: assert? + qDebug("Failed to find local candidate %s", qPrintable(pair->local->addr)); + return; + } - pair.binding = new StunBinding(pair.pool); - connect(pair.binding, SIGNAL(success()), SLOT(binding_success())); + IceComponent::Candidate &lc = localCandidates[at]; + int path = lc.path; + + iceDebug("send connectivity check for pair %s%s", qPrintable(*pair), + (mode == Initiator + ? (pair->binding->useCandidate() ? " (nominating)" : "") + : (pair->isTriggeredForNominated ? " (triggered check for nominated)" : ""))); + lc.iceTransport->writeDatagram(path, packet, pair->remote->addr); + }); + + // pair->pool->setUsername(peerUser + ':' + localUser); + // pair->pool->setPassword(peerPass.toUtf8()); + + pair->binding = new StunBinding(pair->pool.data()); + connect(pair->binding, &StunBinding::success, this, [this, wpair = pair.toWeakRef()]() { + auto pair = wpair.lock(); + if (pair) + handlePairBindingSuccess(pair); + }); + connect(pair->binding, &StunBinding::error, this, [this, wpair = pair.toWeakRef()](XMPP::StunBinding::Error e) { + auto pair = wpair.lock(); + if (pair) + handlePairBindingError(pair, e); + }); + + quint32 prflx_priority = c.ic->peerReflexivePriority(lc.iceTransport, lc.path); + pair->binding->setPriority(prflx_priority); + + if (mode == Ice176::Initiator) { + pair->binding->setIceControlling(0); + if (localFeatures & AggressiveNomination || pair->finalNomination) + pair->binding->setUseCandidate(true); + } else + pair->binding->setIceControlled(0); + + pair->binding->setShortTermUsername(peerUser + ':' + localUser); + pair->binding->setShortTermPassword(peerPass); + + pair->binding->start(); + } - int prflx_priority = c.ic->peerReflexivePriority(lc.iceTransport, lc.path); - pair.binding->setPriority(prflx_priority); + void doPairing(const QList &localCandidates, + const QList &remoteCandidates) + { + QList> pairs; + for (const IceComponent::Candidate &cc : localCandidates) { + auto lc = cc.info; + if (lc->type == IceComponent::PeerReflexiveType) { + iceDebug("not pairing local prflx. %s", qPrintable(lc->addr)); + // see RFC8445 7.2.5.3.1. Discovering Peer-Reflexive Candidates + continue; + } - if(mode == Ice176::Initiator) - { - pair.binding->setIceControlling(0); - pair.binding->setUseCandidate(true); + for (const IceComponent::CandidateInfo::Ptr &rc : std::as_const(remoteCandidates)) { + auto pair = makeCandidatesPair(lc, rc); + if (!pair.isNull()) + pairs += pair; } - else - pair.binding->setIceControlled(0); + } - pair.binding->setShortTermUsername(peerUser + ':' + localUser); - pair.binding->setShortTermPassword(peerPass); + if (!pairs.count()) + return; - pair.binding->start(); - } + addChecklistPairs(pairs); + if (canStartChecks && !checkTimer.isActive()) + checkTimer.start(); } void write(int componentIndex, const QByteArray &datagram) { - int at = -1; - for(int n = 0; n < checkList.pairs.count(); ++n) - { - if(checkList.pairs[n].local.componentId - 1 == componentIndex && checkList.pairs[n].isValid) - { - at = n; - break; + auto cIt = findComponent(componentIndex + 1); + Q_ASSERT(cIt != components.end()); + + auto pair = cIt->selectedPair; + if (!pair) { + pair = cIt->highestPair; + if (!pair) { + iceDebug("An attempt to write to an ICE component w/o valid sockets"); + return; } } - if(at == -1) - return; - CandidatePair &pair = checkList.pairs[at]; - - at = findLocalCandidate(pair.local.addr.addr, pair.local.addr.port); - if(at == -1) // FIXME: assert? + int at = findLocalCandidate(pair->local->addr); + if (at == -1) { // FIXME: assert? + iceDebug("FIXME! Failed to find local candidate for componentId=%d, addr=%s", componentIndex + 1, + qPrintable(pair->local->addr)); return; + } IceComponent::Candidate &lc = localCandidates[at]; - IceTransport *sock = lc.iceTransport; int path = lc.path; - sock->writeDatagram(path, datagram, pair.remote.addr.addr, pair.remote.addr.port); + lc.iceTransport->writeDatagram(path, datagram, pair->remote->addr); // DOR-SR? - QMetaObject::invokeMethod(q, "datagramsWritten", Qt::QueuedConnection, Q_ARG(int, componentIndex), Q_ARG(int, 1)); + QMetaObject::invokeMethod(q, "datagramsWritten", Qt::QueuedConnection, Q_ARG(int, componentIndex), + Q_ARG(int, 1)); } void flagComponentAsLowOverhead(int componentIndex) { + Q_ASSERT(size_t(componentIndex) < components.size()); // FIXME: ok to assume in order? - Component &c = components[componentIndex]; + Component &c = components[componentIndex]; c.lowOverhead = true; // FIXME: actually do something } -private: - int findComponent(const IceComponent *ic) const + void cleanupButSelectedPair(int componentId) { - for(int n = 0; n < components.count(); ++n) - { - if(components[n].ic == ic) - return n; + CandidatePair::Ptr selected = findComponent(componentId)->selectedPair; + Q_ASSERT(selected); + decltype(checkList.validPairs) newValid; + newValid.push_back(selected); + for (auto &p : checkList.validPairs) + if (p->local->componentId != componentId) + newValid.push_back(p); + checkList.validPairs = newValid; + + auto t = findTransport(selected->local->base); + Q_ASSERT(t.data() != nullptr); + + // cancel planned/active transactions + QMutableListIterator> it(checkList.triggeredPairs); + while (it.hasNext()) { + auto p = it.next().toStrongRef(); + if (!p || p->local->componentId == componentId) + it.remove(); } + for (auto &p : checkList.pairs) { + if (p->local->componentId == componentId && p->state == PInProgress) { + p->binding->cancel(); + p->state = PFailed; + iceDebug("Cancel %s setting it to failed state", qPrintable(*p)); + } + } + // stop not used transports + for (auto &c : localCandidates) { + if (c.info->componentId == componentId && c.iceTransport != t) { + c.iceTransport->stop(); + } + } + } - return -1; + void setSelectedPair(int componentId) + { + auto &component = *findComponent(componentId); + auto &pair = component.selectedPair; + if (pair) + return; +#ifdef ICE_DEBUG + iceDebug("Current valid list state:"); + for (auto &p : checkList.validPairs) { + iceDebug(" C%d: %s", p->local->componentId, qPrintable(*p)); + } +#endif + component.nominationTimer.reset(); + pair = component.highestPair; + if (!pair) { + qWarning("C%d: failed to find selected pair for previously nominated component. Candidates removed " + "without ICE restart?", + componentId); + stop(); + emit q->error(ErrorGeneric); + return; + } + iceDebug("C%d: selected pair: %s (base: %s)", componentId, qPrintable(*pair), qPrintable(pair->local->base)); + cleanupButSelectedPair(componentId); + emit q->componentReady(componentId - 1); + tryIceFinished(); } - int findComponent(int id) const + void optimizeCheckList(int componentId) { - for(int n = 0; n < components.count(); ++n) - { - if(components[n].id == id) - return n; + auto it = findComponent(componentId); + Q_ASSERT(it != components.end() && it->highestPair); + + auto minPriority = it->highestPair->priority; + for (auto &p : checkList.pairs) { + bool toStop = p->local->componentId == componentId && (p->state == PFrozen || p->state == PWaiting) + && p->priority < minPriority; + if (toStop) { + iceDebug("Disable check for %s since we already have better valid pairs", qPrintable(*p)); + p->state = PFailed; + } } + for (auto &pWeak : checkList.triggeredPairs) { + auto p = pWeak.toStrongRef(); + if (p->local->componentId == componentId && p->priority < minPriority) { + iceDebug("Disable triggered check for %s since we already have better valid pairs", qPrintable(*p)); + p->state = PFailed; + } + } + } - return -1; + bool doesItWorthNominateNow(int componentId) + { + auto &c = *findComponent(componentId); + if (mode != Initiator || (localFeatures & AggressiveNomination) || state != Started || !c.highestPair + || c.selectedPair || c.nominating) + return false; + + auto pair = c.highestPair; + Q_ASSERT(!pair->isNominated); + if (pair->local->type == IceComponent::RelayedType) { + if (!(localGatheringComplete && remoteGatheringComplete)) { + iceDebug("Waiting for gathering complete on both sides before nomination of relayed pair"); + return false; // maybe we gonna have a non-relayed pair. RFC8445 anyway allows to send data on any + // valid. + } + + // if there is any non-relayed pending pair + if (std::any_of(checkList.pairs.begin(), checkList.pairs.end(), [](auto &p) { + return p->state != PSucceeded && p->state != PFailed && p->local->type != IceComponent::RelayedType; + })) { + iceDebug("There are some non-relayed pairs to check before relayed nomination"); + return false; // either till checked or remote gathering timeout + } + } + return true; } - int findLocalCandidate(const IceTransport *iceTransport, int path) const + void nominateSelectedPair(int componentId) { - for(int n = 0; n < localCandidates.count(); ++n) - { + auto &c = *findComponent(componentId); + Q_ASSERT(mode == Initiator && c.highestPair && !c.selectedPair && !c.nominating); + c.nominationTimer.reset(); + c.nominating = true; + c.highestPair->finalNomination = true; + iceDebug("Nominating valid pair: %s", qPrintable(*c.highestPair)); + checkList.triggeredPairs.prepend(c.highestPair); + if (!checkTimer.isActive()) + checkTimer.start(); + } + + void tryNominateSelectedPair(int componentId) + { + if (doesItWorthNominateNow(componentId)) + nominateSelectedPair(componentId); + } + + void tryIceFinished() + { + if (!std::all_of(components.begin(), components.end(), [](auto &c) { return c.selectedPair != nullptr; })) + return; + tryReadyToSendMedia(); +#ifdef ICE_DEBUG + iceDebug("ICE selected final pairs!"); + for (auto &c : components) { + iceDebug(" C%d: %s", c.id, qPrintable(*c.selectedPair)); + } + iceDebug("Signalling iceFinished now"); +#endif + pacTimer.reset(); + state = Active; + emit q->iceFinished(); + } + + void setupNominationTimer(int componentId) + { + Component &c = *findComponent(componentId); + if (c.nominationTimer) + return; + bool agrNom = bool((mode == Initiator ? localFeatures : remoteFeatures) & AggressiveNomination); + if (!agrNom && mode == Responder) + return; // responder will wait for nominated pairs till very end + + auto timer = new QTimer(); + c.nominationTimer.reset(timer); + timer->setSingleShot(true); + timer->setInterval(nominationTimeout); + connect(timer, &QTimer::timeout, this, [this, componentId, agrNom]() { + Q_ASSERT(state == Started); + Component &c = *findComponent(componentId); + c.nominationTimer.release()->deleteLater(); + if (c.stopped) + return; // already queue signal likely + if (agrNom) + setSelectedPair(componentId); + else if (!c.nominating && !c.selectedPair) + nominateSelectedPair(componentId); + }); + timer->start(); + } + + // nominated - out side=responder. and remote request had USE_CANDIDATE + void doTriggeredCheck(const IceComponent::Candidate &locCand, IceComponent::CandidateInfo::Ptr remCand, + bool nominated) + { + // let's figure out if this pair already in the check list + auto it = std::find_if(checkList.pairs.begin(), checkList.pairs.end(), + [&](auto const &p) { return *(p->local) == locCand.info && *(p->remote) == remCand; }); + + CandidatePair::Ptr pair = (it == checkList.pairs.end()) ? CandidatePair::Ptr() : *it; + Component &component = *findComponent(locCand.info->componentId); + qint64 minPriority = component.highestPair ? component.highestPair->priority : 0; + if (pair) { + if (pair->priority < minPriority) { + iceDebug( + "Don't do triggered check for known pair since the pair has lower priority than highest valid"); + return; + } + if (pair->state == CandidatePairState::PSucceeded) { + // Check nominated here? + iceDebug("Don't do triggered check since pair is already in success state"); + if (mode == Responder && !pair->isNominated && nominated) { + pair->isNominated = true; + onNewValidPair(pair); + } + return; // nothing todo. rfc 8445 7.3.1.4 + } + pair->isNominated = false; + if (pair->state == CandidatePairState::PInProgress) { + if (pair->isTriggered) { + iceDebug( + "Current in-progress check is already triggered. Don't cancel it while have to according to " + "RFC8445\n"); + return; + } + pair->binding->cancel(); + } + if (pair->state == PFailed) { + // if (state == Stopped) { + // TODO Stopped? maybe Failed? and we have to notify the outer world + //} + } + } else { + // RFC8445 7.3.1.4. Triggered Checks / "If the pair is not already on the checklist" + pair = makeCandidatesPair(locCand.info, remCand); + if (pair.isNull()) { + return; + } + if (pair->priority < minPriority) { + iceDebug( + "Don't do triggered check for a new pair since the pair has lower priority than highest valid"); + return; + } + addChecklistPairs(QList() << pair); + } + + pair->state = PWaiting; + pair->isTriggeredForNominated = nominated; + checkList.triggeredPairs.enqueue(pair); + + if (canStartChecks && !checkTimer.isActive()) + checkTimer.start(); + } + + void onPacTimeout() + { + Q_ASSERT(state == Starting || state == Started); + pacTimer.release()->deleteLater(); + iceDebug("Patiently Awaiting Connectivity timeout"); + stop(); + emit q->error(ErrorGeneric); + } + +private: + inline decltype(components)::iterator findComponent(const IceComponent *ic) + { + return std::find_if(components.begin(), components.end(), [&](auto &c) { return c.ic == ic; }); + } + + inline decltype(components)::iterator findComponent(int id) + { + return std::find_if(components.begin(), components.end(), [&](auto &c) { return c.id == id; }); + } + + int findLocalCandidate(const IceTransport *iceTransport, int path, bool hostAndRelayOnly = false) const + { + for (int n = 0; n < localCandidates.count(); ++n) { const IceComponent::Candidate &cc = localCandidates[n]; - if(cc.iceTransport == iceTransport && cc.path == path) + if (cc.iceTransport == iceTransport && cc.path == path + && (!hostAndRelayOnly || cc.info->type == IceComponent::RelayedType + || cc.info->type == IceComponent::HostType)) return n; } return -1; } - int findLocalCandidate(const QHostAddress &fromAddr, int fromPort) + int findLocalCandidate(const TransportAddress &fromAddr) { - for(int n = 0; n < localCandidates.count(); ++n) - { - const IceComponent::Candidate &cc = localCandidates[n]; - if(cc.info.addr.addr == fromAddr && cc.info.addr.port == fromPort) + for (int n = 0; n < localCandidates.count(); ++n) { + if (localCandidates[n].info->addr == fromAddr) return n; } return -1; } + QSharedPointer findTransport(const TransportAddress &addr) + { + auto c = findLocalCandidate(addr); + if (c >= 0) + return localCandidates[c].iceTransport; + return {}; + } + static QString candidateType_to_string(IceComponent::CandidateType type) { QString out; - switch(type) - { - case IceComponent::HostType: out = "host"; break; - case IceComponent::PeerReflexiveType: out = "prflx"; break; - case IceComponent::ServerReflexiveType: out = "srflx"; break; - case IceComponent::RelayedType: out = "relay"; break; - default: Q_ASSERT(0); + switch (type) { + case IceComponent::HostType: + out = "host"; + break; + case IceComponent::PeerReflexiveType: + out = "prflx"; + break; + case IceComponent::ServerReflexiveType: + out = "srflx"; + break; + case IceComponent::RelayedType: + out = "relay"; + break; + default: + Q_ASSERT(0); } return out; } static int string_to_candidateType(const QString &in) { - if(in == "host") + if (in == "host") return IceComponent::HostType; - else if(in == "prflx") + else if (in == "prflx") return IceComponent::PeerReflexiveType; - else if(in == "srflx") + else if (in == "srflx") return IceComponent::ServerReflexiveType; - else if(in == "relay") + else if (in == "relay") return IceComponent::RelayedType; else return -1; } - static int compare_pair(const CandidatePair &a, const CandidatePair &b) + static void toOutCandidate(const IceComponent::Candidate &cc, Ice176::Candidate &out) { - // prefer remote srflx, for leap - if(a.remote.type == IceComponent::ServerReflexiveType && b.remote.type != IceComponent::ServerReflexiveType && b.remote.addr.addr.protocol() != QAbstractSocket::IPv6Protocol) - return -1; - else if(b.remote.type == IceComponent::ServerReflexiveType && a.remote.type != IceComponent::ServerReflexiveType && a.remote.addr.addr.protocol() != QAbstractSocket::IPv6Protocol) - return 1; + out.component = cc.info->componentId; + out.foundation = cc.info->foundation; + out.generation = 0; // TODO + out.id = cc.info->id; + out.ip = cc.info->addr.addr; + out.ip.setScopeId(QString()); + out.network = cc.info->network; + out.port = cc.info->addr.port; + out.priority = cc.info->priority; + out.protocol = "udp"; + if (cc.info->type != IceComponent::HostType) { + out.rel_addr = cc.info->related.addr; + out.rel_addr.setScopeId(QString()); + out.rel_port = cc.info->related.port; + } else { + out.rel_addr = QHostAddress(); + out.rel_port = -1; + } + out.rem_addr = QHostAddress(); + out.rem_port = -1; + out.type = candidateType_to_string(cc.info->type); + } - if(a.priority > b.priority) - return -1; - else if(b.priority > a.priority) - return 1; + void dumpCandidatesAndStart() + { + QList list; + for (auto const &cc : std::as_const(localCandidates)) { + Ice176::Candidate c; + toOutCandidate(cc, c); + list += c; + } + if (list.size()) + emit q->localCandidatesReady(list); - return 0; + state = Started; + emit q->started(); + if (mode == Responder) + doPairing(localCandidates, remoteCandidates); + } + + QString generateIdForCandidate() + { + QString id; + do { + id = IceAgent::randomCredential(10); + } while (std::find_if(localCandidates.begin(), localCandidates.end(), + [&id](auto const &c) { return c.info->id == id; }) + != localCandidates.end()); + return id; + } + + void tryReadyToSendMedia() + { + if (readyToSendMedia) { + return; + } + bool allowNotNominatedData = (localFeatures & NotNominatedData) && (remoteFeatures & NotNominatedData); + // if both follow RFC8445 and allow to send data on any valid pair + if (!std::all_of(components.begin(), components.end(), + [&](auto &c) { return (allowNotNominatedData && c.hasValidPairs) || c.hasNominatedPairs; })) { + return; + } +#ifdef ICE_DEBUG + iceDebug("Ready to send media!"); + for (auto &c : components) { + if (c.selectedPair) + iceDebug(" C%d: selected pair: %s (base: %s)", c.id, qPrintable(*c.selectedPair), + qPrintable(c.selectedPair->local->base)); + else { + iceDebug(" C%d: any pair from valid list", c.id); + iceDebug(" highest: %s", qPrintable(*c.highestPair)); + } + } +#endif + readyToSendMedia = true; + emit q->readyToSendMedia(); + } + + void insertIntoValidList(int componentId, CandidatePair::Ptr pair) + { + auto &component = *findComponent(componentId); + if (!component.selectedPair) { // no final pair yet + // find position to insert in sorted list of valid pairs + auto insIt = std::upper_bound( + checkList.validPairs.begin(), checkList.validPairs.end(), pair, [](auto &item, auto &toins) { + return item->priority == toins->priority + ? item->local->componentId < toins->local->componentId + : item->priority >= toins->priority; // inverted since we need high priority first + }); + + bool highest = false; + if (!component.highestPair || component.highestPair->priority < pair->priority) { + component.highestPair = pair; + highest = true; + } + checkList.validPairs.insert(insIt, pair); // nominated and highest priority first + iceDebug("C%d: insert to valid list %s%s", component.id, qPrintable(*pair), + highest ? " (as highest priority)" : ""); + } + } + + void onNewValidPair(CandidatePair::Ptr pair) + { + auto &component = *findComponent(pair->local->componentId); + bool alreadyInValidList = pair->isValid; + pair->isValid = true; + pair->state = PSucceeded; // what if it was in progress? + + component.hasValidPairs = true; + + // mark all with same foundation as Waiting to prioritize them (see RFC8445 7.2.5.3.3) + for (auto &p : checkList.pairs) + if (p->state == PFrozen && p->foundation == pair->foundation) + p->state = PWaiting; + + if (!alreadyInValidList) + insertIntoValidList(component.id, pair); + + optimizeCheckList(component.id); + + // if (c.lowOverhead) { // commented out since we need turn permissions for all components + iceDebug("component is flagged for low overhead. setting up for %s", qPrintable(*pair)); + auto &cc = localCandidates[findLocalCandidate(pair->local->addr)]; + component.ic->flagPathAsLowOverhead(cc.id, pair->remote->addr); + //} + + if (pair->isNominated) { + component.hasNominatedPairs = true; + bool agrNom = bool((mode == Initiator ? localFeatures : remoteFeatures) & AggressiveNomination); + if (!agrNom) { + setSelectedPair(component.id); + } else + setupNominationTimer(component.id); + } else + setupNominationTimer(component.id); + tryReadyToSendMedia(); + } + + void handlePairBindingSuccess(CandidatePair::Ptr pair) + { + /* + RFC8445 7.2.5.2.1. Non-Symmetric Transport Addresses + tells us addr:port of source->dest of request MUST match with dest<-source of the response, + and we should mark the pair as failed if doesn't match. + But StunTransaction already does this for us in its checkActiveAndFrom. + So it will fail with timeout instead if response comes from a wrong address. + */ + + StunBinding *binding = pair->binding; + // pair->isValid = true; + pair->state = CandidatePairState::PSucceeded; + bool isTriggeredForNominated = pair->isTriggeredForNominated; + bool isNominatedByInitiator = mode == Initiator && binding->useCandidate(); + bool finalNomination = pair->finalNomination; + auto &component = *findComponent(pair->local->componentId); + + iceDebug("check success for %s", qPrintable(QString(*pair))); + + // RFC8445 7.2.5.3.1. Discovering Peer-Reflexive Candidates + auto mappedAddr = binding->reflexiveAddress(); + // skip "If the valid pair equals the pair that generated the check" + if (pair->local->addr != binding->reflexiveAddress()) { + // so mapped address doesn't match with local candidate sending binding request. + // gotta find/create one + auto locIt = std::find_if(localCandidates.begin(), localCandidates.end(), [&](const auto &c) { + return (c.info->base == mappedAddr || c.info->addr == mappedAddr) + && c.info->componentId == component.id; + }); + if (locIt == localCandidates.end()) { + // RFC8445 7.2.5.3.1. Discovering Peer-Reflexive Candidates + // new peer-reflexive local candidate discovered + component.ic->addLocalPeerReflexiveCandidate(mappedAddr, pair->local, binding->priority()); + // find just inserted prflx candidate + locIt = std::find_if(localCandidates.begin(), localCandidates.end(), + [&](const auto &c) { return c.info->addr == mappedAddr; }); + Q_ASSERT(locIt != localCandidates.end()); + // local candidate wasn't found, so it wasn't on the checklist RFC8445 7.2.5.3.1.3 + // allow v4/v6 proto mismatch in case NAT does magic + pair = makeCandidatesPair(locIt->info, pair->remote); + } else { + // local candidate found. If it's a part of a pair on checklist, we have to add this pair to valid list, + // otherwise we have to create a new pair and add it to valid list + auto it = std::find_if(checkList.pairs.begin(), checkList.pairs.end(), [&](auto const &p) { + return p->local->base == locIt->info->base && p->remote->addr == pair->remote->addr + && p->local->componentId == locIt->info->componentId; + }); + if (it == checkList.pairs.end()) { + // allow v4/v6 proto mismatch in case NAT does magic + pair = makeCandidatesPair(locIt->info, pair->remote); + } else { + pair = *it; + iceDebug("mapped address belongs to another pair on checklist %s", qPrintable(QString(*pair))); + } + } + } + + if (!pair) { + qWarning("binding success but failed to build a pair with mapped address %s!", qPrintable(mappedAddr)); + return; + } + + pair->isTriggeredForNominated = isTriggeredForNominated; + pair->finalNomination = finalNomination; + pair->isNominated = isTriggeredForNominated || isNominatedByInitiator; + onNewValidPair(pair); + } + + void handlePairBindingError(CandidatePair::Ptr pair, XMPP::StunBinding::Error) + { + Q_ASSERT(state != Stopped); + if (state == Stopping) + return; // we don't care about late errors + + if (state == Active) { + iceDebug("todo! binding error ignored in Active state"); + return; // TODO hadle keep-alive binding properly + } + + iceDebug("check failed for %s", qPrintable(*pair)); + auto &c = *findComponent(pair->local->componentId); + pair->state = CandidatePairState::PFailed; + if (pair->isValid) { // RFC8445 7.2.5.3.4. Updating the Nominated Flag / about failure + checkList.validPairs.removeOne(pair); + pair->isValid = false; + if (c.highestPair == pair) { + // the failed binding is nomination or triggered after receiving success on canceled binding + c.highestPair.reset(); + } + } + + if ((c.nominating && pair->finalNomination) + || (!(remoteFeatures & AggressiveNomination) && pair->isTriggeredForNominated)) { + + if (pair->isTriggeredForNominated) + qInfo("Failed to do triggered check for nominated selectedPair. set ICE status to failed"); + else + qInfo("Failed to nominate selected pair. set ICE status to failed"); + stop(); + emit q->error(ErrorDisconnected); + return; + } + + // if not nominating but use-candidate then I'm initiator with aggressive nomination. It's Ok to fail. + // if nominating but not use-candidate then I'm initiator and something not important failed } private slots: @@ -697,68 +1318,50 @@ private slots: void ic_candidateAdded(const XMPP::IceComponent::Candidate &_cc) { IceComponent::Candidate cc = _cc; - cc.info.id = randomCredential(10); // FIXME: ensure unique - cc.info.foundation = "0"; // FIXME - // TODO + + cc.info->id = generateIdForCandidate(); + localCandidates += cc; - printf("C%d: candidate added: %s;%d\n", cc.info.componentId, qPrintable(cc.info.addr.addr.toString()), cc.info.addr.port); + iceDebug("C%d: candidate added: %s %s;%d", cc.info->componentId, + qPrintable(candidateType_to_string(cc.info->type)), qPrintable(cc.info->addr.addr.toString()), + cc.info->addr.port); - if(!iceTransports.contains(cc.iceTransport)) - { - connect(cc.iceTransport, SIGNAL(readyRead(int)), SLOT(it_readyRead(int))); - connect(cc.iceTransport, SIGNAL(datagramsWritten(int,int,QHostAddress,int)), SLOT(it_datagramsWritten(int,int,QHostAddress,int))); + if (!iceTransports.contains(cc.iceTransport)) { + connect(cc.iceTransport.data(), &IceTransport::readyRead, this, &Private::it_readyRead); + connect(cc.iceTransport.data(), &IceTransport::datagramsWritten, this, &Private::it_datagramsWritten); iceTransports += cc.iceTransport; } - if(state == Started && useTrickle) - { + if (!localHostGatheringFinished) + return; // all local IPs will be reported at once + + if (localFeatures & Trickle) { QList list; Ice176::Candidate c; - c.component = cc.info.componentId; - c.foundation = cc.info.foundation; - c.generation = 0; // TODO - c.id = cc.info.id; - c.ip = cc.info.addr.addr; - c.ip.setScopeId(QString()); - c.network = cc.info.network; - c.port = cc.info.addr.port; - c.priority = cc.info.priority; - c.protocol = "udp"; - if(cc.info.type != IceComponent::HostType) - { - c.rel_addr = cc.info.base.addr; - c.rel_addr.setScopeId(QString()); - c.rel_port = cc.info.base.port; - } - else - { - c.rel_addr = QHostAddress(); - c.rel_port = -1; - } - c.rem_addr = QHostAddress(); - c.rem_port = -1; - c.type = candidateType_to_string(cc.info.type); + toOutCandidate(cc, c); list += c; emit q->localCandidatesReady(list); } + if (state == Started) { + doPairing(QList() << cc, remoteCandidates); + } } void ic_candidateRemoved(const XMPP::IceComponent::Candidate &cc) { // TODO - printf("C%d: candidate removed: %s;%d\n", cc.info.componentId, qPrintable(cc.info.addr.addr.toString()), cc.info.addr.port); + iceDebug("C%d: candidate removed: %s;%d", cc.info->componentId, qPrintable(cc.info->addr.addr.toString()), + cc.info->addr.port); QStringList idList; - for(int n = 0; n < localCandidates.count(); ++n) - { - if(localCandidates[n].id == cc.id && localCandidates[n].info.componentId == cc.info.componentId) - { + for (int n = 0; n < localCandidates.count(); ++n) { + if (localCandidates[n].id == cc.id && localCandidates[n].info->componentId == cc.info->componentId) { // FIXME: this is rather ridiculous I think - idList += localCandidates[n].info.id; + idList += localCandidates[n].info->id; localCandidates.removeAt(n); --n; // adjust position @@ -766,35 +1369,28 @@ private slots: } bool iceTransportInUse = false; - foreach(const IceComponent::Candidate &lc, localCandidates) - { - if(lc.iceTransport == cc.iceTransport) - { + for (const IceComponent::Candidate &lc : std::as_const(localCandidates)) { + if (lc.iceTransport == cc.iceTransport) { iceTransportInUse = true; break; } } - if(!iceTransportInUse) - { + if (!iceTransportInUse) { cc.iceTransport->disconnect(this); iceTransports.remove(cc.iceTransport); } - for(int n = 0; n < checkList.pairs.count(); ++n) - { - if(idList.contains(checkList.pairs[n].local.id)) - { - StunBinding *binding = checkList.pairs[n].binding; - StunTransactionPool *pool = checkList.pairs[n].pool; + for (int n = 0; n < checkList.pairs.count(); ++n) { + if (idList.contains(checkList.pairs[n]->local->id)) { + StunBinding *binding = checkList.pairs[n]->binding; + auto pool = checkList.pairs[n]->pool; delete binding; - if(pool) - { + if (pool) { pool->disconnect(this); - pool->setParent(0); - pool->deleteLater(); } + checkList.pairs[n]->pool.reset(); checkList.pairs.removeAt(n); --n; // adjust position @@ -805,267 +1401,194 @@ private slots: void ic_localFinished() { IceComponent *ic = static_cast(sender()); - int at = findComponent(ic); - Q_ASSERT(at != -1); + auto it = findComponent(ic); + Q_ASSERT(it != components.end()); + Q_ASSERT(!it->localFinished); - components[at].localFinished = true; + it->localFinished = true; - bool allFinished = true; - foreach(const Component &c, components) - { - if(!c.localFinished) - { - allFinished = false; - break; + for (const Component &c : components) { + if (!c.localFinished) { + return; } } - if(allFinished) - { - state = Started; - - emit q->started(); - - if(!useTrickle) - { - // FIXME: there should be a way to not wait if - // we know for sure there is nothing else - // possibly coming - collectTimer = new QTimer(this); - connect(collectTimer, SIGNAL(timeout()), SLOT(collect_timeout())); - collectTimer->setSingleShot(true); - collectTimer->start(4000); + localHostGatheringFinished = true; + if (localFeatures & Trickle) + dumpCandidatesAndStart(); + } + + void ic_gatheringComplete() + { + if (localGatheringComplete) + return; // wtf? Why are we here then + + for (auto const &c : components) { + if (!c.ic->isGatheringComplete()) { return; } + } + localGatheringComplete = true; - // FIXME: DOR-SS - QList list; - foreach(const IceComponent::Candidate &cc, localCandidates) - { - Ice176::Candidate c; - c.component = cc.info.componentId; - c.foundation = cc.info.foundation; - c.generation = 0; // TODO - c.id = cc.info.id; - c.ip = cc.info.addr.addr; - c.ip.setScopeId(QString()); - c.network = cc.info.network; - c.port = cc.info.addr.port; - c.priority = cc.info.priority; - c.protocol = "udp"; - if(cc.info.type != IceComponent::HostType) - { - c.rel_addr = cc.info.base.addr; - c.rel_addr.setScopeId(QString()); - c.rel_port = cc.info.base.port; - } - else - { - c.rel_addr = QHostAddress(); - c.rel_port = -1; - } - c.rem_addr = QHostAddress(); - c.rem_port = -1; - c.type = candidateType_to_string(cc.info.type); - list += c; - } - if(!list.isEmpty()) - emit q->localCandidatesReady(list); + if (localFeatures & Trickle) { // It was already started + emit q->localGatheringComplete(); + return; } + + dumpCandidatesAndStart(); } void ic_stopped() { IceComponent *ic = static_cast(sender()); - int at = findComponent(ic); - Q_ASSERT(at != -1); + auto it = findComponent(ic); + Q_ASSERT(it != components.end()); - components[at].stopped = true; + it->stopped = true; + it->nominationTimer.reset(); bool allStopped = true; - foreach(const Component &c, components) - { - if(!c.stopped) - { + for (const Component &c : components) { + if (!c.stopped) { allStopped = false; break; } } - if(allStopped) + if (allStopped) postStop(); } void ic_debugLine(const QString &line) { +#ifdef ICE_DEBUG IceComponent *ic = static_cast(sender()); - int at = findComponent(ic); - Q_ASSERT(at != -1); + auto it = findComponent(ic); + Q_ASSERT(it != components.end()); // FIXME: components are always sorted? - printf("C%d: %s\n", at + 1, qPrintable(line)); - } - - void collect_timeout() - { - collectTimer->disconnect(this); - collectTimer->deleteLater(); - collectTimer = 0; - - QList list; - foreach(const IceComponent::Candidate &cc, localCandidates) - { - Ice176::Candidate c; - c.component = cc.info.componentId; - c.foundation = cc.info.foundation; - c.generation = 0; // TODO - c.id = cc.info.id; - c.ip = cc.info.addr.addr; - c.ip.setScopeId(QString()); - c.network = cc.info.network; - c.port = cc.info.addr.port; - c.priority = cc.info.priority; - c.protocol = "udp"; - if(cc.info.type != IceComponent::HostType) - { - c.rel_addr = cc.info.base.addr; - c.rel_addr.setScopeId(QString()); - c.rel_port = cc.info.base.port; - } - else - { - c.rel_addr = QHostAddress(); - c.rel_port = -1; - } - c.rem_addr = QHostAddress(); - c.rem_port = -1; - c.type = candidateType_to_string(cc.info.type); - list += c; - } - if(!list.isEmpty()) - emit q->localCandidatesReady(list); + iceDebug("C%d: %s", it->id, qPrintable(line)); +#else + Q_UNUSED(line) +#endif } + // path is either direct or relayed void it_readyRead(int path) { IceTransport *it = static_cast(sender()); - int at = findLocalCandidate(it, path); + int at = findLocalCandidate(it, path, true); // just host or relay Q_ASSERT(at != -1); - IceComponent::Candidate &cc = localCandidates[at]; + IceComponent::Candidate &locCand = localCandidates[at]; IceTransport *sock = it; - while(sock->hasPendingDatagrams(path)) - { - QHostAddress fromAddr; - int fromPort; - QByteArray buf = sock->readDatagram(path, &fromAddr, &fromPort); + while (sock->hasPendingDatagrams(path)) { + TransportAddress fromAddr; + QByteArray buf = sock->readDatagram(path, fromAddr); - //printf("port %d: received packet (%d bytes)\n", lt->sock->localPort(), buf.size()); + // iceDebug("port %d: received packet (%d bytes)", lt->sock->localPort(), buf.size()); - QString requser = localUser + ':' + peerUser; - QByteArray reqkey = localPass.toUtf8(); + QString requser = localUser + ':' + peerUser; + QByteArray reqkey = localPass.toUtf8(); StunMessage::ConvertResult result; - StunMessage msg = StunMessage::fromBinary(buf, &result, StunMessage::MessageIntegrity | StunMessage::Fingerprint, reqkey); - if(!msg.isNull() && (msg.mclass() == StunMessage::Request || msg.mclass() == StunMessage::Indication)) - { - printf("received validated request or indication from %s:%d\n", qPrintable(fromAddr.toString()), fromPort); - QString user = QString::fromUtf8(msg.attribute(0x0006)); // USERNAME - if(requser != user) - { - printf("user [%s] is wrong. it should be [%s]. skipping\n", qPrintable(user), qPrintable(requser)); + StunMessage msg = StunMessage::fromBinary(buf, &result, + StunMessage::MessageIntegrity | StunMessage::Fingerprint, reqkey); + if (!msg.isNull() && (msg.mclass() == StunMessage::Request || msg.mclass() == StunMessage::Indication)) { + iceDebug("received validated request or indication from %s", qPrintable(fromAddr)); + QString user = QString::fromUtf8(msg.attribute(StunTypes::USERNAME)); + if (requser != user) { + iceDebug("user [%s] is wrong. it should be [%s]. skipping", qPrintable(user), + qPrintable(requser)); continue; } - if(msg.method() != 0x001) - { - printf("not a binding request. skipping\n"); + if (msg.method() != StunTypes::Binding) { + iceDebug("not a binding request. skipping"); continue; } StunMessage response; response.setClass(StunMessage::SuccessResponse); - response.setMethod(0x001); + response.setMethod(StunTypes::Binding); response.setId(msg.id()); - quint16 port16 = fromPort; - quint32 addr4 = fromAddr.toIPv4Address(); - QByteArray val(8, 0); - quint8 *p = (quint8 *)val.data(); - const quint8 *magic = response.magic(); - p[0] = 0; - p[1] = 0x01; - p[2] = (port16 >> 8) & 0xff; - p[2] ^= magic[0]; - p[3] = port16 & 0xff; - p[3] ^= magic[1]; - p[4] = (addr4 >> 24) & 0xff; - p[4] ^= magic[0]; - p[5] = (addr4 >> 16) & 0xff; - p[5] ^= magic[1]; - p[6] = (addr4 >> 8) & 0xff; - p[6] ^= magic[2]; - p[7] = addr4 & 0xff; - p[7] ^= magic[3]; - QList list; - StunMessage::Attribute attr; - attr.type = 0x0020; - attr.value = val; + StunMessage::Attribute attr; + attr.type = StunTypes::XOR_MAPPED_ADDRESS; + attr.value = StunTypes::createXorPeerAddress(fromAddr, response.magic(), response.id()); list += attr; response.setAttributes(list); QByteArray packet = response.toBinary(StunMessage::MessageIntegrity | StunMessage::Fingerprint, reqkey); - sock->writeDatagram(path, packet, fromAddr, fromPort); - } - else - { - QByteArray reskey = peerPass.toUtf8(); - StunMessage msg = StunMessage::fromBinary(buf, &result, StunMessage::MessageIntegrity | StunMessage::Fingerprint, reskey); - if(!msg.isNull() && (msg.mclass() == StunMessage::SuccessResponse || msg.mclass() == StunMessage::ErrorResponse)) - { - printf("received validated response\n"); + sock->writeDatagram(path, packet, fromAddr); + + if (state != Started) // only in started state we do triggered checks + return; + + auto it = std::find_if( + remoteCandidates.begin(), remoteCandidates.end(), [&](IceComponent::CandidateInfo::Ptr remCand) { + return remCand->componentId == locCand.info->componentId && remCand->addr == fromAddr; + }); + bool nominated = false; + if (mode == Responder) + nominated = msg.hasAttribute(StunTypes::USE_CANDIDATE); + if (it == remoteCandidates.end()) { + // RFC8445 7.3.1.3. Learning Peer-Reflexive Candidates + iceDebug("found NEW remote prflx! %s", qPrintable(fromAddr)); + quint32 priority; + StunTypes::parsePriority(msg.attribute(StunTypes::PRIORITY), &priority); + auto remCand + = IceComponent::CandidateInfo::makeRemotePrflx(locCand.info->componentId, fromAddr, priority); + remoteCandidates += remCand; + doTriggeredCheck(locCand, remCand, nominated); + } else { + doTriggeredCheck(locCand, *it, nominated); + } + } else { + QByteArray reskey = peerPass.toUtf8(); + StunMessage msg = StunMessage::fromBinary( + buf, &result, StunMessage::MessageIntegrity | StunMessage::Fingerprint, reskey); + if (!msg.isNull() + && (msg.mclass() == StunMessage::SuccessResponse || msg.mclass() == StunMessage::ErrorResponse)) { + iceDebug("received validated response from %s to %s", qPrintable(fromAddr), + qPrintable(locCand.info->addr)); // FIXME: this is so gross and completely defeats the point of having pools - for(int n = 0; n < checkList.pairs.count(); ++n) - { - CandidatePair &pair = checkList.pairs[n]; - if(pair.local.addr.addr == cc.info.addr.addr && pair.local.addr.port == cc.info.addr.port) + for (int n = 0; n < checkList.pairs.count(); ++n) { + CandidatePair &pair = *checkList.pairs[n]; + if (pair.state == PInProgress && pair.local->addr.addr == locCand.info->addr.addr + && pair.local->addr.port == locCand.info->addr.port) pair.pool->writeIncomingMessage(msg); } - } - else - { - //printf("received some non-stun or invalid stun packet\n"); + } else { + // iceDebug("received some non-stun or invalid stun packet"); // FIXME: i don't know if this is good enough - if(StunMessage::isProbablyStun(buf)) - { - printf("unexpected stun packet (loopback?), skipping.\n"); + if (StunMessage::isProbablyStun(buf)) { + iceDebug("unexpected stun packet (loopback?), skipping."); continue; } int at = -1; - for(int n = 0; n < checkList.pairs.count(); ++n) - { - CandidatePair &pair = checkList.pairs[n]; - if(pair.local.addr.addr == cc.info.addr.addr && pair.local.addr.port == cc.info.addr.port) - { + for (int n = 0; n < checkList.pairs.count(); ++n) { + CandidatePair &pair = *checkList.pairs[n]; + if (pair.local->addr.addr == locCand.info->addr.addr + && pair.local->addr.port == locCand.info->addr.port) { at = n; break; } } - if(at == -1) - { - printf("the local transport does not seem to be associated with a candidate?!\n"); + if (at == -1) { + iceDebug("the local transport does not seem to be associated with a candidate?!"); continue; } - int componentIndex = checkList.pairs[at].local.componentId - 1; - //printf("packet is considered to be application data for component index %d\n", componentIndex); + int componentIndex = checkList.pairs[at]->local->componentId - 1; + // iceDebug("packet is considered to be application data for component index %d", componentIndex); // FIXME: this assumes components are ordered by id in our local arrays in[componentIndex] += buf; @@ -1075,129 +1598,22 @@ private slots: } } - void it_datagramsWritten(int path, int count, const QHostAddress &addr, int port) + void it_datagramsWritten(int path, int count, const TransportAddress &addr) { // TODO Q_UNUSED(path); Q_UNUSED(count); Q_UNUSED(addr); - Q_UNUSED(port); - } - - void pool_outgoingMessage(const QByteArray &packet, const QHostAddress &addr, int port) - { - Q_UNUSED(addr); - Q_UNUSED(port); - - StunTransactionPool *pool = static_cast(sender()); - int at = -1; - for(int n = 0; n < checkList.pairs.count(); ++n) - { - if(checkList.pairs[n].pool == pool) - { - at = n; - break; - } - } - if(at == -1) // FIXME: assert? - return; - - CandidatePair &pair = checkList.pairs[at]; - - at = findLocalCandidate(pair.local.addr.addr, pair.local.addr.port); - if(at == -1) // FIXME: assert? - return; - - IceComponent::Candidate &lc = localCandidates[at]; - - IceTransport *sock = lc.iceTransport; - int path = lc.path; - - printf("connectivity check from %s:%d to %s:%d\n", qPrintable(pair.local.addr.addr.toString()), pair.local.addr.port, qPrintable(pair.remote.addr.addr.toString()), pair.remote.addr.port); - sock->writeDatagram(path, packet, pair.remote.addr.addr, pair.remote.addr.port); - } - - void binding_success() - { - StunBinding *binding = static_cast(sender()); - int at = -1; - for(int n = 0; n < checkList.pairs.count(); ++n) - { - if(checkList.pairs[n].binding == binding) - { - at = n; - break; - } - } - if(at == -1) - return; - - printf("check success\n"); - - CandidatePair &pair = checkList.pairs[at]; - - // TODO: if we were cool, we'd do something with the peer - // reflexive address received - - // TODO: we're also supposed to do triggered checks. except - // that currently we check everything anyway so this is not - // relevant - - // check if there's a candidate already valid - at = -1; - for(int n = 0; n < checkList.pairs.count(); ++n) - { - if(checkList.pairs[n].local.componentId == pair.local.componentId && checkList.pairs[n].isValid) - { - at = n; - break; - } - } - - pair.isValid = true; - - if(at == -1) - { - int at = findComponent(pair.local.componentId); - Component &c = components[at]; - if(c.lowOverhead) - { - printf("component is flagged for low overhead. setting up for %s;%d -> %s;%d\n", - qPrintable(pair.local.addr.addr.toString()), pair.local.addr.port, qPrintable(pair.remote.addr.addr.toString()), pair.remote.addr.port); - at = findLocalCandidate(pair.local.addr.addr, pair.local.addr.port); - IceComponent::Candidate &cc = localCandidates[at]; - c.ic->flagPathAsLowOverhead(cc.id, pair.remote.addr.addr, pair.remote.addr.port); - } - - emit q->componentReady(pair.local.componentId - 1); - } - else - { - printf("component %d already active, not signalling\n", pair.local.componentId); - } } }; -Ice176::Ice176(QObject *parent) : - QObject(parent) -{ - d = new Private(this); -} +Ice176::Ice176(QObject *parent) : QObject(parent) { d = new Private(this); } -Ice176::~Ice176() -{ - delete d; -} +Ice176::~Ice176() { delete d; } -void Ice176::reset() -{ - d->reset(); -} +void Ice176::reset() { d->reset(); } -void Ice176::setProxy(const TurnClient::Proxy &proxy) -{ - d->proxy = proxy; -} +void Ice176::setProxy(const TurnClient::Proxy &proxy) { d->proxy = proxy; } void Ice176::setPortReserver(UdpPortReserver *portReserver) { @@ -1206,57 +1622,47 @@ void Ice176::setPortReserver(UdpPortReserver *portReserver) d->portReserver = portReserver; } -void Ice176::setLocalAddresses(const QList &addrs) -{ - d->updateLocalAddresses(addrs); -} +void Ice176::setLocalAddresses(const QList &addrs) { d->updateLocalAddresses(addrs); } -void Ice176::setExternalAddresses(const QList &addrs) -{ - d->updateExternalAddresses(addrs); -} +void Ice176::setExternalAddresses(const QList &addrs) { d->updateExternalAddresses(addrs); } -void Ice176::setStunBindService(const QHostAddress &addr, int port) -{ - d->stunBindAddr = addr; - d->stunBindPort = port; -} +void Ice176::setStunBindService(const QHostAddress &addr, quint16 port) { d->stunBindAddr = { addr, port }; } -void Ice176::setStunRelayUdpService(const QHostAddress &addr, int port, const QString &user, const QCA::SecureArray &pass) +void Ice176::setStunRelayUdpService(const QHostAddress &addr, quint16 port, const QString &user, + const QCA::SecureArray &pass) { - d->stunRelayUdpAddr = addr; - d->stunRelayUdpPort = port; + d->stunRelayUdpAddr = { addr, port }; d->stunRelayUdpUser = user; d->stunRelayUdpPass = pass; } -void Ice176::setStunRelayTcpService(const QHostAddress &addr, int port, const QString &user, const QCA::SecureArray &pass) +void Ice176::setStunRelayTcpService(const QHostAddress &addr, quint16 port, const QString &user, + const QCA::SecureArray &pass) { - d->stunRelayTcpAddr = addr; - d->stunRelayTcpPort = port; + d->stunRelayTcpAddr = { addr, port }; d->stunRelayTcpUser = user; d->stunRelayTcpPass = pass; } -void Ice176::setUseLocal(bool enabled) -{ - d->useLocal = enabled; -} +void Ice176::setAllowIpExposure(bool enabled) { d->allowIpExposure = enabled; } -void Ice176::setUseStunBind(bool enabled) +void Ice176::setStunDiscoverer(AbstractStunDisco *discoverer) { - d->useStunBind = enabled; + discoverer->setParent(this); + d->stunDiscoverer = discoverer; + connect(d->stunDiscoverer, &AbstractStunDisco::serviceAdded, d, &Private::stunFound); + connect(d->stunDiscoverer, &AbstractStunDisco::serviceModified, d, &Private::stunModified); + connect(d->stunDiscoverer, &AbstractStunDisco::serviceRemoved, d, &Private::stunRemoved); + connect(d->stunDiscoverer, &AbstractStunDisco::discoFinished, d, &Private::stunDiscoFinished); } -void Ice176::setUseStunRelayUdp(bool enabled) -{ - d->useStunRelayUdp = enabled; -} +void Ice176::setUseLocal(bool enabled) { d->useLocal = enabled; } -void Ice176::setUseStunRelayTcp(bool enabled) -{ - d->useStunRelayTcp = enabled; -} +void Ice176::setUseStunBind(bool enabled) { d->useStunBind = enabled; } + +void Ice176::setUseStunRelayUdp(bool enabled) { d->useStunRelayUdp = enabled; } + +void Ice176::setUseStunRelayTcp(bool enabled) { d->useStunRelayTcp = enabled; } void Ice176::setComponentCount(int count) { @@ -1265,10 +1671,9 @@ void Ice176::setComponentCount(int count) d->componentCount = count; } -void Ice176::setLocalCandidateTrickle(bool enabled) -{ - d->useTrickle = enabled; -} +void Ice176::setLocalFeatures(const Features &features) { d->localFeatures = features; } + +void Ice176::setRemoteFeatures(const Features &features) { d->remoteFeatures = features; } void Ice176::start(Mode mode) { @@ -1276,69 +1681,139 @@ void Ice176::start(Mode mode) d->start(); } -void Ice176::stop() -{ - d->stop(); -} +void Ice176::stop() { d->stop(); } -QString Ice176::localUfrag() const -{ - return d->localUser; -} +bool Ice176::isStopped() const { return d->state == Private::Stopped; } -QString Ice176::localPassword() const -{ - return d->localPass; -} +void Ice176::startChecks() { d->startChecks(); } -void Ice176::setPeerUfrag(const QString &ufrag) -{ - d->peerUser = ufrag; -} +QString Ice176::localUfrag() const { return d->localUser; } -void Ice176::setPeerPassword(const QString &pass) +QString Ice176::localPassword() const { return d->localPass; } + +void Ice176::setRemoteCredentials(const QString &ufrag, const QString &pass) { + // TODO detect restart + d->peerUser = ufrag; d->peerPass = pass; } -void Ice176::addRemoteCandidates(const QList &list) -{ - d->addRemoteCandidates(list); -} +void Ice176::addRemoteCandidates(const QList &list) { d->addRemoteCandidates(list); } -bool Ice176::hasPendingDatagrams(int componentIndex) const +void Ice176::setRemoteGatheringComplete() { - return !d->in[componentIndex].isEmpty(); + iceDebug("Got remote gathering complete signal"); + d->setRemoteGatheringComplete(); } -QByteArray Ice176::readDatagram(int componentIndex) +void Ice176::setRemoteSelectedCandidadates(const QList &list) { - return d->in[componentIndex].takeFirst(); + Q_UNUSED(list); + // This thing is likely useless since ICE knows exactly which pairs are nominated. } -void Ice176::writeDatagram(int componentIndex, const QByteArray &datagram) -{ - d->write(componentIndex, datagram); -} +bool Ice176::canSendMedia() const { return d->readyToSendMedia; } -void Ice176::flagComponentAsLowOverhead(int componentIndex) -{ - d->flagComponentAsLowOverhead(componentIndex); -} +bool Ice176::hasPendingDatagrams(int componentIndex) const { return !d->in[componentIndex].isEmpty(); } + +QByteArray Ice176::readDatagram(int componentIndex) { return d->in[componentIndex].takeFirst(); } + +void Ice176::writeDatagram(int componentIndex, const QByteArray &datagram) { d->write(componentIndex, datagram); } + +void Ice176::flagComponentAsLowOverhead(int componentIndex) { d->flagComponentAsLowOverhead(componentIndex); } bool Ice176::isIPv6LinkLocalAddress(const QHostAddress &addr) { Q_ASSERT(addr.protocol() == QAbstractSocket::IPv6Protocol); Q_IPV6ADDR addr6 = addr.toIPv6Address(); - quint16 hi = addr6[0]; + quint16 hi = addr6[0]; hi <<= 8; hi += addr6[1]; - if((hi & 0xffc0) == 0xfe80) - return true; - else - return false; + return (hi & 0xffc0) == 0xfe80; +} + +void Ice176::changeThread(QThread *thread) +{ + for (auto &c : d->localCandidates) { + if (c.iceTransport) + c.iceTransport->changeThread(thread); + } + for (auto &p : d->checkList.pairs) { + if (p->pool) + p->pool->moveToThread(thread); + } + moveToThread(thread); +} + +bool Ice176::isLocalGatheringComplete() const { return d->localGatheringComplete; } + +bool Ice176::isActive() const { return d->state == Private::Active; } + +QList Ice176::selectedCandidates() const +{ + QList ret; + for (auto const &c : d->components) { + if (c.selectedPair) { + const auto &local = c.selectedPair->local; + ret.append({ local->addr.addr, local->addr.port, local->componentId }); + } + } + return ret; } +QList Ice176::availableNetworkAddresses() +{ + QList listenAddrs; + auto const interfaces = QNetworkInterface::allInterfaces(); +#ifdef Q_OS_UNIX + static const auto ignored + = QStringList { QStringLiteral("vmnet"), QStringLiteral("vnic"), QStringLiteral("vboxnet") }; +#endif + for (const QNetworkInterface &ni : interfaces) { + if ((ni.flags() & (QNetworkInterface::IsRunning | QNetworkInterface::IsUp)) + != (QNetworkInterface::IsRunning | QNetworkInterface::IsUp) + || ni.flags() & QNetworkInterface::IsLoopBack +#ifdef Q_OS_UNIX + || std::any_of(ignored.begin(), ignored.end(), [&ni](auto const &ign) { return ni.name().startsWith(ign); }) +#elif defined(Q_OS_WIN) + || ni.humanReadableName().contains(QStringLiteral("VMnet")) +#endif + ) + continue; + + QList entries = ni.addressEntries(); + for (const QNetworkAddressEntry &na : std::as_const(entries)) { +#if QT_VERSION >= QT_VERSION_CHECK(5, 11, 0) + if (na.preferredLifetime().hasExpired() || na.netmask().isNull()) +#else + if (na.netmask().isNull()) +#endif + continue; + + QHostAddress h = na.ip(); + if (h.isNull() || h.isLoopback() + || !(h.protocol() == QAbstractSocket::IPv4Protocol || h.protocol() == QAbstractSocket::IPv6Protocol) + || (h.protocol() == QAbstractSocket::IPv4Protocol && h.toIPv4Address() < 0x01000000)) + continue; + + // don't put the same address in twice. + // this also means that if there are + // two link-local ipv6 interfaces + // with the exact same address, we + // only use the first one + if (listenAddrs.contains(h)) + continue; + + // TODO review if the next condition is needed (and the above too) + if (h.protocol() == QAbstractSocket::IPv6Protocol && XMPP::Ice176::isIPv6LinkLocalAddress(h)) + h.setScopeId(ni.name()); + listenAddrs += h; + } + } + + return sortAddrs(listenAddrs); } +} // namespace XMPP + #include "ice176.moc" diff --git a/src/irisnet/noncore/ice176.h b/src/irisnet/noncore/ice176.h index ac120af8..b589f61b 100644 --- a/src/irisnet/noncore/ice176.h +++ b/src/irisnet/noncore/ice176.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009,2010 Barracuda Networks, Inc. + * Copyright (C) 2009-2010 Barracuda Networks, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -19,93 +19,69 @@ #ifndef ICE176_H #define ICE176_H +#include "turnclient.h" + +#include #include #include -#include -#include "turnclient.h" namespace QCA { - class SecureArray; +class SecureArray; } namespace XMPP { - class UdpPortReserver; +class AbstractStunDisco; -class Ice176 : public QObject -{ +class Ice176 : public QObject { Q_OBJECT public: - enum Error - { - ErrorGeneric - }; + enum Error { ErrorGeneric, ErrorDisconnected }; - enum Mode - { - Initiator, - Responder - }; + enum Mode { Initiator, Responder }; - class LocalAddress - { + class LocalAddress { public: QHostAddress addr; - int network; // -1 = unknown - bool isVpn; - - LocalAddress() : - network(-1), - isVpn(false) - { - } + int network = -1; // -1 = unknown + bool isVpn = false; }; - class ExternalAddress - { + class ExternalAddress { public: LocalAddress base; QHostAddress addr; - int portBase; // -1 = same as base + int portBase; // -1 = same as base - ExternalAddress() : - portBase(-1) - { - } + ExternalAddress() : portBase(-1) { } }; - class Candidate - { + class Candidate { public: - int component; - QString foundation; - int generation; - QString id; + int component = -1; + QString foundation; + int generation = -1; + QString id; QHostAddress ip; - int network; // -1 = unknown - int port; - int priority; - QString protocol; + int network = -1; // -1 = unknown + int port = -1; + int priority = -1; + QString protocol; QHostAddress rel_addr; - int rel_port; + int rel_port = -1; QHostAddress rem_addr; - int rem_port; - QString type; - - Candidate() : - component(-1), - generation(-1), - network(-1), - port(-1), - priority(-1), - rel_port(-1), - rem_port(-1) - { - } + int rem_port = -1; + QString type; + }; + + struct SelectedCandidate { + QHostAddress ip; + int port = -1; + int componentId = -1; }; - Ice176(QObject *parent = 0); + Ice176(QObject *parent = nullptr); ~Ice176(); void reset(); @@ -122,9 +98,11 @@ class Ice176 : public QObject // one per local address. you must set local addresses first. void setExternalAddresses(const QList &addrs); - void setStunBindService(const QHostAddress &addr, int port); - void setStunRelayUdpService(const QHostAddress &addr, int port, const QString &user, const QCA::SecureArray &pass); - void setStunRelayTcpService(const QHostAddress &addr, int port, const QString &user, const QCA::SecureArray &pass); + void setStunBindService(const QHostAddress &addr, quint16 port); // REVIEW if we need both v4 and v6? + void setStunRelayUdpService(const QHostAddress &addr, quint16 port, const QString &user, + const QCA::SecureArray &pass); + void setStunRelayTcpService(const QHostAddress &addr, quint16 port, const QString &user, + const QCA::SecureArray &pass); // these all start out enabled, but can be disabled for diagnostic // purposes @@ -132,24 +110,40 @@ class Ice176 : public QObject void setUseStunBind(bool enabled); void setUseStunRelayUdp(bool enabled); void setUseStunRelayTcp(bool enabled); + void setAllowIpExposure(bool enabled); + void setStunDiscoverer(AbstractStunDisco *discoverer); void setComponentCount(int count); - void setLocalCandidateTrickle(bool enabled); // default false - void start(Mode mode); + enum Feature { + Trickle = 0x1, // additional candidates will be sent later when discovered + AggressiveNomination = 0x2, // all the candidates are nominated. so select by priority + NotNominatedData = 0x4, // Data on valid but not nominated candidates is allowed + RTPOptimization = 0x8, // Different formula for RTO, not used in RFC8445 + GatheringComplete = 0x10 // Looks MUST in XEP-0371 but missed in XEP-0176 + }; + Q_DECLARE_FLAGS(Features, Feature) + + void setLocalFeatures(const Features &features); + void setRemoteFeatures(const Features &features); + + void start(Mode mode); // init everything and prepare candidates void stop(); + bool isStopped() const; + void startChecks(); // actually start doing checks when connection is accepted QString localUfrag() const; QString localPassword() const; - void setPeerUfrag(const QString &ufrag); - void setPeerPassword(const QString &pass); - + void setRemoteCredentials(const QString &ufrag, const QString &pass); void addRemoteCandidates(const QList &list); + void setRemoteGatheringComplete(); + void setRemoteSelectedCandidadates(const QList &list); - bool hasPendingDatagrams(int componentIndex) const; + bool canSendMedia() const; + bool hasPendingDatagrams(int componentIndex) const; QByteArray readDatagram(int componentIndex); - void writeDatagram(int componentIndex, const QByteArray &datagram); + void writeDatagram(int componentIndex, const QByteArray &datagram); // this call will ensure that TURN headers are minimized on this // component, with the drawback that packets might not be able to @@ -162,6 +156,15 @@ class Ice176 : public QObject // FIXME: this should probably be in netinterface.h or such static bool isIPv6LinkLocalAddress(const QHostAddress &addr); + void changeThread(QThread *thread); + + bool isLocalGatheringComplete() const; + bool isActive() const; + + QList selectedCandidates() const; + + static QList availableNetworkAddresses(); + signals: // indicates that the ice engine is started and is ready to receive // peer creds and remote candidates @@ -171,7 +174,10 @@ class Ice176 : public QObject void error(XMPP::Ice176::Error e); void localCandidatesReady(const QList &list); - void componentReady(int index); + void localGatheringComplete(); + void readyToSendMedia(); // Has at least one valid candidate for each component + void componentReady(int index); // has valid nominated candidate for component with index + void iceFinished(); // Final nominated candidates are selected for all components void readyRead(int componentIndex); void datagramsWritten(int componentIndex, int count); @@ -182,6 +188,7 @@ class Ice176 : public QObject Private *d; }; -} +Q_DECLARE_OPERATORS_FOR_FLAGS(Ice176::Features) +} // namespace XMPP -#endif +#endif // ICE176_H diff --git a/src/irisnet/noncore/iceabstractstundisco.h b/src/irisnet/noncore/iceabstractstundisco.h new file mode 100644 index 00000000..f26ff9b6 --- /dev/null +++ b/src/irisnet/noncore/iceabstractstundisco.h @@ -0,0 +1,54 @@ +#ifndef XMPP_ABSTRACTSTUNDISCO_H +#define XMPP_ABSTRACTSTUNDISCO_H + +#include +#include +#include +#include + +#include + +namespace XMPP { + +/** + * Monitors if new STUN services are available, changed or not available anymore. + */ +class AbstractStunDisco : public QObject { + Q_OBJECT +public: + enum Transport : std::uint8_t { Tcp, Udp }; + enum Flag : std::uint8_t { Relay = 0x01, Tls = 0x02, Restricted = 0x04 }; + Q_DECLARE_FLAGS(Flags, Flag) + + struct Service { + using Ptr = std::shared_ptr; + QString name; + QString username; + QString password; + QString host; + QList addresses; + std::uint16_t port = 0; + Transport transport; + Flags flags; + QDeadlineTimer expires; + }; + + using QObject::QObject; + + /** + * Check where initial discovery is still in progress and therefore it's worth waiting for completion + */ + virtual bool isDiscoInProgress() const = 0; + +Q_SIGNALS: + void discoFinished(); // if impl did rediscovery, it will signal when finished. required for initial start() + void serviceAdded(Service::Ptr); + void serviceRemoved(Service::Ptr); + void serviceModified(Service::Ptr); +}; + +} // namespace XMPP + +Q_DECLARE_OPERATORS_FOR_FLAGS(XMPP::AbstractStunDisco::Flags) + +#endif // XMPP_ABSTRACTSTUNDISCO_H diff --git a/src/irisnet/noncore/iceagent.cpp b/src/irisnet/noncore/iceagent.cpp new file mode 100644 index 00000000..cd90bea0 --- /dev/null +++ b/src/irisnet/noncore/iceagent.cpp @@ -0,0 +1,84 @@ +#include "iceagent.h" + +#include +#include + +namespace XMPP { + +struct Foundation { + IceComponent::CandidateType type; + const QHostAddress baseAddr; + const QHostAddress stunServAddr; + QAbstractSocket::SocketType stunRequestProto; + + bool operator==(const Foundation &f) const + { + return type == f.type && baseAddr == f.baseAddr && stunServAddr == f.stunServAddr + && stunRequestProto == f.stunRequestProto; + }; +}; + +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) +inline uint qHash(const Foundation &f, uint seed = 0) +#else +inline size_t qHash(const Foundation &f, size_t seed = 0) +#endif +{ + auto tmp = uint(f.stunRequestProto) & (uint(f.type) << 8); + return qHash(f.baseAddr, seed) ^ qHash(f.stunServAddr, seed) ^ tmp; +} + +static QChar randomPrintableChar() +{ + // 0-25 = a-z + // 26-51 = A-Z + // 52-61 = 0-9 + + uchar c = static_cast(QCA::Random::randomChar() % 62); + if (c <= 25) + return QChar::fromLatin1('a' + c); + else if (c <= 51) + return QChar::fromLatin1('A' + (c - 26)); + else + return QChar::fromLatin1('0' + (c - 52)); +} + +struct IceAgent::Private { + QHash foundations; +}; + +IceAgent *IceAgent::instance() +{ + static auto i = new IceAgent(QCoreApplication::instance()); + return i; +} + +IceAgent::~IceAgent() { } + +QString IceAgent::foundation(IceComponent::CandidateType type, const QHostAddress baseAddr, + const QHostAddress &stunServAddr, QAbstractSocket::SocketType stunRequestProto) +{ + Foundation f { type, baseAddr, stunServAddr, stunRequestProto }; + QString ret = d->foundations.value(f); + if (ret.isEmpty()) { + do { + ret = randomCredential(8); + } while (std::find_if(d->foundations.begin(), d->foundations.end(), [&](auto const &fp) { return fp == ret; }) + != d->foundations.end()); + d->foundations.insert(f, ret); + } + return ret; +} + +QString IceAgent::randomCredential(int len) +{ + QString out; + out.reserve(len); + for (int n = 0; n < len; ++n) + out += randomPrintableChar(); + return out; +} + +IceAgent::IceAgent(QObject *parent) : QObject(parent), d(new Private) { } + +} // namespace XMPP diff --git a/src/irisnet/noncore/iceagent.h b/src/irisnet/noncore/iceagent.h new file mode 100644 index 00000000..4042d594 --- /dev/null +++ b/src/irisnet/noncore/iceagent.h @@ -0,0 +1,35 @@ +#ifndef XMPP_ICEAGENT_H +#define XMPP_ICEAGENT_H + +#include "icecomponent.h" + +#include +#include + +namespace XMPP { + +class IceAgent : public QObject { + Q_OBJECT +public: + static IceAgent *instance(); + ~IceAgent(); + + QString foundation(IceComponent::CandidateType type, const QHostAddress baseAddr, + const QHostAddress &stunServAddr = QHostAddress(), + QAbstractSocket::SocketType stunRequestProto = QAbstractSocket::UnknownSocketType); + + static QString randomCredential(int len); + +private: + explicit IceAgent(QObject *parent = nullptr); + +signals: + +private: + struct Private; + std::unique_ptr d; +}; + +} // namespace XMPP + +#endif // XMPP_ICEAGENT_H diff --git a/src/irisnet/noncore/icecomponent.cpp b/src/irisnet/noncore/icecomponent.cpp index b27c7006..5f2cedd3 100644 --- a/src/irisnet/noncore/icecomponent.cpp +++ b/src/irisnet/noncore/icecomponent.cpp @@ -18,16 +18,21 @@ #include "icecomponent.h" -#include -#include -#include -#include "objectsession.h" -#include "udpportreserver.h" +#include "iceagent.h" #include "icelocaltransport.h" #include "iceturntransport.h" +#include "objectsession.h" +#include "udpportreserver.h" -namespace XMPP { +#include +#include +#include +#include +#include + +template constexpr std::add_const_t &as_const(T &t) noexcept { return t; } +namespace XMPP { static int calc_priority(int typePref, int localPref, int componentId) { Q_ASSERT(typePref >= 0 && typePref <= 126); @@ -40,307 +45,235 @@ static int calc_priority(int typePref, int localPref, int componentId) return priority; } -class IceComponent::Private : public QObject -{ +class IceComponent::Private : public QObject { Q_OBJECT public: - class Config - { + class Config { public: QList localAddrs; + + // for example manually provided external address mapped to every local QList extAddrs; - QHostAddress stunBindAddr; - int stunBindPort; + TransportAddress stunBindAddr; - QHostAddress stunRelayUdpAddr; - int stunRelayUdpPort; - QString stunRelayUdpUser; + TransportAddress stunRelayUdpAddr; + QString stunRelayUdpUser; QCA::SecureArray stunRelayUdpPass; - QHostAddress stunRelayTcpAddr; - int stunRelayTcpPort; - QString stunRelayTcpUser; + TransportAddress stunRelayTcpAddr; + QString stunRelayTcpUser; QCA::SecureArray stunRelayTcpPass; }; - class LocalTransport - { + class LocalTransport { public: - QUdpSocket *qsock; - bool borrowedSocket; - QHostAddress addr; - IceLocalTransport *sock; - int network; - bool isVpn; - bool started; - bool stun_started; - bool stun_finished, turn_finished; - QHostAddress extAddr; - bool ext_finished; + QUdpSocket *qsock; + QHostAddress addr; + QSharedPointer sock; + int network; + bool isVpn; + bool started; + bool stun_started; + bool stun_finished, turn_finished; // candidates emitted + QHostAddress extAddr; + bool ext_finished; + bool borrowed = false; LocalTransport() : - qsock(0), - borrowedSocket(false), - sock(0), - network(-1), - isVpn(false), - started(false), - stun_started(false), - stun_finished(false), - turn_finished(false), + network(-1), isVpn(false), started(false), stun_started(false), stun_finished(false), turn_finished(false), ext_finished(false) { } }; - IceComponent *q; - ObjectSession sess; - int id; - QString clientSoftware; - TurnClient::Proxy proxy; - UdpPortReserver *portReserver; - Config pending; - Config config; - bool stopping; - QList localLeap; - QList localStun; - IceTurnTransport *tt; - QList localCandidates; - QHash > channelPeers; - bool useLocal; - bool useStunBind; - bool useStunRelayUdp; - bool useStunRelayTcp; - bool local_finished; - int debugLevel; - - Private(IceComponent *_q) : - QObject(_q), - q(_q), - sess(this), - portReserver(0), - stopping(false), - tt(0), - useLocal(true), - useStunBind(true), - useStunRelayUdp(true), - useStunRelayTcp(true), - local_finished(false), - debugLevel(DL_None) + IceComponent *q; + ObjectSession sess; + int id; + QString clientSoftware; + TurnClient::Proxy proxy; + UdpPortReserver *portReserver = nullptr; + Config pending; + Config config; + bool stopping = false; + QList udpTransports; // transport for local host-only candidates + QSharedPointer tcpTurn; // tcp relay candidate + QList localCandidates; + QHash> channelPeers; + bool useLocal = true; // use local host candidates + bool useStunBind = true; + bool useStunRelayUdp = true; + bool useStunRelayTcp = true; + bool localFinished = false; + // bool stunFinished = false; + bool gatheringComplete = false; + int debugLevel = DL_Packet; + + Private(IceComponent *_q) : QObject(_q), q(_q), sess(this) { } + + ~Private() { qDeleteAll(udpTransports); } + + LocalTransport *createLocalTransport(QUdpSocket *socket, const Ice176::LocalAddress &la) { + auto lt = new LocalTransport; + lt->qsock = socket; + lt->addr = la.addr; + lt->sock = QSharedPointer::create(); + lt->sock->setDebugLevel(IceTransport::DebugLevel(debugLevel)); + lt->network = la.network; + lt->isVpn = la.isVpn; + connect(lt->sock.data(), &IceLocalTransport::started, this, &Private::lt_started); + connect(lt->sock.data(), &IceLocalTransport::stopped, this, [this, lt]() { + if (eraseLocalTransport(lt)) + tryStopped(); + }); + connect(lt->sock.data(), &IceLocalTransport::addressesChanged, this, &Private::lt_addressesChanged); + connect(lt->sock.data(), &IceLocalTransport::error, this, [this, lt](int error) { + if (error == IceLocalTransport::ErrorStun) { + lt->stun_finished = true; + tryGatheringComplete(); + } else if (error == IceLocalTransport::ErrorTurn) { + lt->turn_finished = true; + tryGatheringComplete(); + } else if (eraseLocalTransport(lt)) + tryGatheringComplete(); + }); + connect(lt->sock.data(), &IceLocalTransport::debugLine, this, &Private::lt_debugLine); + return lt; } - ~Private() + void update(QList *socketList) { - QList socketsToReturn; - - for(int n = 0; n < localLeap.count(); ++n) - { - delete localLeap[n]->sock; + Q_ASSERT(!stopping); - if(localLeap[n]->borrowedSocket) - socketsToReturn += localLeap[n]->qsock; - else - localLeap[n]->qsock->deleteLater(); + // only allow setting stun stuff once + if ((pending.stunBindAddr.isValid() && !config.stunBindAddr.isValid()) + || (pending.stunRelayUdpAddr.isValid() && !config.stunRelayUdpAddr.isValid()) + || (pending.stunRelayTcpAddr.isValid() && !config.stunRelayTcpAddr.isValid())) { + config.stunBindAddr = pending.stunBindAddr; + config.stunRelayUdpAddr = pending.stunRelayUdpAddr; + config.stunRelayUdpUser = pending.stunRelayUdpUser; + config.stunRelayUdpPass = pending.stunRelayUdpPass; + config.stunRelayTcpAddr = pending.stunRelayTcpAddr; + config.stunRelayTcpUser = pending.stunRelayTcpUser; + config.stunRelayTcpPass = pending.stunRelayTcpPass; } - if(!socketsToReturn.isEmpty()) - portReserver->returnSockets(socketsToReturn); - - qDeleteAll(localLeap); - - for(int n = 0; n < localStun.count(); ++n) - delete localStun[n]->sock; - - qDeleteAll(localStun); - - delete tt; - } - - void update(QList *socketList) - { - Q_ASSERT(!stopping); - // for now, only allow setting localAddrs once - if(!pending.localAddrs.isEmpty() && config.localAddrs.isEmpty()) - { - foreach(const Ice176::LocalAddress &la, pending.localAddrs) - { + if (!pending.localAddrs.isEmpty() && config.localAddrs.isEmpty()) { + for (const Ice176::LocalAddress &la : as_const(pending.localAddrs)) { // skip duplicate addrs - if(findLocalAddr(la.addr) != -1) - continue; - - if(!useLocal) - { - // skip out, but log the address in - // case we need it for stun - config.localAddrs += la; + if (findLocalAddr(la.addr) != -1) continue; - } - QUdpSocket *qsock = 0; - if(socketList) + QUdpSocket *qsock = nullptr; + if (useLocal && socketList) { qsock = takeFromSocketList(socketList, la.addr, this); - - bool borrowedSocket; - if(qsock) - { - borrowedSocket = true; } - else - { + bool borrowedSocket = qsock != nullptr; + if (!qsock) { // otherwise, bind to random qsock = new QUdpSocket(this); - if(!qsock->bind(la.addr, 0)) - { + if (!qsock->bind(la.addr, 0)) { delete qsock; emit q->debugLine("Warning: unable to bind to random port."); continue; } - - borrowedSocket = false; } - int port = qsock->localPort(); - config.localAddrs += la; + auto lt = createLocalTransport(qsock, la); + lt->borrowed = borrowedSocket; + udpTransports += lt; + + if (lt->addr.protocol() != QAbstractSocket::IPv6Protocol) { + lt->sock->setClientSoftwareNameAndVersion(clientSoftware); + if (useStunBind && config.stunBindAddr.isValid()) { + lt->sock->setStunBindService(config.stunBindAddr); + } + if (useStunRelayUdp && config.stunRelayUdpAddr.isValid() && !config.stunRelayUdpUser.isEmpty()) { + lt->sock->setStunRelayService(config.stunRelayUdpAddr, config.stunRelayUdpUser, + config.stunRelayUdpPass); + } + } - LocalTransport *lt = new LocalTransport; - lt->addr = la.addr; - lt->qsock = qsock; - lt->borrowedSocket = borrowedSocket; - lt->sock = new IceLocalTransport(this); - lt->sock->setDebugLevel((IceTransport::DebugLevel)debugLevel); - lt->network = la.network; - lt->isVpn = la.isVpn; - connect(lt->sock, SIGNAL(started()), SLOT(lt_started())); - connect(lt->sock, SIGNAL(stopped()), SLOT(lt_stopped())); - connect(lt->sock, SIGNAL(addressesChanged()), SLOT(lt_addressesChanged())); - connect(lt->sock, SIGNAL(error(int)), SLOT(lt_error(int))); - connect(lt->sock, SIGNAL(debugLine(QString)), SLOT(lt_debugLine(QString))); - localLeap += lt; - + int port = qsock->localPort(); lt->sock->start(qsock); - emit q->debugLine(QString("starting transport ") + la.addr.toString() + ';' + QString::number(port) + " for component " + QString::number(id)); + emit q->debugLine(QString("starting transport ") + la.addr.toString() + ';' + QString::number(port) + + " for component " + QString::number(id)); } } // extAddrs created on demand if present, but only once - if(!pending.extAddrs.isEmpty() && config.extAddrs.isEmpty()) - { + if (!pending.extAddrs.isEmpty() && config.extAddrs.isEmpty()) { config.extAddrs = pending.extAddrs; bool need_doExt = false; - foreach(LocalTransport *lt, localLeap) - { + for (auto lt : as_const(udpTransports)) { // already assigned an ext address? skip - if(!lt->extAddr.isNull()) + if (!lt->extAddr.isNull()) continue; - QHostAddress laddr = lt->sock->localAddress(); - int lport = lt->sock->localPort(); - - int at = -1; - for(int n = 0; n < config.extAddrs.count(); ++n) - { - const Ice176::ExternalAddress &ea = config.extAddrs[n]; - if(laddr.protocol() != QAbstractSocket::IPv6Protocol && ea.base.addr == laddr && (ea.portBase == -1 || ea.portBase == lport)) - { - at = n; - break; - } - } + auto laddr = lt->sock->localAddress(); + if (laddr.addr.protocol() == QAbstractSocket::IPv6Protocol) + continue; - if(at != -1) - { - lt->extAddr = config.extAddrs[at].addr; - if(lt->started) + // find external address by address of local socket (external has to be configured that way) + auto eaIt = std::find_if(config.extAddrs.constBegin(), config.extAddrs.constEnd(), [&](auto const &ea) { + return ea.base.addr == laddr.addr && (ea.portBase == -1 || ea.portBase == laddr.port); + }); + + if (eaIt != config.extAddrs.constEnd()) { + lt->extAddr = eaIt->addr; + if (lt->started) need_doExt = true; } } - if(need_doExt) - sess.defer(this, "doExt"); - } + if (need_doExt) + QTimer::singleShot(0, this, [this]() { + if (stopping) + return; - // only allow setting stun stuff once - if(!pending.stunBindAddr.isNull() && config.stunBindAddr.isNull()) - { - config.stunBindAddr = pending.stunBindAddr; - config.stunBindPort = pending.stunBindPort; - config.stunRelayUdpAddr = pending.stunRelayUdpAddr; - config.stunRelayUdpPort = pending.stunRelayUdpPort; - config.stunRelayUdpUser = pending.stunRelayUdpUser; - config.stunRelayUdpPass = pending.stunRelayUdpPass; - config.stunRelayTcpAddr = pending.stunRelayTcpAddr; - config.stunRelayTcpPort = pending.stunRelayTcpPort; - config.stunRelayTcpUser = pending.stunRelayTcpUser; - config.stunRelayTcpPass = pending.stunRelayTcpPass; - } + ObjectSessionWatcher watch(&sess); - // localStun sockets created on demand if stun settings are - // present, but only once (cannot be changed, for now) - if(((useStunBind && !config.stunBindAddr.isNull()) || (useStunRelayUdp && !config.stunRelayUdpAddr.isNull() && !config.stunRelayUdpUser.isEmpty())) && !config.localAddrs.isEmpty() && localStun.isEmpty()) - { - foreach(const Ice176::LocalAddress &la, config.localAddrs) - { - // don't setup stun ports for ipv6 - if(la.addr.protocol() == QAbstractSocket::IPv6Protocol) - continue; + for (auto lt : as_const(udpTransports)) { + if (lt->started) { + int addrAt = findLocalAddr(lt->addr); + Q_ASSERT(addrAt != -1); - LocalTransport *lt = new LocalTransport; - lt->addr = la.addr; - lt->sock = new IceLocalTransport(this); - lt->sock->setDebugLevel((IceTransport::DebugLevel)debugLevel); - lt->network = la.network; - lt->isVpn = la.isVpn; - connect(lt->sock, SIGNAL(started()), SLOT(lt_started())); - connect(lt->sock, SIGNAL(stopped()), SLOT(lt_stopped())); - connect(lt->sock, SIGNAL(addressesChanged()), SLOT(lt_addressesChanged())); - connect(lt->sock, SIGNAL(error(int)), SLOT(lt_error(int))); - connect(lt->sock, SIGNAL(debugLine(QString)), SLOT(lt_debugLine(QString))); - localStun += lt; - - lt->sock->setClientSoftwareNameAndVersion(clientSoftware); - lt->sock->start(la.addr); - emit q->debugLine(QString("starting transport ") + la.addr.toString() + ";(dyn)" + " for component " + QString::number(id)); - } + ensureExt(lt, addrAt); // will emit candidateAdded if everything goes well + if (!watch.isValid()) + return; + } + } + }); } - if((!config.stunBindAddr.isNull() || !config.stunRelayUdpAddr.isNull()) && !localStun.isEmpty()) - { - for(int n = 0; n < localStun.count(); ++n) - { - if(localStun[n]->started && !localStun[n]->stun_started) - tryStun(n); - } - } + if (useStunRelayTcp && config.stunRelayTcpAddr.isValid() && !config.stunRelayTcpUser.isEmpty() && !tcpTurn) { + tcpTurn = QSharedPointer::create(); + tcpTurn->setDebugLevel(IceTransport::DebugLevel(debugLevel)); + connect(tcpTurn.data(), &IceTurnTransport::started, this, &Private::tt_started); + connect(tcpTurn.data(), &IceTurnTransport::stopped, this, &Private::tt_stopped); + connect(tcpTurn.data(), &IceTurnTransport::error, this, &Private::tt_error); + connect(tcpTurn.data(), &IceTurnTransport::debugLine, this, &Private::tt_debugLine); + tcpTurn->setClientSoftwareNameAndVersion(clientSoftware); + tcpTurn->setProxy(proxy); + tcpTurn->setUsername(config.stunRelayTcpUser); + tcpTurn->setPassword(config.stunRelayTcpPass); + tcpTurn->start(config.stunRelayTcpAddr); - if(useStunRelayTcp && !config.stunRelayTcpAddr.isNull() && !config.stunRelayTcpUser.isEmpty() && !tt) - { - tt = new IceTurnTransport(this); - tt->setDebugLevel((IceTransport::DebugLevel)debugLevel); - connect(tt, SIGNAL(started()), SLOT(tt_started())); - connect(tt, SIGNAL(stopped()), SLOT(tt_stopped())); - connect(tt, SIGNAL(error(int)), SLOT(tt_error(int))); - connect(tt, SIGNAL(debugLine(QString)), SLOT(tt_debugLine(QString))); - tt->setClientSoftwareNameAndVersion(clientSoftware); - tt->setProxy(proxy); - tt->setUsername(config.stunRelayTcpUser); - tt->setPassword(config.stunRelayTcpPass); - tt->start(config.stunRelayTcpAddr, config.stunRelayTcpPort); - - emit q->debugLine(QString("starting TURN transport with server ") + config.stunRelayTcpAddr.toString() + ';' + QString::number(config.stunRelayTcpPort) + " for component " + QString::number(id)); + emit q->debugLine(QLatin1String("starting TURN transport with server ") + config.stunRelayTcpAddr + + " for component " + QString::number(id)); } - if(localLeap.isEmpty() && localStun.isEmpty() && !local_finished) - { - local_finished = true; + if (udpTransports.isEmpty() && !localFinished) { + localFinished = true; sess.defer(q, "localFinished"); } + sess.defer(this, "tryGatheringComplete"); } void stop() @@ -350,54 +283,44 @@ class IceComponent::Private : public QObject stopping = true; // nothing to stop? - if(allStopped()) - { + if (allStopped()) { sess.defer(this, "postStop"); return; } - foreach(LocalTransport *lt, localLeap) + for (LocalTransport *lt : as_const(udpTransports)) lt->sock->stop(); - foreach(LocalTransport *lt, localStun) - lt->sock->stop(); - - if(tt) - tt->stop(); + if (tcpTurn) + tcpTurn->stop(); } - int peerReflexivePriority(const IceTransport *iceTransport, int path) const + int peerReflexivePriority(QSharedPointer iceTransport, int path) const { - int addrAt = -1; - const IceLocalTransport *lt = qobject_cast(iceTransport); - if(lt) - { - bool isLocalLeap = false; - addrAt = findLocalTransport(lt, &isLocalLeap); - if(addrAt != -1 && path == 1) - { + int addrAt = -1; + const IceLocalTransport *lt = qobject_cast(iceTransport.data()); + if (lt) { + auto it = std::find_if(udpTransports.begin(), udpTransports.end(), + [&](auto const &a) { return a->sock == lt; }); + Q_ASSERT(it != udpTransports.end()); + addrAt = int(std::distance(udpTransports.begin(), it)); + if (path == 1) { // lower priority, but not as far as IceTurnTransport addrAt += 512; } - } - else if(qobject_cast(iceTransport) == tt) - { + } else if (qobject_cast(iceTransport) == tcpTurn) { // lower priority by making it seem like the last nic addrAt = 1024; } - Q_ASSERT(addrAt != -1); - return choose_default_priority(PeerReflexiveType, 65535 - addrAt, false, id); } - void flagPathAsLowOverhead(int id, const QHostAddress &addr, int port) + void flagPathAsLowOverhead(int id, const TransportAddress &addr) { int at = -1; - for(int n = 0; n < localCandidates.count(); ++n) - { - if(localCandidates[n].id == id) - { + for (int n = 0; n < localCandidates.count(); ++n) { + if (localCandidates[n].id == id) { at = n; break; } @@ -410,15 +333,43 @@ class IceComponent::Private : public QObject Candidate &c = localCandidates[at]; - TransportAddress ta(addr, port); QSet &addrs = channelPeers[c.id]; - if(!addrs.contains(ta)) - { - addrs += ta; - c.iceTransport->addChannelPeer(ta.addr, ta.port); + if (!addrs.contains(addr)) { + addrs += addr; + c.iceTransport->addChannelPeer(addr); } } + void addLocalPeerReflexiveCandidate(const TransportAddress &addr, IceComponent::CandidateInfo::Ptr base, + quint32 priority) + { + auto ci = IceComponent::CandidateInfo::Ptr::create(); + ci->addr = addr; + ci->addr.addr.setScopeId(QString()); + ci->related = base->addr; + ci->base = base->addr; + ci->type = IceComponent::PeerReflexiveType; + ci->priority = priority; + ci->foundation = IceAgent::instance()->foundation(IceComponent::PeerReflexiveType, ci->base.addr); + ci->componentId = base->componentId; + ci->network = base->network; + + auto baseCand = std::find_if(localCandidates.begin(), localCandidates.end(), [&](auto const &c) { + return c.info->base == base->base && c.info->type == HostType; + }); + Q_ASSERT(baseCand != localCandidates.end()); + + Candidate c; + c.id = getId(); + c.info = ci; + c.iceTransport = baseCand->iceTransport; + c.path = 0; + + localCandidates += c; + + emit q->candidateAdded(c); + } + private: // localPref is the priority of the network interface being used for // this candidate. the value must be between 0-65535 and different @@ -427,16 +378,14 @@ class IceComponent::Private : public QObject static int choose_default_priority(CandidateType type, int localPref, bool isVpn, int componentId) { int typePref; - if(type == HostType) - { - if(isVpn) + if (type == HostType) { + if (isVpn) typePref = 0; else typePref = 126; - } - else if(type == PeerReflexiveType) + } else if (type == PeerReflexiveType) typePref = 110; - else if(type == ServerReflexiveType) + else if (type == ServerReflexiveType) typePref = 100; else // RelayedType typePref = 0; @@ -444,181 +393,157 @@ class IceComponent::Private : public QObject return calc_priority(typePref, localPref, componentId); } - static QUdpSocket *takeFromSocketList(QList *socketList, const QHostAddress &addr, QObject *parent = 0) + static QUdpSocket *takeFromSocketList(QList *socketList, const QHostAddress &addr, + QObject *parent = nullptr) { - for(int n = 0; n < socketList->count(); ++n) - { - if((*socketList)[n]->localAddress() == addr) - { + for (int n = 0; n < socketList->count(); ++n) { + if ((*socketList)[n]->localAddress() == addr) { QUdpSocket *sock = socketList->takeAt(n); sock->setParent(parent); return sock; } } - return 0; + return nullptr; } int getId() const { - for(int n = 0;; ++n) - { + for (int n = 0;; ++n) { bool found = false; - foreach(const Candidate &c, localCandidates) - { - if(c.id == n) - { + for (const Candidate &c : localCandidates) { + if (c.id == n) { found = true; break; } } - if(!found) + if (!found) return n; } } int findLocalAddr(const QHostAddress &addr) { - for(int n = 0; n < config.localAddrs.count(); ++n) - { - if(config.localAddrs[n].addr == addr) + for (int n = 0; n < config.localAddrs.count(); ++n) { + if (config.localAddrs[n].addr == addr) return n; } return -1; } - int findLocalTransport(const IceLocalTransport *sock, bool *isLocalLeap) const - { - for(int n = 0; n < localLeap.count(); ++n) - { - if(localLeap[n]->sock == sock) - { - *isLocalLeap = true; - return n; - } - } - - for(int n = 0; n < localStun.count(); ++n) - { - if(localStun[n]->sock == sock) - { - *isLocalLeap = false; - return n; - } - } - - return -1; - } - - void tryStun(int at) - { - LocalTransport *lt = localStun[at]; - - bool atLeastOne = false; - if(useStunBind && !config.stunBindAddr.isNull()) - { - atLeastOne = true; - lt->sock->setStunBindService(config.stunBindAddr, config.stunBindPort); - } - if(useStunRelayUdp && !config.stunRelayUdpAddr.isNull() && !config.stunRelayUdpUser.isEmpty()) - { - atLeastOne = true; - lt->sock->setStunRelayService(config.stunRelayUdpAddr, config.stunRelayUdpPort, config.stunRelayUdpUser, config.stunRelayUdpPass); - } - - Q_ASSERT(atLeastOne); - if(!atLeastOne) - abort(); - - lt->stun_started = true; - lt->sock->stunStart(); - } - void ensureExt(LocalTransport *lt, int addrAt) { - if(!lt->extAddr.isNull() && !lt->ext_finished) - { - CandidateInfo ci; - ci.addr.addr = lt->extAddr; - ci.addr.port = lt->sock->localPort(); - ci.type = ServerReflexiveType; - ci.componentId = id; - ci.priority = choose_default_priority(ci.type, 65535 - addrAt, lt->isVpn, ci.componentId); - ci.base.addr = lt->sock->localAddress(); - ci.base.port = lt->sock->localPort(); - ci.network = lt->network; + if (!lt->extAddr.isNull() && !lt->ext_finished) { + auto ci = CandidateInfo::Ptr::create(); + ci->addr.addr = lt->extAddr; + ci->addr.port = lt->sock->localAddress().port; + ci->type = ServerReflexiveType; + ci->componentId = id; + ci->priority = choose_default_priority(ci->type, 65535 - addrAt, lt->isVpn, ci->componentId); + ci->base = lt->sock->localAddress(); + ci->related = ci->base; + ci->network = lt->network; + ci->foundation = IceAgent::instance()->foundation(ServerReflexiveType, ci->base.addr); Candidate c; - c.id = getId(); - c.info = ci; + c.id = getId(); + c.info = ci; c.iceTransport = lt->sock; - c.path = 0; + c.path = 0; - localCandidates += c; lt->ext_finished = true; - emit q->candidateAdded(c); + storeLocalNotReduntantCandidate(c); } } - void removeLocalCandidates(const IceTransport *sock) + void removeLocalCandidates(const QSharedPointer sock) { ObjectSessionWatcher watch(&sess); - for(int n = 0; n < localCandidates.count(); ++n) - { + for (int n = 0; n < localCandidates.count(); ++n) { Candidate &c = localCandidates[n]; - if(c.iceTransport == sock) - { + if (c.iceTransport == sock) { Candidate tmp = localCandidates.takeAt(n); --n; // adjust position channelPeers.remove(tmp.id); emit q->candidateRemoved(tmp); - if(!watch.isValid()) + if (!watch.isValid()) return; } } } - bool allStopped() const + void storeLocalNotReduntantCandidate(const Candidate &c) { - if(localLeap.isEmpty() && localStun.isEmpty() && !tt) - return true; - else - return false; + ObjectSessionWatcher watch(&sess); + // RFC8445 5.1.3. Eliminating Redundant Candidates + auto it = std::find_if(localCandidates.begin(), localCandidates.end(), [&](const Candidate &cc) { + return cc.info->addr == c.info->addr && cc.info->base == c.info->base + && cc.info->priority >= c.info->priority; + }); + if (it == localCandidates.end()) { // not reduntant + localCandidates += c; + emit q->candidateAdded(c); + } } + bool allStopped() const { return udpTransports.isEmpty() && !tcpTurn; } + void tryStopped() { - if(allStopped()) + if (allStopped()) postStop(); } + // return true if component is still alive after transport removal + bool eraseLocalTransport(LocalTransport *lt) + { + ObjectSessionWatcher watch(&sess); + + emit q->debugLine(QLatin1String("Stopping local transport: ") + lt->sock->localAddress()); + removeLocalCandidates(lt->sock); + if (!watch.isValid()) + return false; + + lt->sock->disconnect(this); + if (lt->borrowed) { + lt->qsock->disconnect(this); + portReserver->returnSockets({ lt->qsock }); + } + delete lt; + udpTransports.removeOne(lt); + return true; + } + private slots: - void doExt() + void tryGatheringComplete() { - if(stopping) + if (gatheringComplete || (tcpTurn && !tcpTurn->isStarted())) return; - ObjectSessionWatcher watch(&sess); - - foreach(LocalTransport *lt, localLeap) - { - if(lt->started) - { - int addrAt = findLocalAddr(lt->addr); - Q_ASSERT(addrAt != -1); + auto checkFinished = [&](const LocalTransport *lt) { + return lt->started && (!lt->sock->stunBindServiceAddress().isValid() || lt->stun_finished) + && (!lt->sock->stunRelayServiceAddress().isValid() || lt->turn_finished); + }; - ensureExt(lt, addrAt); - if(!watch.isValid()) - return; + bool allFinished = true; + for (const LocalTransport *lt : as_const(udpTransports)) { + if (!checkFinished(lt)) { + allFinished = false; + break; } } + + if (allFinished) { + gatheringComplete = true; + emit q->gatheringComplete(); + } } void postStop() @@ -631,15 +556,11 @@ private slots: void lt_started() { IceLocalTransport *sock = static_cast(sender()); - bool isLocalLeap = false; - int at = findLocalTransport(sock, &isLocalLeap); - Q_ASSERT(at != -1); - LocalTransport *lt; - if(isLocalLeap) - lt = localLeap[at]; - else - lt = localStun[at]; + auto it + = std::find_if(udpTransports.begin(), udpTransports.end(), [&](auto const &a) { return a->sock == sock; }); + Q_ASSERT(it != udpTransports.end()); + LocalTransport *lt = *it; lt->started = true; @@ -648,413 +569,303 @@ private slots: ObjectSessionWatcher watch(&sess); - if(useLocal && isLocalLeap) - { - CandidateInfo ci; - ci.addr.addr = lt->sock->localAddress(); - ci.addr.port = lt->sock->localPort(); - ci.type = HostType; - ci.componentId = id; - ci.priority = choose_default_priority(ci.type, 65535 - addrAt, lt->isVpn, ci.componentId); - ci.base = ci.addr; - ci.network = lt->network; + if (useLocal) { + auto ci = CandidateInfo::Ptr::create(); + ci->addr = lt->sock->localAddress(); + ci->type = HostType; + ci->componentId = id; + ci->priority = choose_default_priority(ci->type, 65535 - addrAt, lt->isVpn, ci->componentId); + ci->base = ci->addr; + ci->network = lt->network; + ci->foundation = IceAgent::instance()->foundation(HostType, ci->base.addr); Candidate c; - c.id = getId(); - c.info = ci; - c.iceTransport = sock; - c.path = 0; + c.id = getId(); + c.info = ci; + c.iceTransport = sock->sharedFromThis(); + c.path = 0; localCandidates += c; emit q->candidateAdded(c); - if(!watch.isValid()) + if (!watch.isValid()) return; ensureExt(lt, addrAt); - if(!watch.isValid()) + if (!watch.isValid()) return; } - if(!isLocalLeap && !lt->stun_started) - tryStun(at); - - bool allFinished = true; - foreach(const LocalTransport *lt, localLeap) - { - if(!lt->started) - { - allFinished = false; - break; + if (!lt->stun_started) { + lt->stun_started = true; + if (useStunBind + && (lt->sock->stunBindServiceAddress().isValid() || lt->sock->stunRelayServiceAddress().isValid())) { + lt->sock->stunStart(); + if (!watch.isValid()) + return; + } else { + lt->stun_finished = true; + lt->turn_finished = true; } } - if(allFinished) - { - foreach(const LocalTransport *lt, localStun) - { - if(!lt->started) - { - allFinished = false; + + // check completeness of various stuff + if (!localFinished) { + bool allStarted = true; + for (const LocalTransport *lt : as_const(udpTransports)) { + if (!lt->started) { + allStarted = false; break; } } + if (allStarted) { + localFinished = true; + emit q->localFinished(); + if (!watch.isValid()) + return; + } } - if(allFinished && !local_finished) - { - local_finished = true; - emit q->localFinished(); - } - } - - void lt_stopped() - { - IceLocalTransport *sock = static_cast(sender()); - bool isLocalLeap = false; - int at = findLocalTransport(sock, &isLocalLeap); - Q_ASSERT(at != -1); - - LocalTransport *lt; - if(isLocalLeap) - lt = localLeap[at]; - else - lt = localStun[at]; - - ObjectSessionWatcher watch(&sess); - - removeLocalCandidates(lt->sock); - if(!watch.isValid()) - return; - - delete lt->sock; - lt->sock = 0; - - if(isLocalLeap) - { - if(lt->borrowedSocket) - portReserver->returnSockets(QList() << lt->qsock); - else - lt->qsock->deleteLater(); - - delete lt; - localLeap.removeAt(at); - } - else - { - delete lt; - localStun.removeAt(at); - } - - tryStopped(); + tryGatheringComplete(); } void lt_addressesChanged() { IceLocalTransport *sock = static_cast(sender()); - bool isLocalLeap = false; - int at = findLocalTransport(sock, &isLocalLeap); - Q_ASSERT(at != -1); + auto it + = std::find_if(udpTransports.begin(), udpTransports.end(), [&](auto const &a) { return a->sock == sock; }); - // leap does not use stun, so we should not get this signal - Q_ASSERT(!isLocalLeap); - - LocalTransport *lt = localStun[at]; + Q_ASSERT(it != udpTransports.end()); + LocalTransport *lt = *it; int addrAt = findLocalAddr(lt->addr); Q_ASSERT(addrAt != -1); ObjectSessionWatcher watch(&sess); - if(useStunBind && !lt->sock->serverReflexiveAddress().isNull() && !lt->stun_finished) - { + if (useStunBind && lt->sock->serverReflexiveAddress().isValid() && !lt->stun_finished) { // automatically assign ext to related leaps, if possible - foreach(LocalTransport *i, localLeap) - { - if(i->extAddr.isNull() && i->sock->localAddress() == lt->sock->localAddress()) - { - i->extAddr = lt->sock->serverReflexiveAddress(); - if(i->started) - { + for (LocalTransport *i : as_const(udpTransports)) { + if (i->extAddr.isNull() && i->sock->localAddress() == lt->sock->localAddress()) { + i->extAddr = lt->sock->serverReflexiveAddress().addr; + if (i->started) { ensureExt(i, addrAt); - if(!watch.isValid()) + if (!watch.isValid()) return; } } } - CandidateInfo ci; - ci.addr.addr = lt->sock->serverReflexiveAddress(); - ci.addr.port = lt->sock->serverReflexivePort(); - ci.type = ServerReflexiveType; - ci.componentId = id; - ci.priority = choose_default_priority(ci.type, 65535 - addrAt, lt->isVpn, ci.componentId); - // stun is only used on non-leap sockets, but we don't - // announce non-leap local candidates, so make the - // base the same as the srflx - //ci.base.addr = lt->sock->localAddress(); - //ci.base.port = lt->sock->localPort(); - ci.base = ci.addr; - ci.network = lt->network; + auto ci = CandidateInfo::Ptr::create(); + ci->addr = lt->sock->serverReflexiveAddress(); + ci->base = lt->sock->localAddress(); + ci->related = ci->base; + ci->type = ServerReflexiveType; + ci->componentId = id; + ci->priority = choose_default_priority(ci->type, 65535 - addrAt, lt->isVpn, ci->componentId); + ci->network = lt->network; + ci->foundation = IceAgent::instance()->foundation( + ServerReflexiveType, ci->base.addr, lt->sock->reflexiveAddressSource(), QAbstractSocket::UdpSocket); Candidate c; - c.id = getId(); - c.info = ci; - c.iceTransport = sock; - c.path = 0; + c.id = getId(); + c.info = ci; + c.iceTransport = sock->sharedFromThis(); + c.path = 0; - localCandidates += c; lt->stun_finished = true; - emit q->candidateAdded(c); - if(!watch.isValid()) - return; + storeLocalNotReduntantCandidate(c); + } else if (useStunBind && !lt->sock->isStunAlive() && !lt->stun_finished) { + lt->stun_finished = true; } - if(!lt->sock->relayedAddress().isNull() && !lt->turn_finished) - { - CandidateInfo ci; - ci.addr.addr = lt->sock->relayedAddress(); - ci.addr.port = lt->sock->relayedPort(); - ci.type = RelayedType; - ci.componentId = id; - ci.priority = choose_default_priority(ci.type, 65535 - addrAt, lt->isVpn, ci.componentId); - ci.base.addr = lt->sock->serverReflexiveAddress(); - ci.base.port = lt->sock->serverReflexivePort(); - ci.network = lt->network; + if (lt->sock->relayedAddress().isValid() && !lt->turn_finished) { + auto ci = CandidateInfo::Ptr::create(); + ci->addr = lt->sock->relayedAddress(); + ci->base = ci->addr; + ci->related = lt->sock->serverReflexiveAddress(); + ci->type = RelayedType; + ci->componentId = id; + ci->priority = choose_default_priority(ci->type, 65535 - addrAt, lt->isVpn, ci->componentId); + ci->network = lt->network; + ci->foundation = IceAgent::instance()->foundation( + RelayedType, ci->base.addr, lt->sock->stunRelayServiceAddress().addr, QAbstractSocket::UdpSocket); Candidate c; - c.id = getId(); - c.info = ci; - c.iceTransport = sock; - c.path = 1; + c.id = getId(); + c.info = ci; + c.iceTransport = sock->sharedFromThis(); + c.path = 1; - localCandidates += c; lt->turn_finished = true; - emit q->candidateAdded(c); + storeLocalNotReduntantCandidate(c); + } else if (!lt->sock->isTurnAlive() && !lt->turn_finished) { + lt->turn_finished = true; } - } - - void lt_error(int e) - { - Q_UNUSED(e); - - IceLocalTransport *sock = static_cast(sender()); - bool isLocalLeap = false; - int at = findLocalTransport(sock, &isLocalLeap); - Q_ASSERT(at != -1); - - LocalTransport *lt; - if(isLocalLeap) - lt = localLeap[at]; - else - lt = localStun[at]; - - ObjectSessionWatcher watch(&sess); - - removeLocalCandidates(lt->sock); - if(!watch.isValid()) + if (!watch.isValid()) return; - delete lt->sock; - lt->sock = 0; - - if(isLocalLeap) - { - if(lt->borrowedSocket) - portReserver->returnSockets(QList() << lt->qsock); - else - lt->qsock->deleteLater(); - - delete lt; - localLeap.removeAt(at); - } - else - { - delete lt; - localStun.removeAt(at); - } + tryGatheringComplete(); } - void lt_debugLine(const QString &line) - { - emit q->debugLine(line); - } + void lt_debugLine(const QString &line) { emit q->debugLine(line); } void tt_started() { // lower priority by making it seem like the last nic int addrAt = 1024; - CandidateInfo ci; - ci.addr.addr = tt->relayedAddress(); - ci.addr.port = tt->relayedPort(); - ci.type = RelayedType; - ci.componentId = id; - ci.priority = choose_default_priority(ci.type, 65535 - addrAt, false, ci.componentId); - ci.base = ci.addr; - ci.network = 0; // not relevant + auto ci = CandidateInfo::Ptr::create(); + ci->addr = tcpTurn->relayedAddress(); + ci->related = tcpTurn->reflexiveAddress(); + ci->type = RelayedType; + ci->componentId = id; + ci->priority = choose_default_priority(ci->type, 65535 - addrAt, false, ci->componentId); + ci->base = ci->addr; + ci->network = 0; // not relevant + ci->foundation = IceAgent::instance()->foundation(RelayedType, ci->base.addr, config.stunRelayTcpAddr.addr, + QAbstractSocket::TcpSocket); Candidate c; - c.id = getId(); - c.info = ci; - c.iceTransport = tt; - c.path = 0; + c.id = getId(); + c.info = ci; + c.iceTransport = tcpTurn->sharedFromThis(); + c.path = 0; localCandidates += c; emit q->candidateAdded(c); + + tryGatheringComplete(); } void tt_stopped() { ObjectSessionWatcher watch(&sess); - removeLocalCandidates(tt); - if(!watch.isValid()) + removeLocalCandidates(tcpTurn->sharedFromThis()); + if (!watch.isValid()) return; - delete tt; - tt = 0; + tcpTurn->disconnect(this); + tcpTurn.reset(); tryStopped(); } void tt_error(int e) { - Q_UNUSED(e); + Q_UNUSED(e) ObjectSessionWatcher watch(&sess); - removeLocalCandidates(tt); - if(!watch.isValid()) + removeLocalCandidates(tcpTurn); + if (!watch.isValid()) return; - delete tt; - tt = 0; + tcpTurn->disconnect(this); + tcpTurn.reset(); + tryGatheringComplete(); } - void tt_debugLine(const QString &line) - { - emit q->debugLine(line); - } + void tt_debugLine(const QString &line) { emit q->debugLine(line); } }; -IceComponent::IceComponent(int id, QObject *parent) : - QObject(parent) +IceComponent::IceComponent(int id, QObject *parent) : QObject(parent) { - d = new Private(this); + d = new Private(this); d->id = id; } -IceComponent::~IceComponent() -{ - delete d; -} +IceComponent::~IceComponent() { delete d; } -int IceComponent::id() const -{ - return d->id; -} +int IceComponent::id() const { return d->id; } -void IceComponent::setClientSoftwareNameAndVersion(const QString &str) -{ - d->clientSoftware = str; -} +bool IceComponent::isGatheringComplete() const { return d->gatheringComplete; } -void IceComponent::setProxy(const TurnClient::Proxy &proxy) -{ - d->proxy = proxy; -} +void IceComponent::setClientSoftwareNameAndVersion(const QString &str) { d->clientSoftware = str; } -void IceComponent::setPortReserver(UdpPortReserver *portReserver) -{ - d->portReserver = portReserver; -} +void IceComponent::setProxy(const TurnClient::Proxy &proxy) { d->proxy = proxy; } -void IceComponent::setLocalAddresses(const QList &addrs) -{ - d->pending.localAddrs = addrs; -} +void IceComponent::setPortReserver(UdpPortReserver *portReserver) { d->portReserver = portReserver; } -void IceComponent::setExternalAddresses(const QList &addrs) -{ - d->pending.extAddrs = addrs; -} +UdpPortReserver *IceComponent::portReserver() const { return d->portReserver; } -void IceComponent::setStunBindService(const QHostAddress &addr, int port) -{ - d->pending.stunBindAddr = addr; - d->pending.stunBindPort = port; -} +void IceComponent::setLocalAddresses(const QList &addrs) { d->pending.localAddrs = addrs; } + +void IceComponent::setExternalAddresses(const QList &addrs) { d->pending.extAddrs = addrs; } + +void IceComponent::setStunBindService(const TransportAddress &addr) { d->pending.stunBindAddr = addr; } -void IceComponent::setStunRelayUdpService(const QHostAddress &addr, int port, const QString &user, const QCA::SecureArray &pass) +void IceComponent::setStunRelayUdpService(const TransportAddress &addr, const QString &user, + const QCA::SecureArray &pass) { d->pending.stunRelayUdpAddr = addr; - d->pending.stunRelayUdpPort = port; d->pending.stunRelayUdpUser = user; d->pending.stunRelayUdpPass = pass; } -void IceComponent::setStunRelayTcpService(const QHostAddress &addr, int port, const QString &user, const QCA::SecureArray &pass) +void IceComponent::setStunRelayTcpService(const TransportAddress &addr, const QString &user, + const QCA::SecureArray &pass) { d->pending.stunRelayTcpAddr = addr; - d->pending.stunRelayTcpPort = port; d->pending.stunRelayTcpUser = user; d->pending.stunRelayTcpPass = pass; } -void IceComponent::setUseLocal(bool enabled) -{ - d->useLocal = enabled; -} +void IceComponent::setUseLocal(bool enabled) { d->useLocal = enabled; } -void IceComponent::setUseStunBind(bool enabled) -{ - d->useStunBind = enabled; -} +void IceComponent::setUseStunBind(bool enabled) { d->useStunBind = enabled; } -void IceComponent::setUseStunRelayUdp(bool enabled) -{ - d->useStunRelayUdp = enabled; -} +void IceComponent::setUseStunRelayUdp(bool enabled) { d->useStunRelayUdp = enabled; } -void IceComponent::setUseStunRelayTcp(bool enabled) -{ - d->useStunRelayTcp = enabled; -} +void IceComponent::setUseStunRelayTcp(bool enabled) { d->useStunRelayTcp = enabled; } -void IceComponent::update(QList *socketList) -{ - d->update(socketList); -} +void IceComponent::update(QList *socketList) { d->update(socketList); } -void IceComponent::stop() +void IceComponent::stop() { d->stop(); } + +int IceComponent::peerReflexivePriority(QSharedPointer iceTransport, int path) const { - d->stop(); + return d->peerReflexivePriority(iceTransport, path); } -int IceComponent::peerReflexivePriority(const IceTransport *iceTransport, int path) const +void IceComponent::addLocalPeerReflexiveCandidate(const TransportAddress &addr, IceComponent::CandidateInfo::Ptr base, + quint32 priority) { - return d->peerReflexivePriority(iceTransport, path); + d->addLocalPeerReflexiveCandidate(addr, base, priority); } -void IceComponent::flagPathAsLowOverhead(int id, const QHostAddress &addr, int port) +void IceComponent::flagPathAsLowOverhead(int id, const TransportAddress &addr) { - return d->flagPathAsLowOverhead(id, addr, port); + return d->flagPathAsLowOverhead(id, addr); } void IceComponent::setDebugLevel(DebugLevel level) { d->debugLevel = level; - foreach(const Private::LocalTransport *lt, d->localLeap) - lt->sock->setDebugLevel((IceTransport::DebugLevel)level); - foreach(const Private::LocalTransport *lt, d->localStun) - lt->sock->setDebugLevel((IceTransport::DebugLevel)level); - if(d->tt) - d->tt->setDebugLevel((IceTransport::DebugLevel)level); + for (const Private::LocalTransport *lt : as_const(d->udpTransports)) + lt->sock->setDebugLevel(IceTransport::DebugLevel(level)); + if (d->tcpTurn) + d->tcpTurn->setDebugLevel((IceTransport::DebugLevel)level); } +IceComponent::CandidateInfo::Ptr +IceComponent::CandidateInfo::makeRemotePrflx(int componentId, const TransportAddress &fromAddr, quint32 priority) +{ + auto c = IceComponent::CandidateInfo::Ptr::create(); + c->addr = fromAddr; + c->addr.addr.setScopeId(QString()); + c->type = PeerReflexiveType; + c->priority = priority; + c->foundation = QUuid::createUuid().toString(); + c->componentId = componentId; + c->network = -1; + return c; } +} // namespace XMPP + #include "icecomponent.moc" diff --git a/src/irisnet/noncore/icecomponent.h b/src/irisnet/noncore/icecomponent.h index 94320ce1..29fd09ac 100644 --- a/src/irisnet/noncore/icecomponent.h +++ b/src/irisnet/noncore/icecomponent.h @@ -19,77 +19,45 @@ #ifndef ICECOMPONENT_H #define ICECOMPONENT_H -#include -#include "turnclient.h" -#include "icetransport.h" #include "ice176.h" +#include "icetransport.h" +#include "turnclient.h" + +#include class QUdpSocket; namespace XMPP { - class UdpPortReserver; -class IceComponent : public QObject -{ +class IceComponent : public QObject { Q_OBJECT public: - enum CandidateType - { - HostType, - PeerReflexiveType, - ServerReflexiveType, - RelayedType - }; + enum CandidateType { HostType, PeerReflexiveType, ServerReflexiveType, RelayedType }; - class TransportAddress - { + class CandidateInfo { public: - QHostAddress addr; - int port; - - TransportAddress() : - port(-1) - { - } - - TransportAddress(const QHostAddress &_addr, int _port) : - addr(_addr), - port(_port) - { - } - - bool operator==(const TransportAddress &other) const - { - if(addr == other.addr && port == other.port) - return true; - else - return false; - } - - inline bool operator!=(const TransportAddress &other) const - { - return !operator==(other); - } - }; + using Ptr = QSharedPointer; - class CandidateInfo - { - public: - TransportAddress addr; CandidateType type; - int priority; + int priority; + int componentId; + int network; + + TransportAddress addr; // address according to candidate type + TransportAddress base; // network interface address + TransportAddress related; // not used in agent but usefule for diagnostics + QString foundation; - int componentId; - TransportAddress base; - TransportAddress related; QString id; - int network; + + static Ptr makeRemotePrflx(int componentId, const TransportAddress &fromAddr, quint32 priority); + inline bool operator==(const CandidateInfo &o) const { return addr == o.addr && componentId == o.componentId; } + inline bool operator==(CandidateInfo::Ptr o) const { return *this == *o; } }; - class Candidate - { + class Candidate { public: // unique across all candidates within this component int id; @@ -99,29 +67,26 @@ class IceComponent : public QObject // is up to the user to create the candidate id. // info.foundation is also unset, since awareness of all // components and candidates is needed to calculate it. - CandidateInfo info; + CandidateInfo::Ptr info; // note that these may be the same for multiple candidates - IceTransport *iceTransport; - int path; + QSharedPointer iceTransport; + int path; }; - enum DebugLevel - { - DL_None, - DL_Info, - DL_Packet - }; + enum DebugLevel { DL_None, DL_Info, DL_Packet }; - IceComponent(int id, QObject *parent = 0); + IceComponent(int id, QObject *parent = nullptr); ~IceComponent(); - int id() const; + int id() const; + bool isGatheringComplete() const; void setClientSoftwareNameAndVersion(const QString &str); void setProxy(const TurnClient::Proxy &proxy); - void setPortReserver(UdpPortReserver *portReserver); + void setPortReserver(UdpPortReserver *portReserver); + UdpPortReserver *portReserver() const; // can be set once, but later changes are ignored void setLocalAddresses(const QList &addrs); @@ -131,25 +96,32 @@ class IceComponent : public QObject void setExternalAddresses(const QList &addrs); // can be set at any time, but only once. later changes are ignored - void setStunBindService(const QHostAddress &addr, int port); - void setStunRelayUdpService(const QHostAddress &addr, int port, const QString &user, const QCA::SecureArray &pass); - void setStunRelayTcpService(const QHostAddress &addr, int port, const QString &user, const QCA::SecureArray &pass); + void setStunBindService(const TransportAddress &addr); + void setStunRelayUdpService(const TransportAddress &addr, const QString &user, const QCA::SecureArray &pass); + void setStunRelayTcpService(const TransportAddress &addr, const QString &user, const QCA::SecureArray &pass); // these all start out enabled, but can be disabled for diagnostic // purposes - void setUseLocal(bool enabled); + void setUseLocal(bool enabled); // where to make local host candidates void setUseStunBind(bool enabled); void setUseStunRelayUdp(bool enabled); void setUseStunRelayTcp(bool enabled); - // if socketList is not null then port reserver must be set - void update(QList *socketList = 0); + /** + * @brief update component with local listening sockets + * @param socketList + * If socketList is not null then port reserver must be set. + * If the pool doesn't have enough sockets, the component will allocate its own. + */ + void update(QList *socketList = nullptr); void stop(); // prflx priority to use when replying from this transport/path - int peerReflexivePriority(const IceTransport *iceTransport, int path) const; + int peerReflexivePriority(QSharedPointer iceTransport, int path) const; + + void addLocalPeerReflexiveCandidate(const TransportAddress &addr, CandidateInfo::Ptr base, quint32 priority); - void flagPathAsLowOverhead(int id, const QHostAddress &addr, int port); + void flagPathAsLowOverhead(int id, const TransportAddress &addr); void setDebugLevel(DebugLevel level); @@ -165,6 +137,9 @@ class IceComponent : public QObject // note that it is possible there are no HostType candidates. void localFinished(); + // no more candidates will be emitted unless network candidition changes + void gatheringComplete(); + void stopped(); // reports debug of iceTransports as well. not DOR-SS/DS safe @@ -176,11 +151,6 @@ class IceComponent : public QObject Private *d; }; -inline uint qHash(const XMPP::IceComponent::TransportAddress &key, uint seed = 0) -{ - return ::qHash(key.addr, seed) ^ ::qHash(key.port, seed); -} - -} //namespace XMPP +} // namespace XMPP -#endif +#endif // ICECOMPONENT_H diff --git a/src/irisnet/noncore/icelocaltransport.cpp b/src/irisnet/noncore/icelocaltransport.cpp index f9229f4e..b0eb4870 100644 --- a/src/irisnet/noncore/icelocaltransport.cpp +++ b/src/irisnet/noncore/icelocaltransport.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009,2010 Barracuda Networks, Inc. + * Copyright (C) 2009-2010 Barracuda Networks, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -18,57 +18,48 @@ #include "icelocaltransport.h" -#include -#include -#include #include "objectsession.h" +#include "stunallocate.h" +#include "stunbinding.h" #include "stunmessage.h" #include "stuntransaction.h" -#include "stunbinding.h" -#include "stunallocate.h" #include "turnclient.h" +#include +#include +#include + // don't queue more incoming packets than this per transmit path #define MAX_PACKET_QUEUE 64 namespace XMPP { - -enum -{ - Direct, - Relayed -}; +enum { Direct, Relayed }; //---------------------------------------------------------------------------- // SafeUdpSocket //---------------------------------------------------------------------------- // DOR-safe wrapper for QUdpSocket -class SafeUdpSocket : public QObject -{ +class SafeUdpSocket : public QObject { Q_OBJECT private: ObjectSession sess; - QUdpSocket *sock; - int writtenCount; + QUdpSocket *sock; + int writtenCount; public: - SafeUdpSocket(QUdpSocket *_sock, QObject *parent = 0) : - QObject(parent), - sess(this), - sock(_sock) + SafeUdpSocket(QUdpSocket *_sock, QObject *parent = nullptr) : QObject(parent), sess(this), sock(_sock) { sock->setParent(this); - connect(sock, SIGNAL(readyRead()), SLOT(sock_readyRead())); - connect(sock, SIGNAL(bytesWritten(qint64)), SLOT(sock_bytesWritten(qint64))); + connect(sock, &QUdpSocket::readyRead, this, &SafeUdpSocket::sock_readyRead); + connect(sock, &QUdpSocket::bytesWritten, this, &SafeUdpSocket::sock_bytesWritten); writtenCount = 0; } ~SafeUdpSocket() { - if(sock) - { + if (sock) { QUdpSocket *out = release(); out->deleteLater(); } @@ -77,41 +68,34 @@ class SafeUdpSocket : public QObject QUdpSocket *release() { sock->disconnect(this); - sock->setParent(0); + sock->setParent(nullptr); QUdpSocket *out = sock; - sock = 0; + sock = nullptr; return out; } - QHostAddress localAddress() const - { - return sock->localAddress(); - } + TransportAddress localTransportAddress() const { return { sock->localAddress(), sock->localPort() }; } - quint16 localPort() const - { - return sock->localPort(); - } + QHostAddress localAddress() const { return sock->localAddress(); } - bool hasPendingDatagrams() const - { - return sock->hasPendingDatagrams(); - } + quint16 localPort() const { return sock->localPort(); } - QByteArray readDatagram(QHostAddress *address = 0, quint16 *port = 0) + bool hasPendingDatagrams() const { return sock->hasPendingDatagrams(); } + + QByteArray readDatagram(TransportAddress &address) { - if(!sock->hasPendingDatagrams()) + if (!sock->hasPendingDatagrams()) return QByteArray(); QByteArray buf; - buf.resize(sock->pendingDatagramSize()); - sock->readDatagram(buf.data(), buf.size(), address, port); + buf.resize(int(sock->pendingDatagramSize())); + sock->readDatagram(buf.data(), buf.size(), &address.addr, &address.port); return buf; } - void writeDatagram(const QByteArray &buf, const QHostAddress &address, quint16 port) + void writeDatagram(const QByteArray &buf, const TransportAddress &address) { - sock->writeDatagram(buf, address, port); + sock->writeDatagram(buf, address.addr, address.port); } signals: @@ -119,10 +103,7 @@ class SafeUdpSocket : public QObject void datagramsWritten(int count); private slots: - void sock_readyRead() - { - emit readyRead(); - } + void sock_readyRead() { emit readyRead(); } void sock_bytesWritten(qint64 bytes) { @@ -134,7 +115,7 @@ private slots: void processWritten() { - int count = writtenCount; + int count = writtenCount; writtenCount = 0; emit datagramsWritten(count); @@ -144,132 +125,90 @@ private slots: //---------------------------------------------------------------------------- // IceLocalTransport //---------------------------------------------------------------------------- -class IceLocalTransport::Private : public QObject -{ +class IceLocalTransport::Private : public QObject { Q_OBJECT public: - class WriteItem - { + class WriteItem { public: - enum Type - { - Direct, - Pool, - Turn - }; - - Type type; - QHostAddress addr; - int port; + enum Type { Direct, Pool, Turn }; + + Type type; + TransportAddress addr; }; - class Written - { + class Written { public: - QHostAddress addr; - int port; - int count; + TransportAddress addr; + int count; }; - class Datagram - { + class Datagram { public: - QHostAddress addr; - int port; - QByteArray buf; + TransportAddress addr; + QByteArray buf; }; - IceLocalTransport *q; - ObjectSession sess; - QUdpSocket *extSock; - SafeUdpSocket *sock; - StunTransactionPool *pool; - StunBinding *stunBinding; - TurnClient *turn; - bool turnActivated; - QHostAddress addr; - int port; - QHostAddress refAddr; - int refPort; - QHostAddress relAddr; - int relPort; - QHostAddress stunBindAddr; - int stunBindPort; - QHostAddress stunRelayAddr; - int stunRelayPort; - QString stunUser; - QCA::SecureArray stunPass; - QString clientSoftware; - QList in; - QList inRelayed; - QList pendingWrites; - int retryCount; - bool stopping; - int debugLevel; - - Private(IceLocalTransport *_q) : - QObject(_q), - q(_q), - sess(this), - extSock(0), - sock(0), - pool(0), - stunBinding(0), - turn(0), - turnActivated(false), - port(-1), - refPort(-1), - relPort(-1), - retryCount(0), - stopping(false), - debugLevel(IceTransport::DL_None) - { - } - - ~Private() - { - reset(); - } + IceLocalTransport *q; + ObjectSession sess; + QUdpSocket *extSock = nullptr; + SafeUdpSocket *sock = nullptr; + StunTransactionPool::Ptr pool; + StunBinding *stunBinding = nullptr; + TurnClient *turn = nullptr; + bool turnActivated = false; + TransportAddress addr; + TransportAddress refAddr; + TransportAddress relAddr; + QHostAddress refAddrSource; + TransportAddress stunBindAddr; + TransportAddress stunRelayAddr; + QString stunUser; + QCA::SecureArray stunPass; + QString clientSoftware; + QList in; + QList inRelayed; + QList pendingWrites; + int retryCount = 0; + bool stopping = false; + int debugLevel = IceTransport::DL_None; + + Private(IceLocalTransport *_q) : QObject(_q), q(_q), sess(this) { } + + ~Private() { reset(); } void reset() { sess.reset(); delete stunBinding; - stunBinding = 0; + stunBinding = nullptr; delete turn; - turn = 0; + turn = nullptr; turnActivated = false; - if(sock) - { - if(extSock) - { - sock->release(); - extSock = 0; + if (sock) { // if started + if (extSock) { + sock->release(); // detaches the socket but doesn't destroy + extSock = nullptr; } delete sock; - sock = 0; + sock = nullptr; } - addr = QHostAddress(); - port = -1; - - refAddr = QHostAddress(); - refPort = -1; - - relAddr = QHostAddress(); - relPort = -1; + addr = TransportAddress(); + relAddr = TransportAddress(); + refAddr = TransportAddress(); + refAddrSource = QHostAddress(); in.clear(); inRelayed.clear(); pendingWrites.clear(); retryCount = 0; - stopping = false; + stopping = false; } void start() @@ -282,12 +221,17 @@ class IceLocalTransport::Private : public QObject void stop() { Q_ASSERT(sock); - Q_ASSERT(!stopping); + if (stopping) { + emit q->debugLine(QString("local transport %1 is already stopping. just wait...").arg(addr)); + return; + } else { + emit q->debugLine(QString("stopping local transport %1.").arg(addr)); + } stopping = true; - if(turn) - turn->close(); + if (turn) + turn->close(); // will emit stopped() eventually calling postStop() else sess.defer(this, "postStop"); } @@ -296,49 +240,64 @@ class IceLocalTransport::Private : public QObject { Q_ASSERT(!pool); - pool = new StunTransactionPool(StunTransaction::Udp, this); + pool = StunTransactionPool::Ptr::create(StunTransaction::Udp); pool->setDebugLevel((StunTransactionPool::DebugLevel)debugLevel); - connect(pool, SIGNAL(outgoingMessage(QByteArray,QHostAddress,int)), SLOT(pool_outgoingMessage(QByteArray,QHostAddress,int))); - connect(pool, SIGNAL(needAuthParams()), SLOT(pool_needAuthParams())); - connect(pool, SIGNAL(debugLine(QString)), SLOT(pool_debugLine(QString))); + connect(pool.data(), &StunTransactionPool::outgoingMessage, this, &Private::pool_outgoingMessage); + connect(pool.data(), &StunTransactionPool::needAuthParams, this, &Private::pool_needAuthParams); + connect(pool.data(), &StunTransactionPool::debugLine, this, &Private::pool_debugLine); pool->setLongTermAuthEnabled(true); - if(!stunUser.isEmpty()) - { + if (!stunUser.isEmpty()) { pool->setUsername(stunUser); pool->setPassword(stunPass); } - if(!stunBindAddr.isNull()) - { - stunBinding = new StunBinding(pool); - connect(stunBinding, SIGNAL(success()), SLOT(binding_success())); - connect(stunBinding, SIGNAL(error(XMPP::StunBinding::Error)), SLOT(binding_error(XMPP::StunBinding::Error))); - stunBinding->start(stunBindAddr, stunBindPort); - } + do_stun(); + do_turn(); + } - if(!stunRelayAddr.isNull()) - { - do_turn(); + void do_stun() + { + if (!stunBindAddr.isValid()) { + return; } + stunBinding = new StunBinding(pool.data()); + connect(stunBinding, &StunBinding::success, this, [&]() { + refAddr = stunBinding->reflexiveAddress(); + refAddrSource = stunBindAddr.addr; + + delete stunBinding; + stunBinding = nullptr; + + emit q->addressesChanged(); + }); + connect(stunBinding, &StunBinding::error, this, [&](XMPP::StunBinding::Error) { + delete stunBinding; + stunBinding = nullptr; + emit q->error(IceLocalTransport::ErrorStun); + }); + stunBinding->start(stunBindAddr); } void do_turn() { + if (!stunRelayAddr.isValid()) { + return; + } turn = new TurnClient(this); turn->setDebugLevel((TurnClient::DebugLevel)debugLevel); - connect(turn, SIGNAL(connected()), SLOT(turn_connected())); - connect(turn, SIGNAL(tlsHandshaken()), SLOT(turn_tlsHandshaken())); - connect(turn, SIGNAL(closed()), SLOT(turn_closed())); - connect(turn, SIGNAL(activated()), SLOT(turn_activated())); - connect(turn, SIGNAL(packetsWritten(int,QHostAddress,int)), SLOT(turn_packetsWritten(int,QHostAddress,int))); - connect(turn, SIGNAL(error(XMPP::TurnClient::Error)), SLOT(turn_error(XMPP::TurnClient::Error))); - connect(turn, SIGNAL(outgoingDatagram(QByteArray)), SLOT(turn_outgoingDatagram(QByteArray))); - connect(turn, SIGNAL(debugLine(QString)), SLOT(turn_debugLine(QString))); + connect(turn, &TurnClient::connected, this, &Private::turn_connected); + connect(turn, &TurnClient::tlsHandshaken, this, &Private::turn_tlsHandshaken); + connect(turn, &TurnClient::closed, this, &Private::turn_closed); + connect(turn, &TurnClient::activated, this, &Private::turn_activated); + connect(turn, &TurnClient::packetsWritten, this, &Private::turn_packetsWritten); + connect(turn, &TurnClient::error, this, &Private::turn_error); + connect(turn, &TurnClient::outgoingDatagram, this, &Private::turn_outgoingDatagram); + connect(turn, &TurnClient::debugLine, this, &Private::turn_debugLine); turn->setClientSoftwareNameAndVersion(clientSoftware); - turn->connectToHost(pool, stunRelayAddr, stunRelayPort); + turn->connectToHost(pool.data(), stunRelayAddr); } private: @@ -346,11 +305,10 @@ class IceLocalTransport::Private : public QObject QUdpSocket *createSocket() { QUdpSocket *qsock = new QUdpSocket(this); - if(!qsock->bind(addr, 0)) - { + if (!qsock->bind(addr.addr, 0)) { delete qsock; emit q->error(IceLocalTransport::ErrorBind); - return 0; + return nullptr; } return qsock; @@ -358,36 +316,33 @@ class IceLocalTransport::Private : public QObject void prepareSocket() { - addr = sock->localAddress(); - port = sock->localPort(); + addr = sock->localTransportAddress(); - connect(sock, SIGNAL(readyRead()), SLOT(sock_readyRead())); - connect(sock, SIGNAL(datagramsWritten(int)), SLOT(sock_datagramsWritten(int))); + connect(sock, &SafeUdpSocket::readyRead, this, &Private::sock_readyRead); + connect(sock, &SafeUdpSocket::datagramsWritten, this, &Private::sock_datagramsWritten); } // return true if we are retrying, false if we should error out bool handleRetry() { // don't allow retrying if activated or stopping) - if(turnActivated || stopping) + if (turnActivated || stopping) return false; ++retryCount; - if(retryCount < 3) - { - if(debugLevel >= IceTransport::DL_Info) + if (retryCount < 3) { + if (debugLevel >= IceTransport::DL_Info) emit q->debugLine("retrying..."); delete sock; - sock = 0; + sock = nullptr; // to receive this error, it is a Relay, so change // the mode - //stunType = IceLocalTransport::Relay; + // stunType = IceLocalTransport::Relay; QUdpSocket *qsock = createSocket(); - if(!qsock) - { + if (!qsock) { // signal emitted in this case. bail. // (return true so caller takes no action) return true; @@ -397,11 +352,10 @@ class IceLocalTransport::Private : public QObject prepareSocket(); - refAddr = QHostAddress(); - refPort = -1; + refAddr = TransportAddress(); + refAddrSource = QHostAddress(); - relAddr = QHostAddress(); - relPort = -1; + relAddr = TransportAddress(); do_turn(); @@ -415,27 +369,22 @@ class IceLocalTransport::Private : public QObject } // return true if data packet, false if pool or nothing - bool processIncomingStun(const QByteArray &buf, const QHostAddress &fromAddr, int fromPort, Datagram *dg) + bool processIncomingStun(const QByteArray &buf, const TransportAddress &fromAddr, Datagram *dg) { - QByteArray data; - QHostAddress dataAddr; - int dataPort; + QByteArray data; + TransportAddress dataAddr; bool notStun; - if(!pool->writeIncomingMessage(buf, ¬Stun, fromAddr, fromPort) && turn) - { - data = turn->processIncomingDatagram(buf, notStun, &dataAddr, &dataPort); - if(!data.isNull()) - { + if (!pool->writeIncomingMessage(buf, ¬Stun, fromAddr) && turn) { + data = turn->processIncomingDatagram(buf, notStun, dataAddr); + if (!data.isNull()) { dg->addr = dataAddr; - dg->port = dataPort; - dg->buf = data; + dg->buf = data; return true; - } - else - { - if(debugLevel >= IceTransport::DL_Packet) - emit q->debugLine("Warning: server responded with what doesn't seem to be a STUN or data packet, skipping."); + } else { + if (debugLevel >= IceTransport::DL_Packet) + emit q->debugLine( + "Warning: server responded with what doesn't seem to be a STUN or data packet, skipping."); } } @@ -445,18 +394,14 @@ class IceLocalTransport::Private : public QObject private slots: void postStart() { - if(stopping) + if (stopping) return; - if(extSock) - { + if (extSock) { sock = new SafeUdpSocket(extSock, this); - } - else - { + } else { QUdpSocket *qsock = createSocket(); - if(!qsock) - { + if (!qsock) { // signal emitted in this case. bail return; } @@ -479,48 +424,42 @@ private slots: { ObjectSessionWatcher watch(&sess); - QList dreads; - QList rreads; - - while(sock->hasPendingDatagrams()) - { - QHostAddress from; - quint16 fromPort; + QList dreads; // direct + QList rreads; // relayed - Datagram dg; + while (sock->hasPendingDatagrams()) { + TransportAddress from; + Datagram dg; - QByteArray buf = sock->readDatagram(&from, &fromPort); - if((from == stunBindAddr && fromPort == stunBindPort) || (from == stunRelayAddr && fromPort == stunRelayPort)) - { - bool haveData = processIncomingStun(buf, from, fromPort, &dg); + QByteArray buf = sock->readDatagram(from); + if (buf.isEmpty()) // it's weird we ever came here, but should relax static analyzer + break; + qDebug("got packet from %s", qPrintable(from)); + if (from == stunBindAddr || from == stunRelayAddr) { + bool haveData = processIncomingStun(buf, from, &dg); // processIncomingStun could cause signals to // emit. for example, stopped() - if(!watch.isValid()) + if (!watch.isValid()) return; - if(haveData) + if (haveData) rreads += dg; - } - else - { + } else { dg.addr = from; - dg.port = fromPort; - dg.buf = buf; + dg.buf = buf; dreads += dg; } } - if(dreads.count() > 0) - { + if (dreads.count() > 0) { in += dreads; emit q->readyRead(Direct); - if(!watch.isValid()) + if (!watch.isValid()) return; } - if(rreads.count() > 0) - { + if (rreads.count() > 0) { inRelayed += rreads; emit q->readyRead(Relayed); } @@ -529,66 +468,54 @@ private slots: void sock_datagramsWritten(int count) { QList dwrites; - int twrites = 0; + int twrites = 0; - while(count > 0) - { + while (count > 0) { Q_ASSERT(!pendingWrites.isEmpty()); WriteItem wi = pendingWrites.takeFirst(); --count; - if(wi.type == WriteItem::Direct) - { + if (wi.type == WriteItem::Direct) { int at = -1; - for(int n = 0; n < dwrites.count(); ++n) - { - if(dwrites[n].addr == wi.addr && dwrites[n].port == wi.port) - { + for (int n = 0; n < dwrites.count(); ++n) { + if (dwrites[n].addr == wi.addr) { at = n; break; } } - if(at != -1) - { + if (at != -1) { ++dwrites[at].count; - } - else - { + } else { Written wr; - wr.addr = wi.addr; - wr.port = wi.port; + wr.addr = wi.addr; wr.count = 1; dwrites += wr; } - } - else if(wi.type == WriteItem::Turn) + } else if (wi.type == WriteItem::Turn) ++twrites; } - if(dwrites.isEmpty() && twrites == 0) + if (dwrites.isEmpty() && twrites == 0) return; ObjectSessionWatcher watch(&sess); - if(!dwrites.isEmpty()) - { - foreach(const Written &wr, dwrites) - { - emit q->datagramsWritten(Direct, wr.count, wr.addr, wr.port); - if(!watch.isValid()) + if (!dwrites.isEmpty()) { + for (const Written &wr : std::as_const(dwrites)) { + emit q->datagramsWritten(Direct, wr.count, wr.addr); + if (!watch.isValid()) return; } } - if(twrites > 0) - { + if (twrites > 0) { // note: this will invoke turn_packetsWritten() turn->outgoingDatagramsWritten(twrites); } } - void pool_outgoingMessage(const QByteArray &packet, const QHostAddress &toAddress, int toPort) + void pool_outgoingMessage(const QByteArray &packet, const XMPP::TransportAddress &toAddress) { // warning: read StunTransactionPool docs before modifying // this function @@ -596,65 +523,45 @@ private slots: WriteItem wi; wi.type = WriteItem::Pool; pendingWrites += wi; - sock->writeDatagram(packet, toAddress, toPort); + // emit q->debugLine(QString("Sending udp packet from: %1:%2 to: %3:%4") + // .arg(sock->localAddress().toString()) + // .arg(sock->localPort()) + // .arg(toAddress.toString()) + // .arg(toPort)); + + sock->writeDatagram(packet, toAddress); } - void pool_needAuthParams() + void pool_needAuthParams(const TransportAddress &addr) { // we can get this signal if the user did not provide // creds to us. however, since this class doesn't support // prompting just continue on as if we had a blank // user/pass - pool->continueAfterParams(); - } - - void pool_debugLine(const QString &line) - { - emit q->debugLine(line); + pool->continueAfterParams(addr); } - void binding_success() - { - refAddr = stunBinding->reflexiveAddress(); - refPort = stunBinding->reflexivePort(); - - delete stunBinding; - stunBinding = 0; - - emit q->addressesChanged(); - } - - void binding_error(XMPP::StunBinding::Error e) - { - Q_UNUSED(e); - - delete stunBinding; - stunBinding = 0; - - // don't report any error - //if(stunType == IceLocalTransport::Basic || (stunType == IceLocalTransport::Auto && !turn)) - // emit q->addressesChanged(); - } + void pool_debugLine(const QString &line) { emit q->debugLine(line); } void turn_connected() { - if(debugLevel >= IceTransport::DL_Info) + if (debugLevel >= IceTransport::DL_Info) emit q->debugLine("turn_connected"); } void turn_tlsHandshaken() { - if(debugLevel >= IceTransport::DL_Info) + if (debugLevel >= IceTransport::DL_Info) emit q->debugLine("turn_tlsHandshaken"); } void turn_closed() { - if(debugLevel >= IceTransport::DL_Info) + if (debugLevel >= IceTransport::DL_Info) emit q->debugLine("turn_closed"); delete turn; - turn = 0; + turn = nullptr; turnActivated = false; postStop(); @@ -666,53 +573,51 @@ private slots: // take reflexive address from TURN only if we are not using a // separate STUN server - if(stunBindAddr.isNull() || stunBindAddr == stunRelayAddr) - { - refAddr = allocate->reflexiveAddress(); - refPort = allocate->reflexivePort(); + if (!stunBindAddr.isValid() || stunBindAddr == stunRelayAddr) { + refAddr = allocate->reflexiveAddress(); + refAddrSource = stunRelayAddr.addr; } - if(debugLevel >= IceTransport::DL_Info) - emit q->debugLine(QString("Server says we are ") + allocate->reflexiveAddress().toString() + ';' + QString::number(allocate->reflexivePort())); + if (debugLevel >= IceTransport::DL_Info) + emit q->debugLine(QLatin1String("Server says we are ") + allocate->reflexiveAddress()); relAddr = allocate->relayedAddress(); - relPort = allocate->relayedPort(); - if(debugLevel >= IceTransport::DL_Info) - emit q->debugLine(QString("Server relays via ") + relAddr.toString() + ';' + QString::number(relPort)); + if (debugLevel >= IceTransport::DL_Info) + emit q->debugLine(QLatin1String("Server relays via ") + relAddr); turnActivated = true; emit q->addressesChanged(); } - void turn_packetsWritten(int count, const QHostAddress &addr, int port) + void turn_packetsWritten(int count, const XMPP::TransportAddress &addr) { - emit q->datagramsWritten(Relayed, count, addr, port); + emit q->datagramsWritten(Relayed, count, addr); } void turn_error(XMPP::TurnClient::Error e) { - if(debugLevel >= IceTransport::DL_Info) + if (debugLevel >= IceTransport::DL_Info) emit q->debugLine(QString("turn_error: ") + turn->errorString()); delete turn; - turn = 0; + turn = nullptr; bool wasActivated = turnActivated; - turnActivated = false; + turnActivated = false; - if(e == TurnClient::ErrorMismatch) - { - if(!extSock && handleRetry()) + if (e == TurnClient::ErrorMismatch) { + if (!extSock && handleRetry()) return; } // this means our relay died on us. in the future we might // consider reporting this - if(wasActivated) + if (wasActivated) return; + emit q->error(IceLocalTransport::ErrorTurn); // don't report any error - //if(stunType == IceLocalTransport::Relay || (stunType == IceLocalTransport::Auto && !stunBinding)) + // if(stunType == IceLocalTransport::Relay || (stunType == IceLocalTransport::Auto && !stunBinding)) // emit q->addressesChanged(); } @@ -721,30 +626,17 @@ private slots: WriteItem wi; wi.type = WriteItem::Turn; pendingWrites += wi; - sock->writeDatagram(buf, stunRelayAddr, stunRelayPort); + sock->writeDatagram(buf, stunRelayAddr); } - void turn_debugLine(const QString &line) - { - emit q->debugLine(line); - } + void turn_debugLine(const QString &line) { emit q->debugLine(line); } }; -IceLocalTransport::IceLocalTransport(QObject *parent) : - IceTransport(parent) -{ - d = new Private(this); -} +IceLocalTransport::IceLocalTransport(QObject *parent) : IceTransport(parent) { d = new Private(this); } -IceLocalTransport::~IceLocalTransport() -{ - delete d; -} +IceLocalTransport::~IceLocalTransport() { delete d; } -void IceLocalTransport::setClientSoftwareNameAndVersion(const QString &str) -{ - d->clientSoftware = str; -} +void IceLocalTransport::setClientSoftwareNameAndVersion(const QString &str) { d->clientSoftware = str; } void IceLocalTransport::start(QUdpSocket *sock) { @@ -754,133 +646,107 @@ void IceLocalTransport::start(QUdpSocket *sock) void IceLocalTransport::start(const QHostAddress &addr) { - d->addr = addr; + d->addr.addr = addr; d->start(); } -void IceLocalTransport::stop() -{ - d->stop(); -} +void IceLocalTransport::stop() { d->stop(); } -void IceLocalTransport::setStunBindService(const QHostAddress &addr, int port) -{ - d->stunBindAddr = addr; - d->stunBindPort = port; -} +void IceLocalTransport::setStunBindService(const TransportAddress &addr) { d->stunBindAddr = addr; } -void IceLocalTransport::setStunRelayService(const QHostAddress &addr, int port, const QString &user, const QCA::SecureArray &pass) +void IceLocalTransport::setStunRelayService(const TransportAddress &addr, const QString &user, + const QCA::SecureArray &pass) { d->stunRelayAddr = addr; - d->stunRelayPort = port; - d->stunUser = user; - d->stunPass = pass; + d->stunUser = user; + d->stunPass = pass; } -void IceLocalTransport::stunStart() -{ - d->stunStart(); -} +const TransportAddress &IceLocalTransport::stunBindServiceAddress() const { return d->stunBindAddr; } -QHostAddress IceLocalTransport::localAddress() const -{ - return d->addr; -} +const TransportAddress &IceLocalTransport::stunRelayServiceAddress() const { return d->stunRelayAddr; } -int IceLocalTransport::localPort() const -{ - return d->port; -} +void IceLocalTransport::stunStart() { d->stunStart(); } -QHostAddress IceLocalTransport::serverReflexiveAddress() const -{ - return d->refAddr; -} +const TransportAddress &IceLocalTransport::localAddress() const { return d->addr; } -int IceLocalTransport::serverReflexivePort() const -{ - return d->refPort; -} +const TransportAddress &IceLocalTransport::serverReflexiveAddress() const { return d->refAddr; } -QHostAddress IceLocalTransport::relayedAddress() const -{ - return d->relAddr; -} +QHostAddress IceLocalTransport::reflexiveAddressSource() const { return d->refAddrSource; } -int IceLocalTransport::relayedPort() const -{ - return d->relPort; -} +const TransportAddress &IceLocalTransport::relayedAddress() const { return d->relAddr; } -void IceLocalTransport::addChannelPeer(const QHostAddress &addr, int port) +bool IceLocalTransport::isStunAlive() const { return d->stunBinding != nullptr; } + +bool IceLocalTransport::isTurnAlive() const { return d->turn != nullptr; } + +void IceLocalTransport::addChannelPeer(const TransportAddress &addr) { - if(d->turn) - d->turn->addChannelPeer(addr, port); + if (d->turn) + d->turn->addChannelPeer(addr); } bool IceLocalTransport::hasPendingDatagrams(int path) const { - if(path == Direct) + if (path == Direct) return !d->in.isEmpty(); - else if(path == Relayed) + else if (path == Relayed) return !d->inRelayed.isEmpty(); - else - { + else { Q_ASSERT(0); return false; } } -QByteArray IceLocalTransport::readDatagram(int path, QHostAddress *addr, int *port) +QByteArray IceLocalTransport::readDatagram(int path, TransportAddress &addr) { - QList *in = 0; - if(path == Direct) + QList *in = nullptr; + if (path == Direct) in = &d->in; - else if(path == Relayed) + else if (path == Relayed) in = &d->inRelayed; else Q_ASSERT(0); - if(!in->isEmpty()) - { + if (!in->isEmpty()) { Private::Datagram datagram = in->takeFirst(); - *addr = datagram.addr; - *port = datagram.port; + addr = datagram.addr; return datagram.buf; - } - else + } else return QByteArray(); } -void IceLocalTransport::writeDatagram(int path, const QByteArray &buf, const QHostAddress &addr, int port) +void IceLocalTransport::writeDatagram(int path, const QByteArray &buf, const TransportAddress &addr) { - if(path == Direct) - { + if (path == Direct) { Private::WriteItem wi; wi.type = Private::WriteItem::Direct; wi.addr = addr; - wi.port = port; d->pendingWrites += wi; - d->sock->writeDatagram(buf, addr, port); - } - else if(path == Relayed) - { - if(d->turn && d->turnActivated) - d->turn->write(buf, addr, port); - } - else + d->sock->writeDatagram(buf, addr); + } else if (path == Relayed) { + if (d->turn && d->turnActivated) + d->turn->write(buf, addr); + } else Q_ASSERT(0); } void IceLocalTransport::setDebugLevel(DebugLevel level) { d->debugLevel = level; - if(d->pool) + if (d->pool) d->pool->setDebugLevel((StunTransactionPool::DebugLevel)level); - if(d->turn) + if (d->turn) d->turn->setDebugLevel((TurnClient::DebugLevel)level); } +void IceLocalTransport::changeThread(QThread *thread) +{ + if (d->pool) + d->pool->moveToThread(thread); + moveToThread(thread); } +} // namespace XMPP + #include "icelocaltransport.moc" diff --git a/src/irisnet/noncore/icelocaltransport.h b/src/irisnet/noncore/icelocaltransport.h index e413c095..6d948727 100644 --- a/src/irisnet/noncore/icelocaltransport.h +++ b/src/irisnet/noncore/icelocaltransport.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009,2010 Barracuda Networks, Inc. + * Copyright (C) 2009-2010 Barracuda Networks, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -19,34 +19,31 @@ #ifndef ICELOCALTRANSPORT_H #define ICELOCALTRANSPORT_H -#include -#include #include "icetransport.h" +#include +#include +#include + class QHostAddress; class QUdpSocket; namespace QCA { - class SecureArray; +class SecureArray; } namespace XMPP { - // this class manages a single port on a single interface, including the // relationship with an associated STUN/TURN server. if TURN is used, this // class offers two paths (0=direct and 1=relayed), otherwise it offers // just one path (0=direct) -class IceLocalTransport : public IceTransport -{ +class IceLocalTransport : public IceTransport, public QEnableSharedFromThis { Q_OBJECT public: - enum Error - { - ErrorBind = ErrorCustom - }; + enum Error { ErrorBind = ErrorCustom, ErrorStun, ErrorTurn }; - IceLocalTransport(QObject *parent = 0); + IceLocalTransport(QObject *parent = nullptr); ~IceLocalTransport(); void setClientSoftwareNameAndVersion(const QString &str); @@ -59,28 +56,31 @@ class IceLocalTransport : public IceTransport // retries void start(const QHostAddress &addr); - void setStunBindService(const QHostAddress &addr, int port); - void setStunRelayService(const QHostAddress &addr, int port, const QString &user, const QCA::SecureArray &pass); + void setStunBindService(const TransportAddress &addr); + void setStunRelayService(const TransportAddress &addr, const QString &user, const QCA::SecureArray &pass); + + const TransportAddress &stunBindServiceAddress() const; + const TransportAddress &stunRelayServiceAddress() const; // obtain relay / reflexive void stunStart(); - QHostAddress localAddress() const; - int localPort() const; - - QHostAddress serverReflexiveAddress() const; - int serverReflexivePort() const; + const TransportAddress &localAddress() const; + const TransportAddress &serverReflexiveAddress() const; + QHostAddress reflexiveAddressSource() const; // address of stun/turn server provided the srflx + const TransportAddress &relayedAddress() const; - QHostAddress relayedAddress() const; - int relayedPort() const; + bool isStunAlive() const; + bool isTurnAlive() const; // reimplemented - virtual void stop(); - virtual bool hasPendingDatagrams(int path) const; - virtual QByteArray readDatagram(int path, QHostAddress *addr, int *port); - virtual void writeDatagram(int path, const QByteArray &buf, const QHostAddress &addr, int port); - virtual void addChannelPeer(const QHostAddress &addr, int port); - virtual void setDebugLevel(DebugLevel level); + void stop() override; + bool hasPendingDatagrams(int path) const override; + QByteArray readDatagram(int path, TransportAddress &addr) override; + void writeDatagram(int path, const QByteArray &buf, const TransportAddress &addr) override; + void addChannelPeer(const TransportAddress &addr) override; + void setDebugLevel(DebugLevel level) override; + void changeThread(QThread *thread) override; signals: // may be emitted multiple times. @@ -95,7 +95,6 @@ class IceLocalTransport : public IceTransport friend class Private; Private *d; }; +} // namespace XMPP -} - -#endif +#endif // ICELOCALTRANSPORT_H diff --git a/src/irisnet/noncore/icetransport.cpp b/src/irisnet/noncore/icetransport.cpp index 1a512d73..a34a80c4 100644 --- a/src/irisnet/noncore/icetransport.cpp +++ b/src/irisnet/noncore/icetransport.cpp @@ -19,14 +19,8 @@ #include "icetransport.h" namespace XMPP { +IceTransport::IceTransport(QObject *parent) : QObject(parent) { } -IceTransport::IceTransport(QObject *parent) : - QObject(parent) -{ -} +IceTransport::~IceTransport() { } -IceTransport::~IceTransport() -{ -} - -} +} // namespace XMPP diff --git a/src/irisnet/noncore/icetransport.h b/src/irisnet/noncore/icetransport.h index 82776069..5bf6ecef 100644 --- a/src/irisnet/noncore/icetransport.h +++ b/src/irisnet/noncore/icetransport.h @@ -19,55 +19,53 @@ #ifndef ICETRANSPORT_H #define ICETRANSPORT_H -#include #include +#include +#include class QHostAddress; namespace XMPP { - -class IceTransport : public QObject -{ +class TransportAddress; +class IceTransport : public QObject { Q_OBJECT public: - enum Error - { - ErrorGeneric, - ErrorCustom - }; - - enum DebugLevel - { - DL_None, - DL_Info, - DL_Packet - }; - - IceTransport(QObject *parent = 0); + enum Error { ErrorGeneric, ErrorCustom }; + + enum DebugLevel { DL_None, DL_Info, DL_Packet }; + + IceTransport(QObject *parent = nullptr); ~IceTransport(); virtual void stop() = 0; - virtual bool hasPendingDatagrams(int path) const = 0; - virtual QByteArray readDatagram(int path, QHostAddress *addr, int *port) = 0; - virtual void writeDatagram(int path, const QByteArray &buf, const QHostAddress &addr, int port) = 0; - virtual void addChannelPeer(const QHostAddress &addr, int port) = 0; + virtual bool hasPendingDatagrams(int path) const = 0; + virtual QByteArray readDatagram(int path, TransportAddress &addr) = 0; + virtual void writeDatagram(int path, const QByteArray &buf, const TransportAddress &addr) = 0; + virtual void addChannelPeer(const TransportAddress &addr) = 0; virtual void setDebugLevel(DebugLevel level) = 0; + virtual void changeThread(QThread *thread) = 0; signals: void started(); - void stopped(); + void stopped(); // emitted when stop() finished cleaning up void error(int e); void readyRead(int path); - void datagramsWritten(int path, int count, const QHostAddress &addr, int port); + void datagramsWritten(int path, int count, const TransportAddress &addr); // not DOR-SS/DS safe void debugLine(const QString &str); }; -} - +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) +inline uint qHash(const QWeakPointer &p) { return qHash(p.toStrongRef().data()); } +#else +inline size_t qHash(const QWeakPointer &p) { return qHash(p.toStrongRef().data()); } #endif + +} // namespace XMPP + +#endif // ICETRANSPORT_H diff --git a/src/irisnet/noncore/iceturntransport.cpp b/src/irisnet/noncore/iceturntransport.cpp index 37c1027e..033417ea 100644 --- a/src/irisnet/noncore/iceturntransport.cpp +++ b/src/irisnet/noncore/iceturntransport.cpp @@ -18,91 +18,83 @@ #include "iceturntransport.h" -#include #include "stunallocate.h" -namespace XMPP { +#include -class IceTurnTransport::Private : public QObject -{ +namespace XMPP { +class IceTurnTransport::Private : public QObject { Q_OBJECT public: IceTurnTransport *q; - int mode; - QHostAddress serverAddr; - int serverPort; - QString relayUser; - QCA::SecureArray relayPass; - QHostAddress relayAddr; - int relayPort; - TurnClient turn; - int turnErrorCode; - int debugLevel; - - Private(IceTurnTransport *_q) : - QObject(_q), - q(_q), - turn(this), - debugLevel(IceTransport::DL_None) + int mode; + TransportAddress serverAddr; + QString relayUser; + QCA::SecureArray relayPass; + TransportAddress relayAddr; + TransportAddress refAddr; + TurnClient turn; + int turnErrorCode = 0; + int debugLevel; + bool started = false; + + Private(IceTurnTransport *_q) : QObject(_q), q(_q), turn(this), debugLevel(IceTransport::DL_None) { - connect(&turn, SIGNAL(connected()), SLOT(turn_connected())); - connect(&turn, SIGNAL(tlsHandshaken()), SLOT(turn_tlsHandshaken())); - connect(&turn, SIGNAL(closed()), SLOT(turn_closed())); - connect(&turn, SIGNAL(needAuthParams()), SLOT(turn_needAuthParams())); - connect(&turn, SIGNAL(retrying()), SLOT(turn_retrying())); - connect(&turn, SIGNAL(activated()), SLOT(turn_activated())); - connect(&turn, SIGNAL(readyRead()), SLOT(turn_readyRead())); - connect(&turn, SIGNAL(packetsWritten(int,QHostAddress,int)), SLOT(turn_packetsWritten(int,QHostAddress,int))); - connect(&turn, SIGNAL(error(XMPP::TurnClient::Error)), SLOT(turn_error(XMPP::TurnClient::Error))); - connect(&turn, SIGNAL(debugLine(QString)), SLOT(turn_debugLine(QString))); + connect(&turn, &TurnClient::connected, this, &Private::turn_connected); + connect(&turn, &TurnClient::tlsHandshaken, this, &Private::turn_tlsHandshaken); + connect(&turn, &TurnClient::closed, this, &Private::turn_closed); + connect(&turn, &TurnClient::needAuthParams, this, &Private::turn_needAuthParams); + connect(&turn, &TurnClient::retrying, this, &Private::turn_retrying); + connect(&turn, &TurnClient::activated, this, &Private::turn_activated); + connect(&turn, &TurnClient::readyRead, this, &Private::turn_readyRead); + connect(&turn, &TurnClient::packetsWritten, this, &Private::turn_packetsWritten); + connect(&turn, &TurnClient::error, this, &Private::turn_error); + connect(&turn, &TurnClient::debugLine, this, &Private::turn_debugLine); } void start() { turn.setUsername(relayUser); turn.setPassword(relayPass); - turn.connectToHost(serverAddr, serverPort, (TurnClient::Mode)mode); + turn.connectToHost(serverAddr, (TurnClient::Mode)mode); } - void stop() - { - turn.close(); - } + void stop() { turn.close(); } private slots: void turn_connected() { - if(debugLevel >= IceTransport::DL_Info) + if (debugLevel >= IceTransport::DL_Info) emit q->debugLine("turn_connected"); } void turn_tlsHandshaken() { - if(debugLevel >= IceTransport::DL_Info) + if (debugLevel >= IceTransport::DL_Info) emit q->debugLine("turn_tlsHandshaken"); } void turn_closed() { - if(debugLevel >= IceTransport::DL_Info) + if (debugLevel >= IceTransport::DL_Info) emit q->debugLine("turn_closed"); emit q->stopped(); } - void turn_needAuthParams() + void turn_needAuthParams(const TransportAddress &addr) { // we can get this signal if the user did not provide // creds to us. however, since this class doesn't support // prompting just continue on as if we had a blank // user/pass - turn.continueAfterParams(); + turn.continueAfterParams(addr); } void turn_retrying() { - if(debugLevel >= IceTransport::DL_Info) + if (debugLevel >= IceTransport::DL_Info) emit q->debugLine("turn_retrying"); } @@ -110,132 +102,92 @@ private slots: { StunAllocate *allocate = turn.stunAllocate(); - QHostAddress saddr = allocate->reflexiveAddress(); - quint16 sport = allocate->reflexivePort(); - if(debugLevel >= IceTransport::DL_Info) - emit q->debugLine(QString("Server says we are ") + saddr.toString() + ';' + QString::number(sport)); + auto saddr = allocate->reflexiveAddress(); + if (debugLevel >= IceTransport::DL_Info) + emit q->debugLine(QLatin1String("Server says we are ") + saddr); saddr = allocate->relayedAddress(); - sport = allocate->relayedPort(); - if(debugLevel >= IceTransport::DL_Info) - emit q->debugLine(QString("Server relays via ") + saddr.toString() + ';' + QString::number(sport)); + if (debugLevel >= IceTransport::DL_Info) + emit q->debugLine(QLatin1String("Server relays via ") + saddr); relayAddr = saddr; - relayPort = sport; + refAddr = allocate->reflexiveAddress(); + started = true; emit q->started(); } - void turn_readyRead() - { - emit q->readyRead(0); - } + void turn_readyRead() { emit q->readyRead(0); } - void turn_packetsWritten(int count, const QHostAddress &addr, int port) - { - emit q->datagramsWritten(0, count, addr, port); - } + void turn_packetsWritten(int count, const TransportAddress &addr) { emit q->datagramsWritten(0, count, addr); } void turn_error(XMPP::TurnClient::Error e) { - if(debugLevel >= IceTransport::DL_Info) + if (debugLevel >= IceTransport::DL_Info) emit q->debugLine(QString("turn_error: ") + turn.errorString()); turnErrorCode = e; emit q->error(IceTurnTransport::ErrorTurn); } - void turn_debugLine(const QString &line) - { - emit q->debugLine(line); - } + void turn_debugLine(const QString &line) { emit q->debugLine(line); } }; -IceTurnTransport::IceTurnTransport(QObject *parent) : - IceTransport(parent) -{ - d = new Private(this); -} +IceTurnTransport::IceTurnTransport(QObject *parent) : IceTransport(parent) { d = new Private(this); } -IceTurnTransport::~IceTurnTransport() -{ - delete d; -} +IceTurnTransport::~IceTurnTransport() { delete d; } void IceTurnTransport::setClientSoftwareNameAndVersion(const QString &str) { d->turn.setClientSoftwareNameAndVersion(str); } -void IceTurnTransport::setUsername(const QString &user) -{ - d->relayUser = user; -} +void IceTurnTransport::setUsername(const QString &user) { d->relayUser = user; } -void IceTurnTransport::setPassword(const QCA::SecureArray &pass) -{ - d->relayPass = pass; -} +void IceTurnTransport::setPassword(const QCA::SecureArray &pass) { d->relayPass = pass; } -void IceTurnTransport::setProxy(const TurnClient::Proxy &proxy) -{ - d->turn.setProxy(proxy); -} +void IceTurnTransport::setProxy(const TurnClient::Proxy &proxy) { d->turn.setProxy(proxy); } -void IceTurnTransport::start(const QHostAddress &addr, int port, TurnClient::Mode mode) +void IceTurnTransport::start(const TransportAddress &addr, TurnClient::Mode mode) { d->serverAddr = addr; - d->serverPort = port; - d->mode = mode; + d->mode = mode; d->start(); } -QHostAddress IceTurnTransport::relayedAddress() const -{ - return d->relayAddr; -} +const TransportAddress &IceTurnTransport::relayedAddress() const { return d->relayAddr; } -int IceTurnTransport::relayedPort() const -{ - return d->relayPort; -} +const TransportAddress &IceTurnTransport::reflexiveAddress() const { return d->refAddr; } -void IceTurnTransport::addChannelPeer(const QHostAddress &addr, int port) -{ - d->turn.addChannelPeer(addr, port); -} +bool IceTurnTransport::isStarted() const { return d->started; } -TurnClient::Error IceTurnTransport::turnErrorCode() const -{ - return (TurnClient::Error)d->turnErrorCode; -} +void IceTurnTransport::addChannelPeer(const TransportAddress &addr) { d->turn.addChannelPeer(addr); } -void IceTurnTransport::stop() -{ - d->stop(); -} +TurnClient::Error IceTurnTransport::turnErrorCode() const { return (TurnClient::Error)d->turnErrorCode; } + +void IceTurnTransport::stop() { d->stop(); } bool IceTurnTransport::hasPendingDatagrams(int path) const { Q_ASSERT(path == 0); - Q_UNUSED(path); + Q_UNUSED(path) - return (d->turn.packetsToRead() > 0 ? true : false); + return d->turn.packetsToRead() > 0; } -QByteArray IceTurnTransport::readDatagram(int path, QHostAddress *addr, int *port) +QByteArray IceTurnTransport::readDatagram(int path, TransportAddress &addr) { Q_ASSERT(path == 0); - Q_UNUSED(path); + Q_UNUSED(path) - return d->turn.read(addr, port); + return d->turn.read(addr); } -void IceTurnTransport::writeDatagram(int path, const QByteArray &buf, const QHostAddress &addr, int port) +void IceTurnTransport::writeDatagram(int path, const QByteArray &buf, const TransportAddress &addr) { Q_ASSERT(path == 0); - Q_UNUSED(path); + Q_UNUSED(path) - d->turn.write(buf, addr, port); + d->turn.write(buf, addr); } void IceTurnTransport::setDebugLevel(DebugLevel level) @@ -244,6 +196,12 @@ void IceTurnTransport::setDebugLevel(DebugLevel level) d->turn.setDebugLevel((TurnClient::DebugLevel)level); } +void IceTurnTransport::changeThread(QThread *thread) +{ + d->turn.changeThread(thread); + moveToThread(thread); } +} // namespace XMPP + #include "iceturntransport.moc" diff --git a/src/irisnet/noncore/iceturntransport.h b/src/irisnet/noncore/iceturntransport.h index 46677a08..1414e899 100644 --- a/src/irisnet/noncore/iceturntransport.h +++ b/src/irisnet/noncore/iceturntransport.h @@ -19,27 +19,23 @@ #ifndef ICETURNTRANSPORT_H #define ICETURNTRANSPORT_H -#include +#include "icetransport.h" +#include "turnclient.h" + #include +#include #include -#include "turnclient.h" -#include "icetransport.h" +#include namespace XMPP { - // for the turn transport, only path 0 is used - -class IceTurnTransport : public IceTransport -{ +class IceTurnTransport : public IceTransport, public QEnableSharedFromThis { Q_OBJECT public: - enum Error - { - ErrorTurn = ErrorCustom - }; + enum Error { ErrorTurn = ErrorCustom }; - IceTurnTransport(QObject *parent = 0); + IceTurnTransport(QObject *parent = nullptr); ~IceTurnTransport(); void setClientSoftwareNameAndVersion(const QString &str); @@ -50,27 +46,28 @@ class IceTurnTransport : public IceTransport void setProxy(const TurnClient::Proxy &proxy); - void start(const QHostAddress &addr, int port, TurnClient::Mode mode = TurnClient::PlainMode); + void start(const TransportAddress &addr, TurnClient::Mode mode = TurnClient::PlainMode); - QHostAddress relayedAddress() const; - int relayedPort() const; + const TransportAddress &relayedAddress() const; + const TransportAddress &reflexiveAddress() const; + bool isStarted() const; TurnClient::Error turnErrorCode() const; // reimplemented - virtual void stop(); - virtual bool hasPendingDatagrams(int path) const; - virtual QByteArray readDatagram(int path, QHostAddress *addr, int *port); - virtual void writeDatagram(int path, const QByteArray &buf, const QHostAddress &addr, int port); - virtual void addChannelPeer(const QHostAddress &addr, int port); - virtual void setDebugLevel(DebugLevel level); + virtual void stop() override; + virtual bool hasPendingDatagrams(int path) const override; + virtual QByteArray readDatagram(int path, TransportAddress &addr) override; + virtual void writeDatagram(int path, const QByteArray &buf, const TransportAddress &addr) override; + virtual void addChannelPeer(const TransportAddress &addr) override; + virtual void setDebugLevel(DebugLevel level) override; + virtual void changeThread(QThread *thread) override; private: class Private; friend class Private; Private *d; }; +} // namespace XMPP -} - -#endif +#endif // ICETURNTRANSPORT_H diff --git a/src/irisnet/noncore/legacy/legacy.pri b/src/irisnet/noncore/legacy/legacy.pri deleted file mode 100644 index 7c8838da..00000000 --- a/src/irisnet/noncore/legacy/legacy.pri +++ /dev/null @@ -1,7 +0,0 @@ -HEADERS += \ - $$PWD/ndns.h \ - $$PWD/srvresolver.h - -SOURCES += \ - $$PWD/ndns.cpp \ - $$PWD/srvresolver.cpp diff --git a/src/irisnet/noncore/legacy/ndns.cpp b/src/irisnet/noncore/legacy/ndns.cpp index 5a68a8c4..753e2359 100644 --- a/src/irisnet/noncore/legacy/ndns.cpp +++ b/src/irisnet/noncore/legacy/ndns.cpp @@ -1,6 +1,6 @@ /* * ndns.cpp - native DNS resolution - * Copyright (C) 2001, 2002 Justin Karneges + * Copyright (C) 2001-2002 Justin Karneges * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -61,8 +61,7 @@ //! //! Constructs an NDns object with parent \a parent. -NDns::NDns(QObject *parent) -:QObject(parent),dns(this) +NDns::NDns(QObject *parent) : QObject(parent), dns(this) { busy = false; @@ -72,10 +71,7 @@ NDns::NDns(QObject *parent) //! //! Destroys the object and frees allocated resources. -NDns::~NDns() -{ - stop(); -} +NDns::~NDns() { stop(); } //! //! Resolves hostname \a host (eg. psi.affinix.com) @@ -98,25 +94,16 @@ void NDns::stop() //! //! Returns the IP address as a 32-bit integer in host-byte-order. This will be 0 if the lookup failed. //! \sa resultsReady() -QHostAddress NDns::result() const -{ - return addr; -} +QHostAddress NDns::result() const { return addr; } //! //! Returns the IP address as a string. This will be an empty string if the lookup failed. //! \sa resultsReady() -QString NDns::resultString() const -{ - return addr.toString(); -} +QString NDns::resultString() const { return addr.toString(); } //! //! Returns TRUE if busy resolving a hostname. -bool NDns::isBusy() const -{ - return busy; -} +bool NDns::isBusy() const { return busy; } void NDns::dns_resultsReady(const QList &results) { @@ -133,4 +120,3 @@ void NDns::dns_error(XMPP::NameResolver::Error) } // CS_NAMESPACE_END - diff --git a/src/irisnet/noncore/legacy/ndns.h b/src/irisnet/noncore/legacy/ndns.h index 8b74b8eb..fedf4ddb 100644 --- a/src/irisnet/noncore/legacy/ndns.h +++ b/src/irisnet/noncore/legacy/ndns.h @@ -1,6 +1,6 @@ /* * ndns.h - native DNS resolution - * Copyright (C) 2001, 2002 Justin Karneges + * Copyright (C) 2001-2002 Justin Karneges * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -20,17 +20,16 @@ #ifndef CS_NDNS_H #define CS_NDNS_H +#include "netnames.h" + #include #include -#include "netnames.h" // CS_NAMESPACE_BEGIN - -class NDns : public QObject -{ +class NDns : public QObject { Q_OBJECT public: - NDns(QObject *parent=0); + NDns(QObject *parent = nullptr); ~NDns(); void resolve(const QString &); @@ -38,7 +37,7 @@ class NDns : public QObject bool isBusy() const; QHostAddress result() const; - QString resultString() const; + QString resultString() const; signals: void resultsReady(); @@ -49,10 +48,10 @@ private slots: private: XMPP::NameResolver dns; - bool busy; - QHostAddress addr; + bool busy; + QHostAddress addr; }; // CS_NAMESPACE_END -#endif +#endif // CS_NDNS_H diff --git a/src/irisnet/noncore/legacy/srvresolver.cpp b/src/irisnet/noncore/legacy/srvresolver.cpp index f3d747df..08ab8bc6 100644 --- a/src/irisnet/noncore/legacy/srvresolver.cpp +++ b/src/irisnet/noncore/legacy/srvresolver.cpp @@ -20,27 +20,26 @@ #include "srvresolver.h" #ifndef NO_NDNS -# include "ndns.h" +#include "ndns.h" #endif // CS_NAMESPACE_BEGIN - static void sortSRVList(QList &list) { QList tmp = list; list.clear(); - while(!tmp.isEmpty()) { + while (!tmp.isEmpty()) { QList::Iterator p = tmp.end(); - for(QList::Iterator it = tmp.begin(); it != tmp.end(); ++it) { - if(p == tmp.end()) + for (QList::Iterator it = tmp.begin(); it != tmp.end(); ++it) { + if (p == tmp.end()) p = it; else { int a = (*it).priority; int b = (*p).priority; int j = (*it).weight; int k = (*p).weight; - if(a < b || (a == b && j < k)) + if (a < b || (a == b && j < k)) p = it; } } @@ -49,8 +48,7 @@ static void sortSRVList(QList &list) } } -class SrvResolver::Private -{ +class SrvResolver::Private { public: Private(SrvResolver *_q) : nndns(_q), @@ -61,33 +59,33 @@ class SrvResolver::Private { } - XMPP::NameResolver nndns; + XMPP::NameResolver nndns; XMPP::NameRecord::Type nntype; - bool nndns_busy; + bool nndns_busy; #ifndef NO_NDNS NDns ndns; #endif - bool failed; + bool failed; QHostAddress resultAddress; - quint16 resultPort; + quint16 resultPort; - bool srvonly; - QString srv; + bool srvonly; + QString srv; QList servers; - bool aaaa; + bool aaaa; QTimer t; }; -SrvResolver::SrvResolver(QObject *parent) -:QObject(parent) +SrvResolver::SrvResolver(QObject *parent) : QObject(parent) { - d = new Private(this); + d = new Private(this); d->nndns_busy = false; - connect(&d->nndns, SIGNAL(resultsReady(QList)), SLOT(nndns_resultsReady(QList))); + connect(&d->nndns, SIGNAL(resultsReady(QList)), + SLOT(nndns_resultsReady(QList))); connect(&d->nndns, SIGNAL(error(XMPP::NameResolver::Error)), SLOT(nndns_error(XMPP::NameResolver::Error))); #ifndef NO_NDNS @@ -107,13 +105,13 @@ void SrvResolver::resolve(const QString &server, const QString &type, const QStr { stop(); - d->failed = false; + d->failed = false; d->srvonly = false; - d->srv = QString("_") + type + "._" + proto + '.' + server; + d->srv = QString("_") + type + "._" + proto + '.' + server; d->t.setSingleShot(true); d->t.start(15000); d->nndns_busy = true; - d->nntype = XMPP::NameRecord::Srv; + d->nntype = XMPP::NameRecord::Srv; d->nndns.start(d->srv.toLatin1(), d->nntype); } @@ -121,19 +119,19 @@ void SrvResolver::resolveSrvOnly(const QString &server, const QString &type, con { stop(); - d->failed = false; + d->failed = false; d->srvonly = true; - d->srv = QString("_") + type + "._" + proto + '.' + server; + d->srv = QString("_") + type + "._" + proto + '.' + server; d->t.setSingleShot(true); d->t.start(15000); d->nndns_busy = true; - d->nntype = XMPP::NameRecord::Srv; + d->nntype = XMPP::NameRecord::Srv; d->nndns.start(d->srv.toLatin1(), d->nntype); } void SrvResolver::next() { - if(d->servers.isEmpty()) + if (d->servers.isEmpty()) return; tryNext(); @@ -141,54 +139,42 @@ void SrvResolver::next() void SrvResolver::stop() { - if(d->t.isActive()) + if (d->t.isActive()) d->t.stop(); - if(d->nndns_busy) { + if (d->nndns_busy) { d->nndns.stop(); d->nndns_busy = false; } #ifndef NO_NDNS - if(d->ndns.isBusy()) + if (d->ndns.isBusy()) d->ndns.stop(); #endif d->resultAddress = QHostAddress(); - d->resultPort = 0; + d->resultPort = 0; d->servers.clear(); - d->srv = ""; + d->srv = ""; d->failed = true; } bool SrvResolver::isBusy() const { #ifndef NO_NDNS - if(d->nndns_busy || d->ndns.isBusy()) + if (d->nndns_busy || d->ndns.isBusy()) #else - if(d->nndns_busy) + if (d->nndns_busy) #endif return true; else return false; } -QList SrvResolver::servers() const -{ - return d->servers; -} +QList SrvResolver::servers() const { return d->servers; } -bool SrvResolver::failed() const -{ - return d->failed; -} +bool SrvResolver::failed() const { return d->failed; } -QHostAddress SrvResolver::resultAddress() const -{ - return d->resultAddress; -} +QHostAddress SrvResolver::resultAddress() const { return d->resultAddress; } -quint16 SrvResolver::resultPort() const -{ - return d->resultPort; -} +quint16 SrvResolver::resultPort() const { return d->resultPort; } void SrvResolver::tryNext() { @@ -196,76 +182,72 @@ void SrvResolver::tryNext() d->ndns.resolve(d->servers.first().name); #else d->nndns_busy = true; - d->nntype = d->aaaa ? XMPP::NameRecord::Aaaa : XMPP::NameRecord::A; + d->nntype = d->aaaa ? XMPP::NameRecord::Aaaa : XMPP::NameRecord::A; d->nndns.start(d->servers.first().name.toLatin1(), d->nntype); #endif } void SrvResolver::nndns_resultsReady(const QList &results) { - if(!d->nndns_busy) + if (!d->nndns_busy) return; d->t.stop(); - if(d->nntype == XMPP::NameRecord::Srv) { + if (d->nntype == XMPP::NameRecord::Srv) { // grab the server list and destroy the qdns object QList list; - for(int n = 0; n < results.count(); ++n) - { - list += Q3Dns::Server(QString::fromLatin1(results[n].name()), results[n].priority(), results[n].weight(), results[n].port()); + for (int n = 0; n < results.count(); ++n) { + list += Q3Dns::Server(QString::fromLatin1(results[n].name()), quint16(results[n].priority()), + quint16(results[n].weight()), quint16(results[n].port())); } d->nndns_busy = false; d->nndns.stop(); - if(list.isEmpty()) { + if (list.isEmpty()) { stop(); - resultsReady(); + emit resultsReady(); return; } sortSRVList(list); d->servers = list; - if(d->srvonly) - resultsReady(); + if (d->srvonly) + emit resultsReady(); else { // kick it off d->aaaa = true; tryNext(); } - } - else { + } else { // grab the address list and destroy the qdns object QList list; - if(d->nntype == XMPP::NameRecord::A || d->nntype == XMPP::NameRecord::Aaaa) - { - for(int n = 0; n < results.count(); ++n) - { + if (d->nntype == XMPP::NameRecord::A || d->nntype == XMPP::NameRecord::Aaaa) { + for (int n = 0; n < results.count(); ++n) { list += results[n].address(); } } d->nndns_busy = false; d->nndns.stop(); - if(!list.isEmpty()) { + if (!list.isEmpty()) { int port = d->servers.first().port; d->servers.removeFirst(); d->aaaa = true; d->resultAddress = list.first(); - d->resultPort = port; - resultsReady(); - } - else { - if(!d->aaaa) + d->resultPort = quint16(port); + emit resultsReady(); + } else { + if (!d->aaaa) d->servers.removeFirst(); d->aaaa = !d->aaaa; // failed? bail if last one - if(d->servers.isEmpty()) { + if (d->servers.isEmpty()) { stop(); - resultsReady(); + emit resultsReady(); return; } @@ -275,28 +257,24 @@ void SrvResolver::nndns_resultsReady(const QList &results) } } -void SrvResolver::nndns_error(XMPP::NameResolver::Error) -{ - nndns_resultsReady(QList()); -} +void SrvResolver::nndns_error(XMPP::NameResolver::Error) { nndns_resultsReady(QList()); } void SrvResolver::ndns_done() { #ifndef NO_NDNS - QHostAddress r = d->ndns.result(); - int port = d->servers.first().port; + QHostAddress r = d->ndns.result(); + int port = d->servers.first().port; d->servers.removeFirst(); - if(!r.isNull()) { + if (!r.isNull()) { d->resultAddress = d->ndns.result(); - d->resultPort = port; - resultsReady(); - } - else { + d->resultPort = quint16(port); + emit resultsReady(); + } else { // failed? bail if last one - if(d->servers.isEmpty()) { + if (d->servers.isEmpty()) { stop(); - resultsReady(); + emit resultsReady(); return; } @@ -309,7 +287,7 @@ void SrvResolver::ndns_done() void SrvResolver::t_timeout() { stop(); - resultsReady(); + emit resultsReady(); } // CS_NAMESPACE_END diff --git a/src/irisnet/noncore/legacy/srvresolver.h b/src/irisnet/noncore/legacy/srvresolver.h index 07dca3df..ded8502a 100644 --- a/src/irisnet/noncore/legacy/srvresolver.h +++ b/src/irisnet/noncore/legacy/srvresolver.h @@ -20,20 +20,20 @@ #ifndef CS_SRVRESOLVER_H #define CS_SRVRESOLVER_H +#include "netnames.h" + #include #include -#include "netnames.h" // CS_NAMESPACE_BEGIN - -class Q3Dns -{ +class Q3Dns { public: - class Server - { + class Server { public: - Server(const QString &n = QString(), quint16 p = 0, quint16 w = 0, quint16 po = 0) - :name(n), priority(p), weight(w), port(po) {} + Server(const QString &n = QString(), quint16 p = 0, quint16 w = 0, quint16 po = 0) : + name(n), priority(p), weight(w), port(po) + { + } QString name; quint16 priority; @@ -42,11 +42,10 @@ class Q3Dns }; }; -class SrvResolver : public QObject -{ +class SrvResolver : public QObject { Q_OBJECT public: - SrvResolver(QObject *parent=0); + SrvResolver(QObject *parent = nullptr); ~SrvResolver(); void resolve(const QString &server, const QString &type, const QString &proto); @@ -57,9 +56,9 @@ class SrvResolver : public QObject QList servers() const; - bool failed() const; + bool failed() const; QHostAddress resultAddress() const; - quint16 resultPort() const; + quint16 resultPort() const; signals: void resultsReady(); @@ -79,4 +78,4 @@ private slots: // CS_NAMESPACE_END -#endif +#endif // CS_SRVRESOLVER_H diff --git a/src/irisnet/noncore/noncore.pri b/src/irisnet/noncore/noncore.pri deleted file mode 100644 index 559ed568..00000000 --- a/src/irisnet/noncore/noncore.pri +++ /dev/null @@ -1,49 +0,0 @@ -IRIS_BASE = $$PWD/../../.. - -QT *= network - -irisnetcore_bundle:{ - include(../corelib/corelib.pri) -} -else { - LIBS += -L$$IRIS_BASE/lib -lirisnetcore -} - -INCLUDEPATH += $$PWD/../corelib - -include($$PWD/cutestuff/cutestuff.pri) - -HEADERS += \ - $$PWD/processquit.h \ - $$PWD/stunutil.h \ - $$PWD/stunmessage.h \ - $$PWD/stuntypes.h \ - $$PWD/stuntransaction.h \ - $$PWD/stunbinding.h \ - $$PWD/stunallocate.h \ - $$PWD/turnclient.h \ - $$PWD/udpportreserver.h \ - $$PWD/icetransport.h \ - $$PWD/icelocaltransport.h \ - $$PWD/iceturntransport.h \ - $$PWD/icecomponent.h \ - $$PWD/ice176.h - -SOURCES += \ - $$PWD/processquit.cpp \ - $$PWD/stunutil.cpp \ - $$PWD/stunmessage.cpp \ - $$PWD/stuntypes.cpp \ - $$PWD/stuntransaction.cpp \ - $$PWD/stunbinding.cpp \ - $$PWD/stunallocate.cpp \ - $$PWD/turnclient.cpp \ - $$PWD/udpportreserver.cpp \ - $$PWD/icetransport.cpp \ - $$PWD/icelocaltransport.cpp \ - $$PWD/iceturntransport.cpp \ - $$PWD/icecomponent.cpp \ - $$PWD/ice176.cpp - -INCLUDEPATH += $$PWD/legacy -include(legacy/legacy.pri) diff --git a/src/irisnet/noncore/noncore.pro b/src/irisnet/noncore/noncore.pro deleted file mode 100644 index 66676d00..00000000 --- a/src/irisnet/noncore/noncore.pro +++ /dev/null @@ -1,15 +0,0 @@ -IRIS_BASE = ../../.. - -TEMPLATE = lib -QT -= gui -TARGET = irisnet -DESTDIR = $$IRIS_BASE/lib -CONFIG += staticlib create_prl - -VERSION = 1.0.0 - -include(../../libbase.pri) -include(noncore.pri) - -windows:!staticlib:DEFINES += IRISNET_MAKEDLL -staticlib:PRL_EXPORT_DEFINES += IRISNET_STATIC diff --git a/src/irisnet/noncore/processquit.cpp b/src/irisnet/noncore/processquit.cpp index cc6935c8..f30d2a73 100644 --- a/src/irisnet/noncore/processquit.cpp +++ b/src/irisnet/noncore/processquit.cpp @@ -19,40 +19,33 @@ #include "processquit.h" #ifndef NO_IRISNET -# include "irisnetglobal_p.h" +#include "corelib/irisnetglobal_p.h" #endif #ifdef QT_GUI_LIB -# include +#include #endif - -#ifdef Q_OS_WIN -# include -#endif - #ifdef Q_OS_UNIX -# include -# include +#include +#include +#endif +#ifdef Q_OS_WIN +#include #endif namespace { - // safeobj stuff, from qca - void releaseAndDeleteLater(QObject *owner, QObject *obj) { obj->disconnect(owner); - obj->setParent(0); + obj->setParent(nullptr); obj->deleteLater(); } -class SafeSocketNotifier : public QObject -{ +class SafeSocketNotifier : public QObject { Q_OBJECT public: - SafeSocketNotifier(int socket, QSocketNotifier::Type type, - QObject *parent = 0) : - QObject(parent) + SafeSocketNotifier(int socket, QSocketNotifier::Type type, QObject *parent = nullptr) : QObject(parent) { sn = new QSocketNotifier(socket, type, this); connect(sn, SIGNAL(activated(int)), SIGNAL(activated(int))); @@ -64,12 +57,12 @@ class SafeSocketNotifier : public QObject releaseAndDeleteLater(this, sn); } - bool isEnabled() const { return sn->isEnabled(); } - int socket() const { return sn->socket(); } + bool isEnabled() const { return sn->isEnabled(); } + int socket() const { return int(sn->socket()); } QSocketNotifier::Type type() const { return sn->type(); } public slots: - void setEnabled(bool enable) { sn->setEnabled(enable); } + void setEnabled(bool enable) { sn->setEnabled(enable); } signals: void activated(int socket); @@ -77,15 +70,14 @@ public slots: private: QSocketNotifier *sn; }; - -} +} // namespace #ifndef NO_IRISNET namespace XMPP { #endif Q_GLOBAL_STATIC(QMutex, pq_mutex) -static ProcessQuit *g_pq = 0; +static ProcessQuit *g_pq = nullptr; inline bool is_gui_app() { @@ -96,8 +88,7 @@ inline bool is_gui_app() #endif } -class ProcessQuit::Private : public QObject -{ +class ProcessQuit::Private : public QObject { Q_OBJECT public: ProcessQuit *q; @@ -107,7 +98,7 @@ class ProcessQuit::Private : public QObject bool use_handler; #endif #ifdef Q_OS_UNIX - int sig_pipe[2]; + int sig_pipe[2]; SafeSocketNotifier *sig_notifier; #endif @@ -116,12 +107,11 @@ class ProcessQuit::Private : public QObject done = false; #ifdef Q_OS_WIN use_handler = !is_gui_app(); - if(use_handler) + if (use_handler) SetConsoleCtrlHandler((PHANDLER_ROUTINE)winHandler, TRUE); #endif #ifdef Q_OS_UNIX - if(pipe(sig_pipe) == -1) - { + if (pipe(sig_pipe) == -1) { // no support then return; } @@ -137,7 +127,7 @@ class ProcessQuit::Private : public QObject ~Private() { #ifdef Q_OS_WIN - if(use_handler) + if (use_handler) SetConsoleCtrlHandler((PHANDLER_ROUTINE)winHandler, FALSE); #endif #ifdef Q_OS_UNIX @@ -164,8 +154,7 @@ class ProcessQuit::Private : public QObject { Q_UNUSED(sig); unsigned char c = 0; - if(::write(g_pq->d->sig_pipe[1], &c, 1) == -1) - { + if (::write(g_pq->d->sig_pipe[1], &c, 1) == -1) { // TODO: error handling? return; } @@ -174,29 +163,29 @@ class ProcessQuit::Private : public QObject void unixWatchAdd(int sig) { struct sigaction sa; - sigaction(sig, NULL, &sa); + sigaction(sig, nullptr, &sa); // if the signal is ignored, don't take it over. this is // recommended by the glibc manual - if(sa.sa_handler == SIG_IGN) + if (sa.sa_handler == SIG_IGN) return; sigemptyset(&(sa.sa_mask)); - sa.sa_flags = 0; + sa.sa_flags = 0; sa.sa_handler = unixHandler; - sigaction(sig, &sa, 0); + sigaction(sig, &sa, nullptr); } void unixWatchRemove(int sig) { struct sigaction sa; - sigaction(sig, NULL, &sa); + sigaction(sig, nullptr, &sa); // ignored means we skipped it earlier, so we should // skip it again - if(sa.sa_handler == SIG_IGN) + if (sa.sa_handler == SIG_IGN) return; sigemptyset(&(sa.sa_mask)); - sa.sa_flags = 0; + sa.sa_flags = 0; sa.sa_handler = SIG_DFL; - sigaction(sig, &sa, 0); + sigaction(sig, &sa, nullptr); } #endif @@ -212,8 +201,7 @@ public slots: { #ifdef Q_OS_UNIX unsigned char c; - if(::read(sig_pipe[0], &c, 1) == -1) - { + if (::read(sig_pipe[0], &c, 1) == -1) { // TODO: error handling? return; } @@ -226,30 +214,21 @@ public slots: void do_emit() { // only signal once - if(!done) - { + if (!done) { done = true; emit q->quit(); } } }; -ProcessQuit::ProcessQuit(QObject *parent) -:QObject(parent) -{ - d = new Private(this); -} +ProcessQuit::ProcessQuit(QObject *parent) : QObject(parent) { d = new Private(this); } -ProcessQuit::~ProcessQuit() -{ - delete d; -} +ProcessQuit::~ProcessQuit() { delete d; } ProcessQuit *ProcessQuit::instance() { QMutexLocker locker(pq_mutex()); - if(!g_pq) - { + if (!g_pq) { g_pq = new ProcessQuit; g_pq->moveToThread(QCoreApplication::instance()->thread()); #ifndef NO_IRISNET @@ -262,18 +241,18 @@ ProcessQuit *ProcessQuit::instance() void ProcessQuit::reset() { QMutexLocker locker(pq_mutex()); - if(g_pq) + if (g_pq) g_pq->d->done = false; } void ProcessQuit::cleanup() { delete g_pq; - g_pq = 0; + g_pq = nullptr; } #ifndef NO_IRISNET } -#endif +#endif // NO_IRISNET #include "processquit.moc" diff --git a/src/irisnet/noncore/processquit.h b/src/irisnet/noncore/processquit.h index d9e3b552..39d0e2d8 100644 --- a/src/irisnet/noncore/processquit.h +++ b/src/irisnet/noncore/processquit.h @@ -20,10 +20,10 @@ #define PROCESSQUIT_H #ifdef NO_IRISNET -# include -# define IRISNET_EXPORT +#include +#define IRISNET_EXPORT #else -# include "irisnetglobal.h" +#include "../corelib/irisnetglobal.h" #endif #ifndef NO_IRISNET @@ -55,8 +55,7 @@ myapp.connect(ProcessQuit::instance(), SIGNAL(quit()), SLOT(do_quit())); The quit() signal is emitted when a request to terminate is received. The quit() signal is only emitted once, future termination requests are ignored. Call reset() to allow the quit() signal to be emitted again. */ -class IRISNET_EXPORT ProcessQuit : public QObject -{ +class IRISNET_EXPORT ProcessQuit : public QObject { Q_OBJECT public: /** @@ -71,7 +70,10 @@ class IRISNET_EXPORT ProcessQuit : public QObject /** \brief Allows the quit() signal to be emitted again - ProcessQuit only emits the quit() signal once, so that if a user repeatedly presses Ctrl-C or sends SIGTERM, your shutdown slot will not be called multiple times. This is normally the desired behavior, but if you are ignoring the termination request then you may want to allow future notifications. Calling this function will allow the quit() signal to be emitted again, if a new termination request arrives. + ProcessQuit only emits the quit() signal once, so that if a user repeatedly presses Ctrl-C or sends SIGTERM, your + shutdown slot will not be called multiple times. This is normally the desired behavior, but if you are ignoring + the termination request then you may want to allow future notifications. Calling this function will allow the + quit() signal to be emitted again, if a new termination request arrives. \sa quit */ @@ -80,7 +82,9 @@ class IRISNET_EXPORT ProcessQuit : public QObject /** \brief Frees all resources used by ProcessQuit - This function will free any resources used by ProcessQuit, including the global instance, and the termination handlers will be uninstalled (reverted to default). Future termination requests will cause the application to exit abruptly. + This function will free any resources used by ProcessQuit, including the global instance, and the termination + handlers will be uninstalled (reverted to default). Future termination requests will cause the application to + exit abruptly. \note You normally do not need to call this function directly. When IrisNet cleans up, it will be called. @@ -92,9 +96,11 @@ class IRISNET_EXPORT ProcessQuit : public QObject /** \brief Notification of termination request - This signal is emitted when a termination request is received. It is only emitted once, unless reset() is called. + This signal is emitted when a termination request is received. It is only emitted once, unless reset() is + called. - Upon receiving this signal, the application should proceed to exit gracefully, and generally without user interaction. + Upon receiving this signal, the application should proceed to exit gracefully, and generally without user + interaction. \sa reset */ @@ -113,4 +119,4 @@ class IRISNET_EXPORT ProcessQuit : public QObject } #endif -#endif +#endif // PROCESSQUIT_H diff --git a/src/irisnet/noncore/sctp/DepUsrSCTP.cpp b/src/irisnet/noncore/sctp/DepUsrSCTP.cpp new file mode 100644 index 00000000..68be85e5 --- /dev/null +++ b/src/irisnet/noncore/sctp/DepUsrSCTP.cpp @@ -0,0 +1,207 @@ +#define MS_CLASS "DepUsrSCTP" +// #define MS_LOG_DEV_LEVEL 3 + +#include "DepUsrSCTP.hpp" +// #include "DepLibUV.hpp" +// #include "Logger.hpp" +#include + +#include +#include + +#include + +#define MS_TRACE() +#define MS_WARN_TAG(args, ...) +#define MS_DEBUG_TAG(args, ...) +#define MS_ASSERT(cond, what) Q_ASSERT_X(cond, "sctp", what) + +/* Static. */ + +static constexpr size_t CheckerInterval { 10u }; // In ms. + +/* Static methods for usrsctp global callbacks. */ + +inline static int onSendSctpData(void *addr, void *data, size_t len, uint8_t /*tos*/, uint8_t /*setDf*/) +{ + auto *sctpAssociation = DepUsrSCTP::RetrieveSctpAssociation(reinterpret_cast(addr)); + + if (!sctpAssociation) { + MS_WARN_TAG(sctp, "no SctpAssociation found"); + + return -1; + } + + sctpAssociation->OnUsrSctpSendSctpData(data, len); + + // NOTE: Must not free data, usrsctp lib does it. + + return 0; +} + +// Static method for printing usrsctp debug. +inline static void sctpDebug(const char *format, ...) +{ + char buffer[10000]; + va_list ap; + + va_start(ap, format); + vsprintf(buffer, format, ap); + + // Remove the artificial carriage return set by usrsctp. + buffer[std::strlen(buffer) - 1] = '\0'; + + MS_DEBUG_TAG(sctp, "%s", buffer); + + va_end(ap); +} + +/* Static variables. */ + +DepUsrSCTP::Checker *DepUsrSCTP::checker { nullptr }; +uint64_t DepUsrSCTP::numSctpAssociations { 0u }; +uintptr_t DepUsrSCTP::nextSctpAssociationId { 0u }; +std::unordered_map DepUsrSCTP::mapIdSctpAssociation; + +/* Static methods. */ + +void DepUsrSCTP::ClassInit() +{ + MS_TRACE(); + + MS_DEBUG_TAG(info, "usrsctp"); + + usrsctp_init_nothreads(0, onSendSctpData, sctpDebug); + + // Disable explicit congestion notifications (ecn). + usrsctp_sysctl_set_sctp_ecn_enable(0); + +#ifdef SCTP_DEBUG + usrsctp_sysctl_set_sctp_debug_on(SCTP_DEBUG_ALL); +#endif + + DepUsrSCTP::checker = new DepUsrSCTP::Checker(); +} + +void DepUsrSCTP::ClassDestroy() +{ + MS_TRACE(); + + usrsctp_finish(); + + delete DepUsrSCTP::checker; +} + +uintptr_t DepUsrSCTP::GetNextSctpAssociationId() +{ + MS_TRACE(); + + // NOTE: usrsctp_connect() fails with a value of 0. + if (DepUsrSCTP::nextSctpAssociationId == 0u) + ++DepUsrSCTP::nextSctpAssociationId; + + // In case we've wrapped around and need to find an empty spot from a removed + // SctpAssociation. Assumes we'll never be full. + while (DepUsrSCTP::mapIdSctpAssociation.find(DepUsrSCTP::nextSctpAssociationId) + != DepUsrSCTP::mapIdSctpAssociation.end()) { + ++DepUsrSCTP::nextSctpAssociationId; + + if (DepUsrSCTP::nextSctpAssociationId == 0u) + ++DepUsrSCTP::nextSctpAssociationId; + } + + return DepUsrSCTP::nextSctpAssociationId++; +} + +void DepUsrSCTP::RegisterSctpAssociation(RTC::SctpAssociation *sctpAssociation) +{ + MS_TRACE(); + + auto it = DepUsrSCTP::mapIdSctpAssociation.find(sctpAssociation->id); + + MS_ASSERT(it == DepUsrSCTP::mapIdSctpAssociation.end(), "the id of the SctpAssociation is already in the map"); + + DepUsrSCTP::mapIdSctpAssociation[sctpAssociation->id] = sctpAssociation; + + if (++DepUsrSCTP::numSctpAssociations == 1u) + DepUsrSCTP::checker->Start(); +} + +void DepUsrSCTP::DeregisterSctpAssociation(RTC::SctpAssociation *sctpAssociation) +{ + MS_TRACE(); + + auto found = DepUsrSCTP::mapIdSctpAssociation.erase(sctpAssociation->id); + + MS_ASSERT(found > 0, "SctpAssociation not found"); + MS_ASSERT(DepUsrSCTP::numSctpAssociations > 0u, "numSctpAssociations was not higher than 0"); + + if (--DepUsrSCTP::numSctpAssociations == 0u) + DepUsrSCTP::checker->Stop(); +} + +RTC::SctpAssociation *DepUsrSCTP::RetrieveSctpAssociation(uintptr_t id) +{ + MS_TRACE(); + + auto it = DepUsrSCTP::mapIdSctpAssociation.find(id); + + if (it == DepUsrSCTP::mapIdSctpAssociation.end()) + return nullptr; + + return it->second; +} + +/* DepUsrSCTP::Checker instance methods. */ + +DepUsrSCTP::Checker::Checker() +{ + MS_TRACE(); + + this->timer = new QTimer(); + QObject::connect(this->timer, &QTimer::timeout, qApp, [this]() { OnTimer(); }); +} + +DepUsrSCTP::Checker::~Checker() +{ + MS_TRACE(); + + delete this->timer; +} + +void DepUsrSCTP::Checker::Start() +{ + MS_TRACE(); + + MS_DEBUG_TAG(sctp, "usrsctp periodic check started"); + + this->elapsedTimer.invalidate(); + + this->timer->setInterval(CheckerInterval); + this->timer->start(); +} + +void DepUsrSCTP::Checker::Stop() +{ + MS_TRACE(); + + MS_DEBUG_TAG(sctp, "usrsctp periodic check stopped"); + + this->elapsedTimer.invalidate(); + + this->timer->stop(); +} + +void DepUsrSCTP::Checker::OnTimer() +{ + MS_TRACE(); + + qint64 elapsedMs = 0; + if (elapsedTimer.isValid()) { + elapsedMs = elapsedTimer.restart(); + } else { + elapsedTimer.start(); + } + + usrsctp_handle_timers(elapsedMs); +} diff --git a/src/irisnet/noncore/sctp/DepUsrSCTP.hpp b/src/irisnet/noncore/sctp/DepUsrSCTP.hpp new file mode 100644 index 00000000..09432dca --- /dev/null +++ b/src/irisnet/noncore/sctp/DepUsrSCTP.hpp @@ -0,0 +1,48 @@ +#ifndef MS_DEP_USRSCTP_HPP +#define MS_DEP_USRSCTP_HPP + +// #include "common.hpp" +#include "SctpAssociation.hpp" +// #include "handles/Timer.hpp" +#include + +#include +#include +#include + +class DepUsrSCTP { +private: + class Checker { + public: + Checker(); + ~Checker(); + + public: + void Start(); + void Stop(); + + /* Pure virtual methods inherited from Timer::Listener. */ + public: + void OnTimer(); + + private: + QTimer *timer { nullptr }; + QElapsedTimer elapsedTimer; + }; + +public: + static void ClassInit(); + static void ClassDestroy(); + static uintptr_t GetNextSctpAssociationId(); + static void RegisterSctpAssociation(RTC::SctpAssociation *sctpAssociation); + static void DeregisterSctpAssociation(RTC::SctpAssociation *sctpAssociation); + static RTC::SctpAssociation *RetrieveSctpAssociation(uintptr_t id); + +private: + static Checker *checker; + static uint64_t numSctpAssociations; + static uintptr_t nextSctpAssociationId; + static std::unordered_map mapIdSctpAssociation; +}; + +#endif diff --git a/src/irisnet/noncore/sctp/LICENSE b/src/irisnet/noncore/sctp/LICENSE new file mode 100644 index 00000000..0fcef6b3 --- /dev/null +++ b/src/irisnet/noncore/sctp/LICENSE @@ -0,0 +1,15 @@ +ISC License + +Copyright © 2015, Iñaki Baz Castillo + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. diff --git a/src/irisnet/noncore/sctp/Logger.hpp b/src/irisnet/noncore/sctp/Logger.hpp new file mode 100644 index 00000000..14247629 --- /dev/null +++ b/src/irisnet/noncore/sctp/Logger.hpp @@ -0,0 +1,28 @@ +#pragma once + +#include +#include + +// #define PSI_ENABLE_SCTP_DEBUG +#define MS_WARN_TAG(tag, ...) qWarning("sctp:" __VA_ARGS__) +#ifdef PSI_ENABLE_SCTP_DEBUG +#define MS_DEBUG_TAG(tag, ...) qDebug("sctp:" __VA_ARGS__) +#define MS_TRACE() +#define MS_DEBUG_DEV(...) qDebug(__VA_ARGS__) +#else +#define MS_DEBUG_TAG(tag, ...) +#define MS_TRACE() +#define MS_DEBUG_DEV(...) +#endif +#define MS_ERROR(...) qCritical(__VA_ARGS__) +#define MS_HAS_DEBUG_TAG(tag) false + +#define MS_ABORT(desc, ...) \ + do { \ + qFatal("(ABORT) " desc, ##__VA_ARGS__); \ + } while (false) + +#define MS_ASSERT(condition, desc, ...) \ + if (!(condition)) { \ + MS_ABORT("failed assertion `%s': " desc, #condition, ##__VA_ARGS__); \ + } diff --git a/src/irisnet/noncore/sctp/MediaSoupErrors.hpp b/src/irisnet/noncore/sctp/MediaSoupErrors.hpp new file mode 100644 index 00000000..d6de2ed2 --- /dev/null +++ b/src/irisnet/noncore/sctp/MediaSoupErrors.hpp @@ -0,0 +1,64 @@ +#ifndef MS_MEDIASOUP_ERRORS_HPP +#define MS_MEDIASOUP_ERRORS_HPP + +#include "Logger.hpp" +#include // std::snprintf() +#include + +class MediaSoupError : public std::runtime_error { +public: + explicit MediaSoupError(const char *description) : std::runtime_error(description) { } +}; + +class MediaSoupTypeError : public MediaSoupError { +public: + explicit MediaSoupTypeError(const char *description) : MediaSoupError(description) { } +}; + +// clang-format off +#define MS_THROW_ERROR(desc, ...) \ + do \ + { \ + MS_ERROR("throwing MediaSoupError: " desc, ##__VA_ARGS__); \ + \ + static char buffer[2000]; \ + \ + std::snprintf(buffer, 2000, desc, ##__VA_ARGS__); \ + throw MediaSoupError(buffer); \ + } while (false) + +#define MS_THROW_ERROR_STD(desc, ...) \ + do \ + { \ + MS_ERROR_STD("throwing MediaSoupError: " desc, ##__VA_ARGS__); \ + \ + static char buffer[2000]; \ + \ + std::snprintf(buffer, 2000, desc, ##__VA_ARGS__); \ + throw MediaSoupError(buffer); \ + } while (false) + +#define MS_THROW_TYPE_ERROR(desc, ...) \ + do \ + { \ + MS_ERROR("throwing MediaSoupTypeError: " desc, ##__VA_ARGS__); \ + \ + static char buffer[2000]; \ + \ + std::snprintf(buffer, 2000, desc, ##__VA_ARGS__); \ + throw MediaSoupTypeError(buffer); \ + } while (false) + +#define MS_THROW_TYPE_ERROR_STD(desc, ...) \ + do \ + { \ + MS_ERROR_STD("throwing MediaSoupTypeError: " desc, ##__VA_ARGS__); \ + \ + static char buffer[2000]; \ + \ + std::snprintf(buffer, 2000, desc, ##__VA_ARGS__); \ + throw MediaSoupTypeError(buffer); \ + } while (false) +// clang-format on + +#endif diff --git a/src/irisnet/noncore/sctp/README.md b/src/irisnet/noncore/sctp/README.md new file mode 100644 index 00000000..ea1f44b6 --- /dev/null +++ b/src/irisnet/noncore/sctp/README.md @@ -0,0 +1,20 @@ +DataChannel implementation is borrowed from https://github.com/versatica/mediasoup/tree/v3/worker +https://github.com/versatica/mediasoup/commit/d3d369ecf3e255a6da04cc6d35098a3fd6bd6f22 +on Mar 17, 2021 + +Files taken: +* DepUsrSCTP.cpp +* DepUsrSCTP.hpp +* SctpAssociation.cpp +* SctpAssociation.hpp +* MediaSoupErrors.hpp +* Logger.hpp (partially, just to make it compile) + +Remaining files are written from scratch. + +Changes to aforementioned files: +* removed not needed includes +* fixed printf format lines to avoid warnings +* replaced channel notifier with emit, added QObject stuff correspondingly +* preserve errno in SctpAssociation::SendSctpMessage (possible rewrite by logs handling) +* remove filtering out PPID 50 diff --git a/src/irisnet/noncore/sctp/RTC/DataConsumer.hpp b/src/irisnet/noncore/sctp/RTC/DataConsumer.hpp new file mode 100644 index 00000000..1265a413 --- /dev/null +++ b/src/irisnet/noncore/sctp/RTC/DataConsumer.hpp @@ -0,0 +1,14 @@ +#pragma once + +#include "SctpParameters.hpp" + +namespace RTC { + +class DataConsumer { + +public: + SctpParameters sctpParameters; + const SctpParameters &GetSctpStreamParameters() const { return sctpParameters; } +}; + +} diff --git a/src/irisnet/noncore/sctp/RTC/DataProducer.hpp b/src/irisnet/noncore/sctp/RTC/DataProducer.hpp new file mode 100644 index 00000000..af469777 --- /dev/null +++ b/src/irisnet/noncore/sctp/RTC/DataProducer.hpp @@ -0,0 +1,14 @@ +#pragma once + +#include "SctpParameters.hpp" + +namespace RTC { + +class DataProducer { +public: + SctpParameters sctpParameters; + + const SctpParameters &GetSctpStreamParameters() const { return sctpParameters; } +}; + +} diff --git a/src/irisnet/noncore/sctp/RTC/SctpParameters.hpp b/src/irisnet/noncore/sctp/RTC/SctpParameters.hpp new file mode 100644 index 00000000..6a17408c --- /dev/null +++ b/src/irisnet/noncore/sctp/RTC/SctpParameters.hpp @@ -0,0 +1,14 @@ +#pragma once + +#include + +namespace RTC { + +struct SctpParameters { + quint16 streamId = 0; + bool ordered = false; + quint32 maxPacketLifeTime = 0; + quint32 maxRetransmits = 0; +}; + +} diff --git a/src/irisnet/noncore/sctp/SctpAssociation.cpp b/src/irisnet/noncore/sctp/SctpAssociation.cpp new file mode 100644 index 00000000..eade02d3 --- /dev/null +++ b/src/irisnet/noncore/sctp/SctpAssociation.cpp @@ -0,0 +1,883 @@ +#define MS_CLASS "RTC::SctpAssociation" +// #define MS_LOG_DEV_LEVEL 3 + +#include "SctpAssociation.hpp" +#include "DepUsrSCTP.hpp" +#include "Logger.hpp" +#include "MediaSoupErrors.hpp" +// #include "Channel/Notifier.hpp" +#include // std::malloc(), std::free() +#include // std::memset(), std::memcpy() +#include + +// Free send buffer threshold (in bytes) upon which send_cb will be executed. +static uint32_t SendBufferThreshold { 256u }; + +/* SCTP events to which we are subscribing. */ + +/* clang-format off */ +uint16_t EventTypes[] = +{ + SCTP_ADAPTATION_INDICATION, + SCTP_ASSOC_CHANGE, + SCTP_ASSOC_RESET_EVENT, + SCTP_REMOTE_ERROR, + SCTP_SHUTDOWN_EVENT, + SCTP_SEND_FAILED_EVENT, + SCTP_STREAM_RESET_EVENT, + SCTP_STREAM_CHANGE_EVENT +}; +/* clang-format on */ + +/* Static methods for usrsctp callbacks. */ + +inline static int onRecvSctpData(struct socket * /*sock*/, union sctp_sockstore /*addr*/, void *data, size_t len, + struct sctp_rcvinfo rcv, int flags, void *ulpInfo) +{ + auto *sctpAssociation = DepUsrSCTP::RetrieveSctpAssociation(reinterpret_cast(ulpInfo)); + + if (!sctpAssociation) { + MS_WARN_TAG(sctp, "no SctpAssociation found"); + + std::free(data); + + return 0; + } + + if (flags & MSG_NOTIFICATION) { + sctpAssociation->OnUsrSctpReceiveSctpNotification(static_cast(data), len); + } else { + uint16_t streamId = rcv.rcv_sid; + uint32_t ppid = ntohl(rcv.rcv_ppid); + uint16_t ssn = rcv.rcv_ssn; + + MS_DEBUG_TAG(sctp, + "data chunk received [length:%lu, streamId:%" PRIu16 ", SSN:%" PRIu16 ", TSN:%" PRIu32 + ", PPID:%" PRIu32 ", context:%" PRIu32 ", flags:%d]", + len, rcv.rcv_sid, rcv.rcv_ssn, rcv.rcv_tsn, ntohl(rcv.rcv_ppid), rcv.rcv_context, flags); + + sctpAssociation->OnUsrSctpReceiveSctpData(streamId, ssn, ppid, flags, static_cast(data), len); + } + + std::free(data); + + return 1; +} + +inline static int onSendSctpData(struct socket * /*sock*/, uint32_t freeBuffer, void *ulpInfo) +{ + auto *sctpAssociation = DepUsrSCTP::RetrieveSctpAssociation(reinterpret_cast(ulpInfo)); + + if (!sctpAssociation) { + MS_WARN_TAG(sctp, "no SctpAssociation found"); + + return 0; + } + + sctpAssociation->OnUsrSctpSentData(freeBuffer); + + return 1; +} + +namespace RTC { +/* Static. */ + +static constexpr size_t SctpMtu { 1200 }; +static constexpr uint16_t MaxSctpStreams { 65535 }; + +/* Instance methods. */ + +SctpAssociation::SctpAssociation(Listener *listener, uint16_t os, uint16_t mis, size_t maxSctpMessageSize, + size_t sctpSendBufferSize, bool isDataChannel) : + listener(listener), os(os), mis(mis), maxSctpMessageSize(maxSctpMessageSize), + sctpSendBufferSize(sctpSendBufferSize), isDataChannel(isDataChannel) +{ + MS_TRACE(); + + // Get a id for this SctpAssociation. + this->id = DepUsrSCTP::GetNextSctpAssociationId(); + + // Register ourselves in usrsctp. + // NOTE: This must be done before calling usrsctp_bind(). + usrsctp_register_address(reinterpret_cast(this->id)); + + int ret; + + this->socket = usrsctp_socket(AF_CONN, SOCK_STREAM, IPPROTO_SCTP, onRecvSctpData, onSendSctpData, + SendBufferThreshold, reinterpret_cast(this->id)); + + if (!this->socket) { + usrsctp_deregister_address(reinterpret_cast(this->id)); + + MS_THROW_ERROR("usrsctp_socket() failed: %s", std::strerror(errno)); + } + + usrsctp_set_ulpinfo(this->socket, reinterpret_cast(this->id)); + + // Make the socket non-blocking. + ret = usrsctp_set_non_blocking(this->socket, 1); + + if (ret < 0) { + usrsctp_deregister_address(reinterpret_cast(this->id)); + + MS_THROW_ERROR("usrsctp_set_non_blocking() failed: %s", std::strerror(errno)); + } + + // Set SO_LINGER. + // This ensures that the usrsctp close call deletes the association. This + // prevents usrsctp from calling the global send callback with references to + // this class as the address. + struct linger lingerOpt; // NOLINT(cppcoreguidelines-pro-type-member-init) + + lingerOpt.l_onoff = 1; + lingerOpt.l_linger = 0; + + ret = usrsctp_setsockopt(this->socket, SOL_SOCKET, SO_LINGER, &lingerOpt, sizeof(lingerOpt)); + + if (ret < 0) { + usrsctp_deregister_address(reinterpret_cast(this->id)); + + MS_THROW_ERROR("usrsctp_setsockopt(SO_LINGER) failed: %s", std::strerror(errno)); + } + + // Set SCTP_ENABLE_STREAM_RESET. + struct sctp_assoc_value av; // NOLINT(cppcoreguidelines-pro-type-member-init) + + av.assoc_value = SCTP_ENABLE_RESET_STREAM_REQ | SCTP_ENABLE_RESET_ASSOC_REQ | SCTP_ENABLE_CHANGE_ASSOC_REQ; + + ret = usrsctp_setsockopt(this->socket, IPPROTO_SCTP, SCTP_ENABLE_STREAM_RESET, &av, sizeof(av)); + + if (ret < 0) { + usrsctp_deregister_address(reinterpret_cast(this->id)); + + MS_THROW_ERROR("usrsctp_setsockopt(SCTP_ENABLE_STREAM_RESET) failed: %s", std::strerror(errno)); + } + + // Set SCTP_NODELAY. + uint32_t noDelay = 1; + + ret = usrsctp_setsockopt(this->socket, IPPROTO_SCTP, SCTP_NODELAY, &noDelay, sizeof(noDelay)); + + if (ret < 0) { + usrsctp_deregister_address(reinterpret_cast(this->id)); + + MS_THROW_ERROR("usrsctp_setsockopt(SCTP_NODELAY) failed: %s", std::strerror(errno)); + } + + // Enable events. + struct sctp_event event; // NOLINT(cppcoreguidelines-pro-type-member-init) + + std::memset(&event, 0, sizeof(event)); + event.se_on = 1; + + for (size_t i { 0 }; i < sizeof(EventTypes) / sizeof(uint16_t); ++i) { + event.se_type = EventTypes[i]; + + ret = usrsctp_setsockopt(this->socket, IPPROTO_SCTP, SCTP_EVENT, &event, sizeof(event)); + + if (ret < 0) { + usrsctp_deregister_address(reinterpret_cast(this->id)); + + MS_THROW_ERROR("usrsctp_setsockopt(SCTP_EVENT) failed: %s", std::strerror(errno)); + } + } + + // Init message. + struct sctp_initmsg initmsg; // NOLINT(cppcoreguidelines-pro-type-member-init) + + std::memset(&initmsg, 0, sizeof(initmsg)); + initmsg.sinit_num_ostreams = this->os; + initmsg.sinit_max_instreams = this->mis; + + ret = usrsctp_setsockopt(this->socket, IPPROTO_SCTP, SCTP_INITMSG, &initmsg, sizeof(initmsg)); + + if (ret < 0) { + usrsctp_deregister_address(reinterpret_cast(this->id)); + + MS_THROW_ERROR("usrsctp_setsockopt(SCTP_INITMSG) failed: %s", std::strerror(errno)); + } + + // Server side. + struct sockaddr_conn sconn; // NOLINT(cppcoreguidelines-pro-type-member-init) + + std::memset(&sconn, 0, sizeof(sconn)); + sconn.sconn_family = AF_CONN; + sconn.sconn_port = htons(5000); + sconn.sconn_addr = reinterpret_cast(this->id); +#ifdef HAVE_SCONN_LEN + sconn.sconn_len = sizeof(sconn); +#endif + + ret = usrsctp_bind(this->socket, reinterpret_cast(&sconn), sizeof(sconn)); + + if (ret < 0) { + usrsctp_deregister_address(reinterpret_cast(this->id)); + + MS_THROW_ERROR("usrsctp_bind() failed: %s", std::strerror(errno)); + } + + auto bufferSize = static_cast(sctpSendBufferSize); + + if (usrsctp_setsockopt(this->socket, SOL_SOCKET, SO_SNDBUF, &bufferSize, sizeof(int)) < 0) { + usrsctp_deregister_address(reinterpret_cast(this->id)); + + MS_THROW_ERROR("usrsctp_setsockopt(SO_SNDBUF) failed: %s", std::strerror(errno)); + } + + // Register the SctpAssociation into the global map. + DepUsrSCTP::RegisterSctpAssociation(this); +} + +SctpAssociation::~SctpAssociation() +{ + MS_TRACE(); + + usrsctp_set_ulpinfo(this->socket, nullptr); + usrsctp_close(this->socket); + + // Deregister ourselves from usrsctp. + usrsctp_deregister_address(reinterpret_cast(this->id)); + + // Register the SctpAssociation from the global map. + DepUsrSCTP::DeregisterSctpAssociation(this); + + delete[] this->messageBuffer; +} + +void SctpAssociation::TransportConnected() +{ + MS_TRACE(); + + // Just run the SCTP stack if our state is 'new'. + if (this->state != SctpState::NEW) + return; + + try { + int ret; + struct sockaddr_conn rconn; // NOLINT(cppcoreguidelines-pro-type-member-init) + + std::memset(&rconn, 0, sizeof(rconn)); + rconn.sconn_family = AF_CONN; + rconn.sconn_port = htons(5000); + rconn.sconn_addr = reinterpret_cast(this->id); +#ifdef HAVE_SCONN_LEN + rconn.sconn_len = sizeof(rconn); +#endif + + ret = usrsctp_connect(this->socket, reinterpret_cast(&rconn), sizeof(rconn)); + + if (ret < 0 && errno != EINPROGRESS) + MS_THROW_ERROR("usrsctp_connect() failed: %s", std::strerror(errno)); + + // Disable MTU discovery. + sctp_paddrparams peerAddrParams; // NOLINT(cppcoreguidelines-pro-type-member-init) + + std::memset(&peerAddrParams, 0, sizeof(peerAddrParams)); + std::memcpy(&peerAddrParams.spp_address, &rconn, sizeof(rconn)); + peerAddrParams.spp_flags = SPP_PMTUD_DISABLE; + + // The MTU value provided specifies the space available for chunks in the + // packet, so let's subtract the SCTP header size. + peerAddrParams.spp_pathmtu = SctpMtu - sizeof(struct sctp_common_header); + + ret = usrsctp_setsockopt(this->socket, IPPROTO_SCTP, SCTP_PEER_ADDR_PARAMS, &peerAddrParams, + sizeof(peerAddrParams)); + + if (ret < 0) + MS_THROW_ERROR("usrsctp_setsockopt(SCTP_PEER_ADDR_PARAMS) failed: %s", std::strerror(errno)); + + // Announce connecting state. + this->state = SctpState::CONNECTING; + this->listener->OnSctpAssociationConnecting(this); + } catch (const MediaSoupError & /*error*/) { + this->state = SctpState::FAILED; + this->listener->OnSctpAssociationFailed(this); + } +} + +void SctpAssociation::FillJson(QJsonObject &jsonObject) const +{ + MS_TRACE(); + + // Add port (always 5000). + jsonObject[QLatin1String("port")] = 5000; + + // Add OS. + jsonObject[QLatin1String("OS")] = this->os; + + // Add MIS. + jsonObject[QLatin1String("MIS")] = this->mis; + + // Add maxMessageSize. + jsonObject[QLatin1String("maxMessageSize")] = int(this->maxSctpMessageSize); + + // Add sendBufferSize. + jsonObject[QLatin1String("sendBufferSize")] = qint64(this->sctpSendBufferSize); + + // Add sctpBufferedAmountLowThreshold. + jsonObject[QLatin1String("sctpBufferedAmount")] = qint64(this->sctpBufferedAmount); + + // Add isDataChannel. + jsonObject[QLatin1String("isDataChannel")] = this->isDataChannel; +} + +void SctpAssociation::ProcessSctpData(const uint8_t *data, size_t len) +{ + MS_TRACE(); + +#if MS_LOG_DEV_LEVEL == 3 + MS_DUMP_DATA(data, len); +#endif + + usrsctp_conninput(reinterpret_cast(this->id), data, len, 0); +} + +void SctpAssociation::SendSctpMessage(RTC::DataConsumer *dataConsumer, uint32_t ppid, const uint8_t *msg, size_t len, + onQueuedCallback *cb) +{ + MS_TRACE(); + + // This must be controlled by the DataConsumer. + MS_ASSERT(len <= this->maxSctpMessageSize, + "given message exceeds max allowed message size [message size:%lu, max message size:%lu]", len, + this->maxSctpMessageSize); + + const auto ¶meters = dataConsumer->GetSctpStreamParameters(); + + // Fill stcp_sendv_spa. + struct sctp_sendv_spa spa; // NOLINT(cppcoreguidelines-pro-type-member-init) + + std::memset(&spa, 0, sizeof(spa)); + spa.sendv_flags = SCTP_SEND_SNDINFO_VALID; + spa.sendv_sndinfo.snd_sid = parameters.streamId; + spa.sendv_sndinfo.snd_ppid = htonl(ppid); + spa.sendv_sndinfo.snd_flags = SCTP_EOR; + + // If ordered it must be reliable. + if (parameters.ordered) { + spa.sendv_prinfo.pr_policy = SCTP_PR_SCTP_NONE; + spa.sendv_prinfo.pr_value = 0; + } + // Configure reliability: https://tools.ietf.org/html/rfc3758 + else { + spa.sendv_flags |= SCTP_SEND_PRINFO_VALID; + spa.sendv_sndinfo.snd_flags |= SCTP_UNORDERED; + + if (parameters.maxPacketLifeTime != 0) { + spa.sendv_prinfo.pr_policy = SCTP_PR_SCTP_TTL; + spa.sendv_prinfo.pr_value = parameters.maxPacketLifeTime; + } else if (parameters.maxRetransmits != 0) { + spa.sendv_prinfo.pr_policy = SCTP_PR_SCTP_RTX; + spa.sendv_prinfo.pr_value = parameters.maxRetransmits; + } + } + + this->sctpBufferedAmount += len; + + // Notify the listener about the buffered amount increase regardless + // usrsctp_sendv result. + // In case of failure the correct value will be later provided by usrsctp + // via onSendSctpData. + this->listener->OnSctpAssociationBufferedAmount(this, this->sctpBufferedAmount); + + int ret = usrsctp_sendv(this->socket, msg, len, nullptr, 0, &spa, static_cast(sizeof(spa)), + SCTP_SENDV_SPA, 0); + + if (ret < 0) { + auto errno_copy = errno; + MS_WARN_TAG(sctp, "error sending SCTP message [sid:%" PRIu16 ", ppid:%" PRIu32 ", message size:%zu]: %s", + parameters.streamId, ppid, len, std::strerror(errno)); + errno = errno_copy; + if (cb) { + (*cb)(false); + delete cb; + } + } else if (cb) { + (*cb)(true); + delete cb; + } + + this->sendBufferFull = errno == EWOULDBLOCK || errno == EAGAIN; +} + +void SctpAssociation::HandleDataConsumer(RTC::DataConsumer *dataConsumer) +{ + MS_TRACE(); + + auto streamId = dataConsumer->GetSctpStreamParameters().streamId; + + // We need more OS. + if (streamId > this->os - 1) + AddOutgoingStreams(/*force*/ false); +} + +void SctpAssociation::DataProducerClosed(RTC::DataProducer *dataProducer) +{ + MS_TRACE(); + + auto streamId = dataProducer->GetSctpStreamParameters().streamId; + + // Send SCTP_RESET_STREAMS to the remote. + // https://tools.ietf.org/html/draft-ietf-rtcweb-data-channel-13#section-6.7 + if (this->isDataChannel) + ResetSctpStream(streamId, StreamDirection::OUTGOING); + else + ResetSctpStream(streamId, StreamDirection::INCOMING); +} + +void SctpAssociation::DataConsumerClosed(RTC::DataConsumer *dataConsumer) +{ + MS_TRACE(); + + auto streamId = dataConsumer->GetSctpStreamParameters().streamId; + + // Send SCTP_RESET_STREAMS to the remote. + ResetSctpStream(streamId, StreamDirection::OUTGOING); +} + +void SctpAssociation::ResetSctpStream(uint16_t streamId, StreamDirection direction) +{ + MS_TRACE(); + + // Do nothing if an outgoing stream that could not be allocated by us. + if (direction == StreamDirection::OUTGOING && streamId > this->os - 1) + return; + + int ret; + struct sctp_assoc_value av; // NOLINT(cppcoreguidelines-pro-type-member-init) + socklen_t len = sizeof(av); + + ret = usrsctp_getsockopt(this->socket, IPPROTO_SCTP, SCTP_RECONFIG_SUPPORTED, &av, &len); + + if (ret == 0) { + if (av.assoc_value != 1) { + MS_DEBUG_TAG(sctp, "stream reconfiguration not negotiated"); + + return; + } + } else { + MS_WARN_TAG(sctp, "could not retrieve whether stream reconfiguration has been negotiated: %s\n", + std::strerror(errno)); + + return; + } + + // As per spec: https://tools.ietf.org/html/rfc6525#section-4.1 + len = sizeof(sctp_assoc_t) + (2 + 1) * sizeof(uint16_t); + + auto *srs = static_cast(std::malloc(len)); + + switch (direction) { + case StreamDirection::INCOMING: + srs->srs_flags = SCTP_STREAM_RESET_INCOMING; + break; + + case StreamDirection::OUTGOING: + srs->srs_flags = SCTP_STREAM_RESET_OUTGOING; + break; + } + + srs->srs_number_streams = 1; + srs->srs_stream_list[0] = streamId; // No need for htonl(). + + ret = usrsctp_setsockopt(this->socket, IPPROTO_SCTP, SCTP_RESET_STREAMS, srs, len); + + if (ret == 0) { + MS_DEBUG_TAG(sctp, "SCTP_RESET_STREAMS sent [streamId:%" PRIu16 "]", streamId); + } else { + MS_WARN_TAG(sctp, "usrsctp_setsockopt(SCTP_RESET_STREAMS) failed: %s", std::strerror(errno)); + } + + std::free(srs); +} + +void SctpAssociation::AddOutgoingStreams(bool force) +{ + MS_TRACE(); + + uint16_t additionalOs { 0 }; + + if (MaxSctpStreams - this->os >= 32) + additionalOs = 32; + else + additionalOs = MaxSctpStreams - this->os; + + if (additionalOs == 0) { + MS_WARN_TAG(sctp, "cannot add more outgoing streams [OS:%" PRIu16 "]", this->os); + + return; + } + + auto nextDesiredOs = this->os + additionalOs; + + // Already in progress, ignore (unless forced). + if (!force && nextDesiredOs == this->desiredOs) + return; + + // Update desired value. + this->desiredOs = nextDesiredOs; + + // If not connected, defer it. + if (this->state != SctpState::CONNECTED) { + MS_DEBUG_TAG(sctp, "SCTP not connected, deferring OS increase"); + + return; + } + + struct sctp_add_streams sas; // NOLINT(cppcoreguidelines-pro-type-member-init) + + std::memset(&sas, 0, sizeof(sas)); + sas.sas_instrms = 0; + sas.sas_outstrms = additionalOs; + + MS_DEBUG_TAG(sctp, "adding %" PRIu16 " outgoing streams", additionalOs); + + int ret + = usrsctp_setsockopt(this->socket, IPPROTO_SCTP, SCTP_ADD_STREAMS, &sas, static_cast(sizeof(sas))); + + if (ret < 0) + MS_WARN_TAG(sctp, "usrsctp_setsockopt(SCTP_ADD_STREAMS) failed: %s", std::strerror(errno)); +} + +void SctpAssociation::OnUsrSctpSendSctpData(void *buffer, size_t len) +{ + MS_TRACE(); + + const uint8_t *data = static_cast(buffer); + +#if MS_LOG_DEV_LEVEL == 3 + MS_DUMP_DATA(data, len); +#endif + + this->listener->OnSctpAssociationSendData(this, data, len); +} + +void SctpAssociation::OnUsrSctpReceiveSctpData(uint16_t streamId, uint16_t ssn, uint32_t ppid, int flags, + const uint8_t *data, size_t len) +{ + if (this->messageBufferLen != 0 && ssn != this->lastSsnReceived) { + MS_WARN_TAG(sctp, + "message chunk received with different SSN while buffer not empty, buffer discarded [ssn:%" PRIu16 + ", last ssn received:%" PRIu16 "]", + ssn, this->lastSsnReceived); + + this->messageBufferLen = 0; + } + + // Update last SSN received. + this->lastSsnReceived = ssn; + + auto eor = static_cast(flags & MSG_EOR); + + if (this->messageBufferLen + len > this->maxSctpMessageSize) { + MS_WARN_TAG(sctp, + "ongoing received message exceeds max allowed message size [message size:%lu, max message " + "size:%lu, eor:%u]", + this->messageBufferLen + len, this->maxSctpMessageSize, eor ? 1 : 0); + + this->lastSsnReceived = 0; + + return; + } + + // If end of message and there is no buffered data, notify it directly. + if (eor && this->messageBufferLen == 0) { + MS_DEBUG_DEV("directly notifying listener [eor:1, buffer len:0]"); + + this->listener->OnSctpAssociationMessageReceived(this, streamId, ppid, data, len); + } + // If end of message and there is buffered data, append data and notify buffer. + else if (eor && this->messageBufferLen != 0) { + std::memcpy(this->messageBuffer + this->messageBufferLen, data, len); + this->messageBufferLen += len; + + MS_DEBUG_DEV("notifying listener [eor:1, buffer len:%lu]", this->messageBufferLen); + + this->listener->OnSctpAssociationMessageReceived(this, streamId, ppid, this->messageBuffer, + this->messageBufferLen); + + this->messageBufferLen = 0; + } + // If non end of message, append data to the buffer. + else if (!eor) { + // Allocate the buffer if not already done. + if (!this->messageBuffer) + this->messageBuffer = new uint8_t[this->maxSctpMessageSize]; + + std::memcpy(this->messageBuffer + this->messageBufferLen, data, len); + this->messageBufferLen += len; + + MS_DEBUG_DEV("data buffered [eor:0, buffer len:%lu]", this->messageBufferLen); + } +} + +void SctpAssociation::OnUsrSctpReceiveSctpNotification(union sctp_notification *notification, size_t len) +{ + if (notification->sn_header.sn_length != (uint32_t)len) + return; + + switch (notification->sn_header.sn_type) { + case SCTP_ADAPTATION_INDICATION: { + MS_DEBUG_TAG(sctp, "SCTP adaptation indication [%x]", notification->sn_adaptation_event.sai_adaptation_ind); + + break; + } + + case SCTP_ASSOC_CHANGE: { + switch (notification->sn_assoc_change.sac_state) { + case SCTP_COMM_UP: { + MS_DEBUG_TAG(sctp, "SCTP association connected, streams [out:%" PRIu16 ", in:%" PRIu16 "]", + notification->sn_assoc_change.sac_outbound_streams, + notification->sn_assoc_change.sac_inbound_streams); + + // Update our OS. + this->os = notification->sn_assoc_change.sac_outbound_streams; + + // Increase if requested before connected. + if (this->desiredOs > this->os) + AddOutgoingStreams(/*force*/ true); + + if (this->state != SctpState::CONNECTED) { + this->state = SctpState::CONNECTED; + this->listener->OnSctpAssociationConnected(this); + } + + break; + } + + case SCTP_COMM_LOST: { + if (notification->sn_header.sn_length > 0) { + static const size_t BufferSize { 1024 }; + static char buffer[BufferSize]; + + uint32_t len = notification->sn_header.sn_length; + + for (uint32_t i { 0 }; i < len; ++i) { + std::snprintf(buffer, BufferSize, " 0x%02x", notification->sn_assoc_change.sac_info[i]); + } + + MS_DEBUG_TAG(sctp, "SCTP communication lost [info:%s]", buffer); + } else { + MS_DEBUG_TAG(sctp, "SCTP communication lost"); + } + + if (this->state != SctpState::CLOSED) { + this->state = SctpState::CLOSED; + this->listener->OnSctpAssociationClosed(this); + } + + break; + } + + case SCTP_RESTART: { + MS_DEBUG_TAG(sctp, "SCTP remote association restarted, streams [out:%" PRIu16 ", int:%" PRIu16 "]", + notification->sn_assoc_change.sac_outbound_streams, + notification->sn_assoc_change.sac_inbound_streams); + + // Update our OS. + this->os = notification->sn_assoc_change.sac_outbound_streams; + + // Increase if requested before connected. + if (this->desiredOs > this->os) + AddOutgoingStreams(/*force*/ true); + + if (this->state != SctpState::CONNECTED) { + this->state = SctpState::CONNECTED; + this->listener->OnSctpAssociationConnected(this); + } + + break; + } + + case SCTP_SHUTDOWN_COMP: { + MS_DEBUG_TAG(sctp, "SCTP association gracefully closed"); + + if (this->state != SctpState::CLOSED) { + this->state = SctpState::CLOSED; + this->listener->OnSctpAssociationClosed(this); + } + + break; + } + + case SCTP_CANT_STR_ASSOC: { + if (notification->sn_header.sn_length > 0) { + static const size_t BufferSize { 1024 }; + static char buffer[BufferSize]; + + uint32_t len = notification->sn_header.sn_length; + + for (uint32_t i { 0 }; i < len; ++i) { + std::snprintf(buffer, BufferSize, " 0x%02x", notification->sn_assoc_change.sac_info[i]); + } + + MS_WARN_TAG(sctp, "SCTP setup failed: %s", buffer); + } + + if (this->state != SctpState::FAILED) { + this->state = SctpState::FAILED; + this->listener->OnSctpAssociationFailed(this); + } + + break; + } + + default:; + } + + break; + } + + // https://tools.ietf.org/html/rfc6525#section-6.1.2. + case SCTP_ASSOC_RESET_EVENT: { + MS_DEBUG_TAG(sctp, "SCTP association reset event received"); + + break; + } + + // An Operation Error is not considered fatal in and of itself, but may be + // used with an ABORT chunk to report a fatal condition. + case SCTP_REMOTE_ERROR: { + static const size_t BufferSize { 1024 }; + static char buffer[BufferSize]; + + uint32_t len = notification->sn_remote_error.sre_length - sizeof(struct sctp_remote_error); + + for (uint32_t i { 0 }; i < len; i++) { + std::snprintf(buffer, BufferSize, "0x%02x", notification->sn_remote_error.sre_data[i]); + } + + MS_WARN_TAG(sctp, "remote SCTP association error [type:0x%04x, data:%s]", + notification->sn_remote_error.sre_error, buffer); + + break; + } + + // When a peer sends a SHUTDOWN, SCTP delivers this notification to + // inform the application that it should cease sending data. + case SCTP_SHUTDOWN_EVENT: { + MS_DEBUG_TAG(sctp, "remote SCTP association shutdown"); + + if (this->state != SctpState::CLOSED) { + this->state = SctpState::CLOSED; + this->listener->OnSctpAssociationClosed(this); + } + + break; + } + + case SCTP_SEND_FAILED_EVENT: { + static const size_t BufferSize { 1024 }; + static char buffer[BufferSize]; + + uint32_t len = notification->sn_send_failed_event.ssfe_length - sizeof(struct sctp_send_failed_event); + + for (uint32_t i { 0 }; i < len; ++i) { + std::snprintf(buffer, BufferSize, "0x%02x", notification->sn_send_failed_event.ssfe_data[i]); + } + + MS_WARN_TAG( + sctp, "SCTP message sent failure [streamId:%" PRIu16 ", ppid:%" PRIu32 ", sent:%s, error:0x%08x, info:%s]", + notification->sn_send_failed_event.ssfe_info.snd_sid, + ntohl(notification->sn_send_failed_event.ssfe_info.snd_ppid), + (notification->sn_send_failed_event.ssfe_flags & SCTP_DATA_SENT) ? "yes" : "no", + notification->sn_send_failed_event.ssfe_error, buffer); + + break; + } + + case SCTP_STREAM_RESET_EVENT: { + bool incoming { false }; + bool outgoing { false }; + uint16_t numStreams = (notification->sn_strreset_event.strreset_length - sizeof(struct sctp_stream_reset_event)) + / sizeof(uint16_t); + + if (notification->sn_strreset_event.strreset_flags & SCTP_STREAM_RESET_INCOMING_SSN) + incoming = true; + + if (notification->sn_strreset_event.strreset_flags & SCTP_STREAM_RESET_OUTGOING_SSN) + outgoing = true; + + if (MS_HAS_DEBUG_TAG(sctp)) { + std::string streamIds; + + for (uint16_t i { 0 }; i < numStreams; ++i) { + auto streamId = notification->sn_strreset_event.strreset_stream_list[i]; + + // Don't log more than 5 stream ids. + if (i > 4) { + streamIds.append("..."); + + break; + } + + if (i > 0) + streamIds.append(","); + + streamIds.append(std::to_string(streamId)); + } + + MS_DEBUG_TAG(sctp, "SCTP stream reset event [flags:%x, i|o:%s|%s, num streams:%" PRIu16 ", stream ids:%s]", + notification->sn_strreset_event.strreset_flags, incoming ? "true" : "false", + outgoing ? "true" : "false", numStreams, streamIds.c_str()); + } + + // Special case for WebRTC DataChannels in which we must also reset our + // outgoing SCTP stream. + if (incoming && !outgoing && this->isDataChannel) { + for (uint16_t i { 0 }; i < numStreams; ++i) { + auto streamId = notification->sn_strreset_event.strreset_stream_list[i]; + + ResetSctpStream(streamId, StreamDirection::OUTGOING); + this->listener->OnSctpStreamClosed(this, streamId); + } + } + + break; + } + + case SCTP_STREAM_CHANGE_EVENT: { + if (notification->sn_strchange_event.strchange_flags == 0) { + MS_DEBUG_TAG(sctp, "SCTP stream changed, streams [out:%" PRIu16 ", in:%" PRIu16 ", flags:%x]", + notification->sn_strchange_event.strchange_outstrms, + notification->sn_strchange_event.strchange_instrms, + notification->sn_strchange_event.strchange_flags); + } else if (notification->sn_strchange_event.strchange_flags & SCTP_STREAM_RESET_DENIED) { + MS_WARN_TAG(sctp, "SCTP stream change denied, streams [out:%" PRIu16 ", in:%" PRIu16 ", flags:%x]", + notification->sn_strchange_event.strchange_outstrms, + notification->sn_strchange_event.strchange_instrms, + notification->sn_strchange_event.strchange_flags); + + break; + } else if (notification->sn_strchange_event.strchange_flags & SCTP_STREAM_RESET_FAILED) { + MS_WARN_TAG(sctp, "SCTP stream change failed, streams [out:%" PRIu16 ", in:%" PRIu16 ", flags:%x]", + notification->sn_strchange_event.strchange_outstrms, + notification->sn_strchange_event.strchange_instrms, + notification->sn_strchange_event.strchange_flags); + + break; + } + + // Update OS. + this->os = notification->sn_strchange_event.strchange_outstrms; + + break; + } + + default: { + MS_WARN_TAG(sctp, "unhandled SCTP event received [type:%" PRIu16 "]", notification->sn_header.sn_type); + } + } +} + +void SctpAssociation::OnUsrSctpSentData(uint32_t freeBuffer) +{ + auto previousSctpBufferedAmount = this->sctpBufferedAmount; + + this->sctpBufferedAmount = this->sctpSendBufferSize - freeBuffer; + + if (this->sctpBufferedAmount != previousSctpBufferedAmount) { + this->listener->OnSctpAssociationBufferedAmount(this, this->sctpBufferedAmount); + } +} +} // namespace RTC diff --git a/src/irisnet/noncore/sctp/SctpAssociation.hpp b/src/irisnet/noncore/sctp/SctpAssociation.hpp new file mode 100644 index 00000000..1e371f6a --- /dev/null +++ b/src/irisnet/noncore/sctp/SctpAssociation.hpp @@ -0,0 +1,105 @@ +#ifndef MS_RTC_SCTP_ASSOCIATION_HPP +#define MS_RTC_SCTP_ASSOCIATION_HPP + +// #include "common.hpp" +// #include "Utils.hpp" +#include "RTC/DataConsumer.hpp" +#include "RTC/DataProducer.hpp" +#include "usrsctp.h" +#include +#include +#include +#include + +// using json = nlohmann::json; + +namespace RTC { +class SctpAssociation { +public: + enum class SctpState { NEW = 1, CONNECTING, CONNECTED, FAILED, CLOSED }; + +private: + enum class StreamDirection { INCOMING = 1, OUTGOING }; + +protected: + using onQueuedCallback = const std::function; + +public: + class Listener { + public: + virtual void OnSctpAssociationConnecting(RTC::SctpAssociation *sctpAssociation) = 0; + virtual void OnSctpAssociationConnected(RTC::SctpAssociation *sctpAssociation) = 0; + virtual void OnSctpAssociationFailed(RTC::SctpAssociation *sctpAssociation) = 0; + virtual void OnSctpAssociationClosed(RTC::SctpAssociation *sctpAssociation) = 0; + virtual void OnSctpAssociationSendData(RTC::SctpAssociation *sctpAssociation, const uint8_t *data, size_t len) + = 0; + virtual void OnSctpAssociationMessageReceived(RTC::SctpAssociation *sctpAssociation, uint16_t streamId, + uint32_t ppid, const uint8_t *msg, size_t len) + = 0; + virtual void OnSctpAssociationBufferedAmount(RTC::SctpAssociation *sctpAssociation, uint32_t len) = 0; + virtual void OnSctpStreamClosed(RTC::SctpAssociation *sctpAssociation, uint16_t streamId) = 0; + }; + +public: + static bool IsSctp(const uint8_t *data, size_t len) + { + return ((len >= 12) && + // Must have Source Port Number and Destination Port Number set to 5000 (hack). + (qFromBigEndian(data) == 5000) && (qFromBigEndian(data + 2) == 5000)); + } + +public: + SctpAssociation(Listener *listener, uint16_t os, uint16_t mis, size_t maxSctpMessageSize, size_t sctpSendBufferSize, + bool isDataChannel); + ~SctpAssociation(); + +public: + void FillJson(QJsonObject &jsonObject) const; + void TransportConnected(); + SctpState GetState() const { return this->state; } + size_t GetSctpBufferedAmount() const { return this->sctpBufferedAmount; } + void ProcessSctpData(const uint8_t *data, size_t len); + void SendSctpMessage(RTC::DataConsumer *dataConsumer, uint32_t ppid, const uint8_t *msg, size_t len, + onQueuedCallback *cb = nullptr); + void HandleDataConsumer(RTC::DataConsumer *dataConsumer); + void DataProducerClosed(RTC::DataProducer *dataProducer); + void DataConsumerClosed(RTC::DataConsumer *dataConsumer); + bool isSendBufferFull() const { return sendBufferFull; } + +private: + void ResetSctpStream(uint16_t streamId, StreamDirection); + void AddOutgoingStreams(bool force = false); + + /* Callbacks fired by usrsctp events. */ +public: + void OnUsrSctpSendSctpData(void *buffer, size_t len); + void OnUsrSctpReceiveSctpData(uint16_t streamId, uint16_t ssn, uint32_t ppid, int flags, const uint8_t *data, + size_t len); + void OnUsrSctpReceiveSctpNotification(union sctp_notification *notification, size_t len); + void OnUsrSctpSentData(uint32_t freeBuffer); + +public: + uintptr_t id { 0u }; + +private: + // Passed by argument. + Listener *listener { nullptr }; + uint16_t os { 1024u }; + uint16_t mis { 1024u }; + size_t maxSctpMessageSize { 262144u }; + size_t sctpSendBufferSize { 262144u }; + size_t sctpBufferedAmount { 0u }; + bool isDataChannel { false }; + bool sendBufferFull { false }; + // Allocated by this. + uint8_t *messageBuffer { nullptr }; + // Others. + SctpState state { SctpState::NEW }; + struct socket *socket { nullptr }; + uint16_t desiredOs { 0u }; + size_t messageBufferLen { 0u }; + uint16_t lastSsnReceived { 0u }; // Valid for us since no SCTP I-DATA support. +}; +} // namespace RTC + +#endif diff --git a/src/irisnet/noncore/stunallocate.cpp b/src/irisnet/noncore/stunallocate.cpp index ef027a0f..f61f0779 100644 --- a/src/irisnet/noncore/stunallocate.cpp +++ b/src/irisnet/noncore/stunallocate.cpp @@ -18,28 +18,28 @@ #include "stunallocate.h" -#include -#include -#include -#include #include "objectsession.h" -#include "stunutil.h" #include "stunmessage.h" -#include "stuntypes.h" #include "stuntransaction.h" +#include "stuntypes.h" +#include "stunutil.h" + +#include +#include +#include +#include // permissions last 5 minutes, update them every 4 minutes -#define PERM_INTERVAL (4 * 60 * 1000) +#define PERM_INTERVAL (4 * 60 * 1000) // channels last 10 minutes, update them every 9 minutes -#define CHAN_INTERVAL (9 * 60 * 1000) +#define CHAN_INTERVAL (9 * 60 * 1000) namespace XMPP { - void releaseAndDeleteLater(QObject *owner, QObject *obj) { obj->disconnect(owner); - obj->setParent(0); + obj->setParent(nullptr); obj->deleteLater(); } @@ -47,61 +47,47 @@ void releaseAndDeleteLater(QObject *owner, QObject *obj) static int check_channelData(const quint8 *data, int size) { // top two bits are never zero for ChannelData - if((data[0] & 0xc0) == 0) + if ((data[0] & 0xc0) == 0) return -1; - if(size < 4) + if (size < 4) return -1; quint16 len = StunUtil::read16(data + 2); - if(size - 4 < (int)len) + if (size - 4 < (int)len) return -1; // data from a stream must be 4 byte aligned - int plen = len; + int plen = len; int remainder = plen % 4; - if(remainder != 0) + if (remainder != 0) plen += (4 - remainder); int need = plen + 4; - if(size < need) + if (size < need) return -1; return need; } -class StunAllocatePermission : public QObject -{ +class StunAllocatePermission : public QObject { Q_OBJECT public: - QTimer *timer; - StunTransactionPool *pool; - StunTransaction *trans; - QHostAddress stunAddr; - int stunPort; - QHostAddress addr; - bool active; - - enum Error - { - ErrorGeneric, - ErrorProtocol, - ErrorCapacity, - ErrorForbidden, - ErrorRejected, - ErrorTimeout - }; + QTimer *timer; + StunTransactionPool::Ptr pool; + StunTransaction *trans; + TransportAddress stunAddr; + QHostAddress addr; + bool active; + + enum Error { ErrorGeneric, ErrorProtocol, ErrorCapacity, ErrorForbidden, ErrorRejected, ErrorTimeout }; - StunAllocatePermission(StunTransactionPool *_pool, const QHostAddress &_addr) : - QObject(_pool), - pool(_pool), - trans(0), - addr(_addr), - active(false) + StunAllocatePermission(StunTransactionPool::Ptr _pool, const QHostAddress &_addr) : + QObject(_pool.data()), pool(_pool), trans(nullptr), addr(_addr), active(false) { timer = new QTimer(this); - connect(timer, SIGNAL(timeout()), SLOT(timer_timeout())); + connect(timer, &QTimer::timeout, this, &StunAllocatePermission::timer_timeout); timer->setSingleShot(true); timer->setInterval(PERM_INTERVAL); } @@ -113,31 +99,28 @@ class StunAllocatePermission : public QObject releaseAndDeleteLater(this, timer); } - void start(const QHostAddress &_addr, int _port) + void start(const TransportAddress &_addr) { Q_ASSERT(!active); stunAddr = _addr; - stunPort = _port; - doTransaction(); } static StunAllocate::Error errorToStunAllocateError(Error e) { - switch(e) - { - case ErrorProtocol: - return StunAllocate::ErrorProtocol; - case ErrorCapacity: - return StunAllocate::ErrorCapacity; - case ErrorForbidden: - case ErrorRejected: - return StunAllocate::ErrorRejected; - case ErrorTimeout: - return StunAllocate::ErrorTimeout; - default: - return StunAllocate::ErrorGeneric; + switch (e) { + case ErrorProtocol: + return StunAllocate::ErrorProtocol; + case ErrorCapacity: + return StunAllocate::ErrorCapacity; + case ErrorForbidden: + case ErrorRejected: + return StunAllocate::ErrorRejected; + case ErrorTimeout: + return StunAllocate::ErrorTimeout; + default: + return StunAllocate::ErrorGeneric; } } @@ -149,7 +132,7 @@ class StunAllocatePermission : public QObject void cleanup() { delete trans; - trans = 0; + trans = nullptr; timer->stop(); @@ -160,16 +143,13 @@ class StunAllocatePermission : public QObject { Q_ASSERT(!trans); trans = new StunTransaction(this); - connect(trans, SIGNAL(createMessage(QByteArray)), SLOT(trans_createMessage(QByteArray))); - connect(trans, SIGNAL(finished(XMPP::StunMessage)), SLOT(trans_finished(XMPP::StunMessage))); - connect(trans, SIGNAL(error(XMPP::StunTransaction::Error)), SLOT(trans_error(XMPP::StunTransaction::Error))); - trans->start(pool, stunAddr, stunPort); + connect(trans, &StunTransaction::createMessage, this, &StunAllocatePermission::trans_createMessage); + connect(trans, &StunTransaction::finished, this, &StunAllocatePermission::trans_finished); + connect(trans, &StunTransaction::error, this, &StunAllocatePermission::trans_error); + trans->start(pool.data(), stunAddr); } - void restartTimer() - { - timer->start(); - } + void restartTimer() { timer->start(); } private slots: void trans_createMessage(const QByteArray &transactionId) @@ -187,8 +167,8 @@ private slots: { StunMessage::Attribute a; - a.type = StunTypes::XOR_PEER_ADDRESS; - a.value = StunTypes::createXorPeerAddress(addr, 0, message.magic(), message.id()); + a.type = StunTypes::XOR_PEER_ADDRESS; + a.value = StunTypes::createXorPeerAddress(TransportAddress { addr, 0 }, message.magic(), message.id()); list += a; } @@ -200,15 +180,13 @@ private slots: void trans_finished(const XMPP::StunMessage &response) { delete trans; - trans = 0; + trans = nullptr; - bool err = false; - int code; + bool err = false; + int code; QString reason; - if(response.mclass() == StunMessage::ErrorResponse) - { - if(!StunTypes::parseErrorCode(response.attribute(StunTypes::ERROR_CODE), &code, &reason)) - { + if (response.mclass() == StunMessage::ErrorResponse) { + if (!StunTypes::parseErrorCode(response.attribute(StunTypes::ERROR_CODE), &code, &reason)) { cleanup(); emit error(ErrorProtocol, "Unable to parse ERROR-CODE in error response."); return; @@ -217,13 +195,12 @@ private slots: err = true; } - if(err) - { + if (err) { cleanup(); - if(code == StunTypes::InsufficientCapacity) + if (code == StunTypes::InsufficientCapacity) emit error(ErrorCapacity, reason); - else if(code == StunTypes::Forbidden) + else if (code == StunTypes::Forbidden) emit error(ErrorForbidden, reason); else emit error(ErrorRejected, reason); @@ -233,8 +210,7 @@ private slots: restartTimer(); - if(!active) - { + if (!active) { active = true; emit ready(); } @@ -244,54 +220,34 @@ private slots: { cleanup(); - if(e == XMPP::StunTransaction::ErrorTimeout) + if (e == XMPP::StunTransaction::ErrorTimeout) emit error(ErrorTimeout, "Request timed out."); else emit error(ErrorGeneric, "Generic transaction error."); } - void timer_timeout() - { - doTransaction(); - } + void timer_timeout() { doTransaction(); } }; -class StunAllocateChannel : public QObject -{ +class StunAllocateChannel : public QObject { Q_OBJECT public: - QTimer *timer; - StunTransactionPool *pool; - StunTransaction *trans; - QHostAddress stunAddr; - int stunPort; - int channelId; - QHostAddress addr; - int port; - bool active; - - enum Error - { - ErrorGeneric, - ErrorProtocol, - ErrorCapacity, - ErrorForbidden, - ErrorRejected, - ErrorTimeout - }; - - StunAllocateChannel(StunTransactionPool *_pool, int _channelId, const QHostAddress &_addr, int _port) : - QObject(_pool), - pool(_pool), - trans(0), - channelId(_channelId), - addr(_addr), - port(_port), - active(false) + QTimer *timer; + StunTransactionPool::Ptr pool; + StunTransaction *trans = nullptr; + TransportAddress stunAddr; + int channelId; + TransportAddress addr; + bool active = false; + + enum Error { ErrorGeneric, ErrorProtocol, ErrorCapacity, ErrorForbidden, ErrorRejected, ErrorTimeout }; + + StunAllocateChannel(StunTransactionPool::Ptr _pool, int _channelId, const TransportAddress &_addr) : + QObject(_pool.data()), pool(_pool), trans(nullptr), channelId(_channelId), addr(_addr), active(false) { timer = new QTimer(this); - connect(timer, SIGNAL(timeout()), SLOT(timer_timeout())); + connect(timer, &QTimer::timeout, this, &StunAllocateChannel::timer_timeout); timer->setSingleShot(true); timer->setInterval(CHAN_INTERVAL); } @@ -303,31 +259,28 @@ class StunAllocateChannel : public QObject releaseAndDeleteLater(this, timer); } - void start(const QHostAddress &_addr, int _port) + void start(const TransportAddress &_addr) { Q_ASSERT(!active); stunAddr = _addr; - stunPort = _port; - doTransaction(); } static StunAllocate::Error errorToStunAllocateError(Error e) { - switch(e) - { - case ErrorProtocol: - return StunAllocate::ErrorProtocol; - case ErrorCapacity: - return StunAllocate::ErrorCapacity; - case ErrorForbidden: - case ErrorRejected: - return StunAllocate::ErrorRejected; - case ErrorTimeout: - return StunAllocate::ErrorTimeout; - default: - return StunAllocate::ErrorGeneric; + switch (e) { + case ErrorProtocol: + return StunAllocate::ErrorProtocol; + case ErrorCapacity: + return StunAllocate::ErrorCapacity; + case ErrorForbidden: + case ErrorRejected: + return StunAllocate::ErrorRejected; + case ErrorTimeout: + return StunAllocate::ErrorTimeout; + default: + return StunAllocate::ErrorGeneric; } } @@ -339,28 +292,25 @@ class StunAllocateChannel : public QObject void cleanup() { delete trans; - trans = 0; + trans = nullptr; timer->stop(); channelId = -1; - active = false; + active = false; } void doTransaction() { Q_ASSERT(!trans); trans = new StunTransaction(this); - connect(trans, SIGNAL(createMessage(QByteArray)), SLOT(trans_createMessage(QByteArray))); - connect(trans, SIGNAL(finished(XMPP::StunMessage)), SLOT(trans_finished(XMPP::StunMessage))); - connect(trans, SIGNAL(error(XMPP::StunTransaction::Error)), SLOT(trans_error(XMPP::StunTransaction::Error))); - trans->start(pool, stunAddr, stunPort); + connect(trans, &StunTransaction::createMessage, this, &StunAllocateChannel::trans_createMessage); + connect(trans, &StunTransaction::finished, this, &StunAllocateChannel::trans_finished); + connect(trans, &StunTransaction::error, this, &StunAllocateChannel::trans_error); + trans->start(pool.data(), stunAddr); } - void restartTimer() - { - timer->start(); - } + void restartTimer() { timer->start(); } private slots: void trans_createMessage(const QByteArray &transactionId) @@ -374,15 +324,15 @@ private slots: { StunMessage::Attribute a; - a.type = StunTypes::CHANNEL_NUMBER; - a.value = StunTypes::createChannelNumber(channelId); + a.type = StunTypes::CHANNEL_NUMBER; + a.value = StunTypes::createChannelNumber(quint16(channelId)); list += a; } { StunMessage::Attribute a; - a.type = StunTypes::XOR_PEER_ADDRESS; - a.value = StunTypes::createXorPeerAddress(addr, port, message.magic(), message.id()); + a.type = StunTypes::XOR_PEER_ADDRESS; + a.value = StunTypes::createXorPeerAddress(addr, message.magic(), message.id()); list += a; } @@ -394,15 +344,13 @@ private slots: void trans_finished(const XMPP::StunMessage &response) { delete trans; - trans = 0; + trans = nullptr; - bool err = false; - int code; + bool err = false; + int code; QString reason; - if(response.mclass() == StunMessage::ErrorResponse) - { - if(!StunTypes::parseErrorCode(response.attribute(StunTypes::ERROR_CODE), &code, &reason)) - { + if (response.mclass() == StunMessage::ErrorResponse) { + if (!StunTypes::parseErrorCode(response.attribute(StunTypes::ERROR_CODE), &code, &reason)) { cleanup(); emit error(ErrorProtocol, "Unable to parse ERROR-CODE in error response."); return; @@ -411,13 +359,12 @@ private slots: err = true; } - if(err) - { + if (err) { cleanup(); - if(code == StunTypes::InsufficientCapacity) + if (code == StunTypes::InsufficientCapacity) emit error(ErrorCapacity, reason); - else if(code == StunTypes::Forbidden) + else if (code == StunTypes::Forbidden) emit error(ErrorForbidden, reason); else emit error(ErrorRejected, reason); @@ -427,8 +374,7 @@ private slots: restartTimer(); - if(!active) - { + if (!active) { active = true; emit ready(); } @@ -438,32 +384,22 @@ private slots: { cleanup(); - if(e == XMPP::StunTransaction::ErrorTimeout) + if (e == XMPP::StunTransaction::ErrorTimeout) emit error(ErrorTimeout, "Request timed out."); else emit error(ErrorGeneric, "Generic transaction error."); } - void timer_timeout() - { - doTransaction(); - } + void timer_timeout() { doTransaction(); } }; -class StunAllocate::Private : public QObject -{ +class StunAllocate::Private : public QObject { Q_OBJECT public: - enum DontFragmentState - { - DF_Unknown, - DF_Supported, - DF_Unsupported - }; + enum DontFragmentState { DF_Unknown, DF_Supported, DF_Unsupported }; - enum State - { + enum State { Stopped, Starting, Started, @@ -472,40 +408,33 @@ class StunAllocate::Private : public QObject Erroring // like stopping, but emits error when finished }; - StunAllocate *q; - ObjectSession sess; - StunTransactionPool *pool; - StunTransaction *trans; - QHostAddress stunAddr; - int stunPort; - State state; - QString errorString; - DontFragmentState dfState; - QString clientSoftware, serverSoftware; - QHostAddress reflexiveAddress, relayedAddress; - int reflexivePort, relayedPort; - StunMessage msg; - int allocateLifetime; - QTimer *allocateRefreshTimer; - QList perms; - QList channels; - QList permsOut; - QList channelsOut; - int erroringCode; - QString erroringString; + StunAllocate *q; + ObjectSession sess; + StunTransactionPool::Ptr pool; + StunTransaction *trans; + TransportAddress stunAddr; + State state; + QString errorString; + DontFragmentState dfState; + QString clientSoftware, serverSoftware; + TransportAddress reflexiveAddress, relayedAddress; + StunMessage msg; + int allocateLifetime; + QTimer *allocateRefreshTimer; + QList perms; + QList channels; + QList permQueue; + QList permsOut; + QList channelsOut; + int erroringCode; + QString erroringString; Private(StunAllocate *_q) : - QObject(_q), - q(_q), - sess(this), - pool(0), - trans(0), - state(Stopped), - dfState(DF_Unknown), + QObject(_q), q(_q), sess(this), pool(nullptr), trans(nullptr), state(Stopped), dfState(DF_Unknown), erroringCode(-1) { allocateRefreshTimer = new QTimer(this); - connect(allocateRefreshTimer, SIGNAL(timeout()), SLOT(refresh())); + connect(allocateRefreshTimer, &QTimer::timeout, this, &Private::refresh); allocateRefreshTimer->setSingleShot(true); } @@ -516,12 +445,11 @@ class StunAllocate::Private : public QObject releaseAndDeleteLater(this, allocateRefreshTimer); } - void start(const QHostAddress &_addr = QHostAddress(), int _port = -1) + void start(const TransportAddress &_addr = TransportAddress()) { Q_ASSERT(state == Stopped); stunAddr = _addr; - stunPort = _port; state = Starting; doTransaction(); @@ -530,7 +458,7 @@ class StunAllocate::Private : public QObject void stop() { // erroring already? no need to do anything - if(state == Erroring) + if (state == Erroring) return; Q_ASSERT(state == Started); @@ -545,42 +473,41 @@ class StunAllocate::Private : public QObject Q_ASSERT(state == Started); cleanupTasks(); - erroringCode = code; + erroringCode = code; erroringString = str; - state = Erroring; + state = Erroring; doTransaction(); } void setPermissions(const QList &newPerms) { // if currently erroring out, skip - if(state == Erroring) + if (state == Erroring) return; + if (state == Starting) { + permQueue += newPerms; + return; + } + Q_ASSERT(state == Started); int freeCount = 0; // removed? - for(int n = 0; n < perms.count(); ++n) - { + for (int n = 0; n < perms.count(); ++n) { bool found = false; - for(int k = 0; k < newPerms.count(); ++k) - { - if(newPerms[k] == perms[n]->addr) - { + for (int k = 0; k < newPerms.count(); ++k) { + if (newPerms[k] == perms[n]->addr) { found = true; break; } } - if(!found) - { + if (!found) { // delete related channels - for(int j = 0; j < channels.count(); ++j) - { - if(channels[j]->addr == perms[n]->addr) - { + for (int j = 0; j < channels.count(); ++j) { + if (channels[j]->addr.addr == perms[n]->addr) { delete channels[j]; channels.removeAt(j); --j; // adjust position @@ -595,39 +522,33 @@ class StunAllocate::Private : public QObject } } - if(freeCount > 0) - { + if (freeCount > 0) { // removals count as a change, so emit the signal sess.deferExclusive(q, "permissionsChanged"); // wake up inactive perms now that we've freed space - for(int n = 0; n < perms.count(); ++n) - { - if(!perms[n]->active) - perms[n]->start(stunAddr, stunPort); + for (int n = 0; n < perms.count(); ++n) { + if (!perms[n]->active) + perms[n]->start(stunAddr); } } // added? - for(int n = 0; n < newPerms.count(); ++n) - { + for (int n = 0; n < newPerms.count(); ++n) { bool found = false; - for(int k = 0; k < perms.count(); ++k) - { - if(perms[k]->addr == newPerms[n]) - { + for (int k = 0; k < perms.count(); ++k) { + if (perms[k]->addr == newPerms[n]) { found = true; break; } } - if(!found) - { + if (!found) { StunAllocatePermission *perm = new StunAllocatePermission(pool, newPerms[n]); - connect(perm, SIGNAL(ready()), SLOT(perm_ready())); - connect(perm, SIGNAL(error(XMPP::StunAllocatePermission::Error,QString)), SLOT(perm_error(XMPP::StunAllocatePermission::Error,QString))); + connect(perm, &StunAllocatePermission::ready, this, &Private::perm_ready); + connect(perm, &StunAllocatePermission::error, this, &Private::perm_error); perms += perm; - perm->start(stunAddr, stunPort); + perm->start(stunAddr); } } } @@ -635,7 +556,7 @@ class StunAllocate::Private : public QObject void setChannels(const QList &newChannels) { // if currently erroring out, skip - if(state == Erroring) + if (state == Erroring) return; Q_ASSERT(state == Started); @@ -643,20 +564,16 @@ class StunAllocate::Private : public QObject int freeCount = 0; // removed? - for(int n = 0; n < channels.count(); ++n) - { + for (int n = 0; n < channels.count(); ++n) { bool found = false; - for(int k = 0; k < newChannels.count(); ++k) - { - if(newChannels[k].address == channels[n]->addr && newChannels[k].port == channels[n]->port) - { + for (int k = 0; k < newChannels.count(); ++k) { + if (newChannels[k].address == channels[n]->addr) { found = true; break; } } - if(!found) - { + if (!found) { ++freeCount; delete channels[n]; @@ -665,66 +582,56 @@ class StunAllocate::Private : public QObject } } - if(freeCount > 0) - { + if (freeCount > 0) { // removals count as a change, so emit the signal sess.deferExclusive(q, "channelsChanged"); // wake up inactive channels now that we've freed space - for(int n = 0; n < channels.count(); ++n) - { - if(!channels[n]->active) - { + for (int n = 0; n < channels.count(); ++n) { + if (!channels[n]->active) { int channelId = getFreeChannelNumber(); // out of channels? give up - if(channelId == -1) + if (channelId == -1) break; channels[n]->channelId = channelId; - channels[n]->start(stunAddr, stunPort); + channels[n]->start(stunAddr); } } } // added? - for(int n = 0; n < newChannels.count(); ++n) - { + for (int n = 0; n < newChannels.count(); ++n) { bool found = false; - for(int k = 0; k < channels.count(); ++k) - { - if(channels[k]->addr == newChannels[n].address && channels[k]->port == newChannels[n].port) - { + for (int k = 0; k < channels.count(); ++k) { + if (channels[k]->addr == newChannels[n].address) { found = true; break; } } - if(!found) - { + if (!found) { // look up the permission for this channel int at = -1; - for(int k = 0; k < perms.count(); ++k) - { - if(perms[k]->addr == newChannels[n].address) - { + for (int k = 0; k < perms.count(); ++k) { + if (perms[k]->addr == newChannels[n].address.addr) { at = k; break; } } // only install a channel if we have a permission - if(at != -1) - { + if (at != -1) { int channelId = getFreeChannelNumber(); - StunAllocateChannel *channel = new StunAllocateChannel(pool, channelId, newChannels[n].address, newChannels[n].port); - connect(channel, SIGNAL(ready()), SLOT(channel_ready())); - connect(channel, SIGNAL(error(XMPP::StunAllocateChannel::Error,QString)), SLOT(channel_error(XMPP::StunAllocateChannel::Error,QString))); + StunAllocateChannel *channel = new StunAllocateChannel(pool, channelId, newChannels[n].address); + connect(channel, &StunAllocateChannel::ready, this, &Private::channel_ready); + connect(channel, &StunAllocateChannel::error, this, &Private::channel_error); channels += channel; - if(channelId != -1) - channel->start(stunAddr, stunPort); + if (channelId != -1) + channel->start(stunAddr); } } } @@ -732,30 +639,26 @@ class StunAllocate::Private : public QObject int getFreeChannelNumber() { - for(int tryId = 0x4000; tryId <= 0x7fff; ++tryId) - { + for (int tryId = 0x4000; tryId <= 0x7fff; ++tryId) { bool found = false; - for(int n = 0; n < channels.count(); ++n) - { - if(channels[n]->channelId == tryId) - { + for (int n = 0; n < channels.count(); ++n) { + if (channels[n]->channelId == tryId) { found = true; break; } } - if(!found) + if (!found) return tryId; } return -1; } - int getChannel(const QHostAddress &addr, int port) + int getChannel(const TransportAddress &addr) { - for(int n = 0; n < channels.count(); ++n) - { - if(channels[n]->active && channels[n]->addr == addr && channels[n]->port == port) + for (int n = 0; n < channels.count(); ++n) { + if (channels[n]->active && channels[n]->addr == addr) return channels[n]->channelId; } @@ -765,14 +668,11 @@ class StunAllocate::Private : public QObject // note that this function works even for inactive channels, so that // incoming traffic that is received out-of-order with a // ChannelBind success response is still processable - bool getAddressPort(int channelId, QHostAddress *addr, int *port) + bool getAddressPort(int channelId, TransportAddress &addr) { - for(int n = 0; n < channels.count(); ++n) - { - if(channels[n]->channelId == channelId) - { - *addr = channels[n]->addr; - *port = channels[n]->port; + for (int n = 0; n < channels.count(); ++n) { + if (channels[n]->channelId == channelId) { + addr = channels[n]->addr; return true; } } @@ -797,7 +697,7 @@ class StunAllocate::Private : public QObject void cleanupTasks() { delete trans; - trans = 0; + trans = nullptr; allocateRefreshTimer->stop(); @@ -814,10 +714,10 @@ class StunAllocate::Private : public QObject { Q_ASSERT(!trans); trans = new StunTransaction(this); - connect(trans, SIGNAL(createMessage(QByteArray)), SLOT(trans_createMessage(QByteArray))); - connect(trans, SIGNAL(finished(XMPP::StunMessage)), SLOT(trans_finished(XMPP::StunMessage))); - connect(trans, SIGNAL(error(XMPP::StunTransaction::Error)), SLOT(trans_error(XMPP::StunTransaction::Error))); - trans->start(pool, stunAddr, stunPort); + connect(trans, &StunTransaction::createMessage, this, &Private::trans_createMessage); + connect(trans, &StunTransaction::finished, this, &Private::trans_finished); + connect(trans, &StunTransaction::error, this, &Private::trans_error); + trans->start(pool.data(), stunAddr); } void restartRefreshTimer() @@ -830,13 +730,12 @@ class StunAllocate::Private : public QObject { QList newList; - for(int n = 0; n < perms.count(); ++n) - { - if(perms[n]->active) + for (int n = 0; n < perms.count(); ++n) { + if (perms[n]->active) newList += perms[n]->addr; } - if(newList == permsOut) + if (newList == permsOut) return false; permsOut = newList; @@ -847,13 +746,12 @@ class StunAllocate::Private : public QObject { QList newList; - for(int n = 0; n < channels.count(); ++n) - { - if(channels[n]->active) - newList += StunAllocate::Channel(channels[n]->addr, channels[n]->port); + for (int n = 0; n < channels.count(); ++n) { + if (channels[n]->active) + newList += StunAllocate::Channel(channels[n]->addr); } - if(newList == channelsOut) + if (std::as_const(newList) == std::as_const(channelsOut)) return false; channelsOut = newList; @@ -871,8 +769,7 @@ private slots: void trans_createMessage(const QByteArray &transactionId) { - if(state == Starting) - { + if (state == Starting) { // send Allocate request StunMessage message; message.setMethod(StunTypes::Allocate); @@ -880,30 +777,28 @@ private slots: QList list; - if(!clientSoftware.isEmpty()) - { + if (!clientSoftware.isEmpty()) { StunMessage::Attribute a; - a.type = StunTypes::SOFTWARE; + a.type = StunTypes::SOFTWARE; a.value = StunTypes::createSoftware(clientSoftware); list += a; } { StunMessage::Attribute a; - a.type = StunTypes::LIFETIME; + a.type = StunTypes::LIFETIME; a.value = StunTypes::createLifetime(3600); list += a; } { StunMessage::Attribute a; - a.type = StunTypes::REQUESTED_TRANSPORT; + a.type = StunTypes::REQUESTED_TRANSPORT; a.value = StunTypes::createRequestedTransport(17); // 17=UDP list += a; } - if(dfState == DF_Unknown) - { + if (dfState == DF_Unknown) { StunMessage::Attribute a; a.type = StunTypes::DONT_FRAGMENT; list += a; @@ -912,9 +807,7 @@ private slots: message.setAttributes(list); trans->setMessage(message); - } - else if(state == Stopping || state == Erroring) - { + } else if (state == Stopping || state == Erroring) { StunMessage message; message.setMethod(StunTypes::Refresh); message.setId((const quint8 *)transactionId.data()); @@ -923,7 +816,7 @@ private slots: { StunMessage::Attribute a; - a.type = StunTypes::LIFETIME; + a.type = StunTypes::LIFETIME; a.value = StunTypes::createLifetime(0); list += a; } @@ -931,9 +824,7 @@ private slots: message.setAttributes(list); trans->setMessage(message); - } - else if(state == Refreshing) - { + } else if (state == Refreshing) { StunMessage message; message.setMethod(StunTypes::Refresh); message.setId((const quint8 *)transactionId.data()); @@ -942,7 +833,7 @@ private slots: { StunMessage::Attribute a; - a.type = StunTypes::LIFETIME; + a.type = StunTypes::LIFETIME; a.value = StunTypes::createLifetime(3600); list += a; } @@ -956,15 +847,13 @@ private slots: void trans_finished(const XMPP::StunMessage &response) { delete trans; - trans = 0; + trans = nullptr; - bool error = false; - int code; + bool error = false; + int code; QString reason; - if(response.mclass() == StunMessage::ErrorResponse) - { - if(!StunTypes::parseErrorCode(response.attribute(StunTypes::ERROR_CODE), &code, &reason)) - { + if (response.mclass() == StunMessage::ErrorResponse) { + if (!StunTypes::parseErrorCode(response.attribute(StunTypes::ERROR_CODE), &code, &reason)) { cleanup(); errorString = "Unable to parse ERROR-CODE in error response."; emit q->error(StunAllocate::ErrorProtocol); @@ -974,60 +863,46 @@ private slots: error = true; } - if(state == Starting) - { - if(error) - { - if(code == StunTypes::UnknownAttribute) - { + if (state == Starting) { + if (error) { + if (code == StunTypes::UnknownAttribute) { QList typeList; - if(!StunTypes::parseUnknownAttributes(response.attribute(StunTypes::UNKNOWN_ATTRIBUTES), &typeList)) - { + if (!StunTypes::parseUnknownAttributes(response.attribute(StunTypes::UNKNOWN_ATTRIBUTES), + &typeList)) { cleanup(); errorString = "Unable to parse UNKNOWN-ATTRIBUTES in 420 (Unknown Attribute) error response."; emit q->error(StunAllocate::ErrorProtocol); return; } - if(typeList.contains(StunTypes::DONT_FRAGMENT)) - { + if (typeList.contains(StunTypes::DONT_FRAGMENT)) { dfState = DF_Unsupported; // stay in same state, try again doTransaction(); - } - else - { + } else { cleanup(); errorString = reason; emit q->error(StunAllocate::ErrorGeneric); } return; - } - else if(code == StunTypes::AllocationMismatch) - { + } else if (code == StunTypes::AllocationMismatch) { cleanup(); errorString = "437 (Allocation Mismatch)."; emit q->error(StunAllocate::ErrorMismatch); return; - } - else if(code == StunTypes::InsufficientCapacity) - { + } else if (code == StunTypes::InsufficientCapacity) { cleanup(); errorString = reason; emit q->error(StunAllocate::ErrorCapacity); return; - } - else if(code == StunTypes::Unauthorized) - { + } else if (code == StunTypes::Unauthorized) { cleanup(); errorString = "Unauthorized"; emit q->error(StunAllocate::ErrorAuth); return; - } - else - { + } else { cleanup(); errorString = reason; emit q->error(StunAllocate::ErrorGeneric); @@ -1036,69 +911,60 @@ private slots: } quint32 lifetime; - if(!StunTypes::parseLifetime(response.attribute(StunTypes::LIFETIME), &lifetime)) - { + if (!StunTypes::parseLifetime(response.attribute(StunTypes::LIFETIME), &lifetime)) { cleanup(); errorString = "Unable to parse LIFETIME."; emit q->error(StunAllocate::ErrorProtocol); return; } - QHostAddress raddr; - quint16 rport; - if(!StunTypes::parseXorRelayedAddress(response.attribute(StunTypes::XOR_RELAYED_ADDRESS), response.magic(), response.id(), &raddr, &rport)) - { + TransportAddress raddr; + if (!StunTypes::parseXorRelayedAddress(response.attribute(StunTypes::XOR_RELAYED_ADDRESS), response.magic(), + response.id(), raddr)) { cleanup(); errorString = "Unable to parse XOR-RELAYED-ADDRESS."; emit q->error(StunAllocate::ErrorProtocol); return; } - QHostAddress saddr; - quint16 sport; - if(!StunTypes::parseXorMappedAddress(response.attribute(StunTypes::XOR_MAPPED_ADDRESS), response.magic(), response.id(), &saddr, &sport)) - { + TransportAddress saddr; + if (!StunTypes::parseXorMappedAddress(response.attribute(StunTypes::XOR_MAPPED_ADDRESS), response.magic(), + response.id(), saddr)) { cleanup(); errorString = "Unable to parse XOR-MAPPED-ADDRESS."; emit q->error(StunAllocate::ErrorProtocol); return; } - if(lifetime < 120) - { + if (lifetime < 120) { state = Started; // stopWithError requires this - stopWithError(StunAllocate::ErrorProtocol, - "LIFETIME is less than two minutes. That is ridiculous."); + stopWithError(StunAllocate::ErrorProtocol, "LIFETIME is less than two minutes. That is ridiculous."); return; } QString str; - if(StunTypes::parseSoftware(response.attribute(StunTypes::SOFTWARE), &str)) - { + if (StunTypes::parseSoftware(response.attribute(StunTypes::SOFTWARE), &str)) { serverSoftware = str; } allocateLifetime = lifetime; - relayedAddress = raddr; - relayedPort = rport; + relayedAddress = raddr; reflexiveAddress = saddr; - reflexivePort = sport; - if(dfState == DF_Unknown) + if (dfState == DF_Unknown) dfState = DF_Supported; state = Started; restartRefreshTimer(); + setPermissions(permQueue); + permQueue.clear(); + emit q->started(); - } - else if(state == Stopping || state == Erroring) - { - if(error) - { + } else if (state == Stopping || state == Erroring) { + if (error) { // AllocationMismatch on session cancel doesn't count as an error - if(code != StunTypes::AllocationMismatch) - { + if (code != StunTypes::AllocationMismatch) { cleanup(); errorString = reason; emit q->error(StunAllocate::ErrorGeneric); @@ -1106,27 +972,22 @@ private slots: } } - if(state == Stopping) - { + if (state == Stopping) { // cleanup will set the state to Stopped cleanup(); emit q->stopped(); - } - else // Erroring + } else // Erroring { - int code = erroringCode; - QString str = erroringString; + int code = erroringCode; + QString str = erroringString; // cleanup will set the state to Stopped cleanup(); errorString = str; emit q->error((StunAllocate::Error)code); } - } - else if(state == Refreshing) - { - if(error) - { + } else if (state == Refreshing) { + if (error) { cleanup(); errorString = reason; emit q->error(StunAllocate::ErrorRejected); @@ -1134,8 +995,7 @@ private slots: } quint32 lifetime; - if(!StunTypes::parseLifetime(response.attribute(StunTypes::LIFETIME), &lifetime)) - { + if (!StunTypes::parseLifetime(response.attribute(StunTypes::LIFETIME), &lifetime)) { cleanup(); errorString = "Unable to parse LIFETIME."; emit q->error(StunAllocate::ErrorProtocol); @@ -1151,25 +1011,22 @@ private slots: void perm_ready() { - if(updatePermsOut()) + if (updatePermsOut()) emit q->permissionsChanged(); } void perm_error(XMPP::StunAllocatePermission::Error e, const QString &reason) { - if(e == StunAllocatePermission::ErrorCapacity) - { + if (e == StunAllocatePermission::ErrorCapacity) { // if we aren't allowed to make anymore permissions, // don't consider this an error. the perm stays // in the list inactive. we'll try it again if // any perms get removed. return; - } - else if(e == StunAllocatePermission::ErrorForbidden) - { + } else if (e == StunAllocatePermission::ErrorForbidden) { // silently discard the permission request StunAllocatePermission *perm = static_cast(sender()); - QHostAddress addr = perm->addr; + QHostAddress addr = perm->addr; delete perm; perms.removeAll(perm); emit q->debugLine(QString("Warning: permission forbidden to %1").arg(addr.toString())); @@ -1183,14 +1040,13 @@ private slots: void channel_ready() { - if(updateChannelsOut()) + if (updateChannelsOut()) emit q->channelsChanged(); } void channel_error(XMPP::StunAllocateChannel::Error e, const QString &reason) { - if(e == StunAllocateChannel::ErrorCapacity) - { + if (e == StunAllocateChannel::ErrorCapacity) { // if we aren't allowed to make anymore channels, // don't consider this an error. the channel stays // in the list inactive. we'll try it again if @@ -1206,122 +1062,66 @@ private slots: void trans_error(XMPP::StunTransaction::Error e) { delete trans; - trans = 0; + trans = nullptr; cleanup(); - if(e == StunTransaction::ErrorTimeout) - { + if (e == StunTransaction::ErrorTimeout) { errorString = "Request timed out."; emit q->error(StunAllocate::ErrorTimeout); - } - else - { + } else { errorString = "Generic transaction error."; emit q->error(StunAllocate::ErrorGeneric); } } }; -StunAllocate::StunAllocate(StunTransactionPool *pool) : - QObject(pool) -{ - d = new Private(this); - d->pool = pool; -} - -StunAllocate::~StunAllocate() -{ - delete d; -} - -void StunAllocate::setClientSoftwareNameAndVersion(const QString &str) +StunAllocate::StunAllocate(StunTransactionPool *pool) : QObject(pool) { - d->clientSoftware = str; + d = new Private(this); + d->pool = pool->sharedFromThis(); } -void StunAllocate::start() -{ - d->start(); -} +StunAllocate::~StunAllocate() { delete d; } -void StunAllocate::start(const QHostAddress &addr, int port) -{ - d->start(addr, port); -} +void StunAllocate::setClientSoftwareNameAndVersion(const QString &str) { d->clientSoftware = str; } -void StunAllocate::stop() -{ - d->stop(); -} +void StunAllocate::start() { d->start(); } -QString StunAllocate::serverSoftwareNameAndVersion() const -{ - return d->serverSoftware; -} +void StunAllocate::start(const TransportAddress &addr) { d->start(addr); } -QHostAddress StunAllocate::reflexiveAddress() const -{ - return d->reflexiveAddress; -} +void StunAllocate::stop() { d->stop(); } -int StunAllocate::reflexivePort() const -{ - return d->reflexivePort; -} +QString StunAllocate::serverSoftwareNameAndVersion() const { return d->serverSoftware; } -QHostAddress StunAllocate::relayedAddress() const -{ - return d->relayedAddress; -} +const TransportAddress &StunAllocate::reflexiveAddress() const { return d->reflexiveAddress; } -int StunAllocate::relayedPort() const -{ - return d->relayedPort; -} +const TransportAddress &StunAllocate::relayedAddress() const { return d->relayedAddress; } -QList StunAllocate::permissions() const -{ - return d->permsOut; -} +QList StunAllocate::permissions() const { return d->permsOut; } -void StunAllocate::setPermissions(const QList &perms) -{ - d->setPermissions(perms); -} +void StunAllocate::setPermissions(const QList &perms) { d->setPermissions(perms); } -QList StunAllocate::channels() const -{ - return d->channelsOut; -} +QList StunAllocate::channels() const { return d->channelsOut; } -void StunAllocate::setChannels(const QList &channels) -{ - d->setChannels(channels); -} +void StunAllocate::setChannels(const QList &channels) { d->setChannels(channels); } -int StunAllocate::packetHeaderOverhead(const QHostAddress &addr, int port) const +int StunAllocate::packetHeaderOverhead(const TransportAddress &addr) const { - int channelId = d->getChannel(addr, port); + int channelId = d->getChannel(addr); - if(channelId != -1) - { + if (channelId != -1) { // overhead of ChannelData - if(d->pool->mode() == StunTransaction::Udp) + if (d->pool->mode() == StunTransaction::Udp) return 4; - else // Tcp + else // Tcp return 4 + 3; // add 3 for potential padding - } - else - { + } else { // we add 3 for potential padding - if(d->dfState == StunAllocate::Private::DF_Supported) - { + if (d->dfState == StunAllocate::Private::DF_Supported) { // overhead of STUN-based data, with DONT_FRAGMENT return 40 + 3; - } - else - { + } else { // overhead of STUN-based data, without DONT-FRAGMENT return 36 + 3; } @@ -1330,37 +1130,33 @@ int StunAllocate::packetHeaderOverhead(const QHostAddress &addr, int port) const return -1; } -QByteArray StunAllocate::encode(const QByteArray &datagram, const QHostAddress &addr, int port) +QByteArray StunAllocate::encode(const QByteArray &datagram, const TransportAddress &addr) { - int channelId = d->getChannel(addr, port); + int channelId = d->getChannel(addr); - if(channelId != -1) - { - if(datagram.size() > 65535) + if (channelId != -1) { + if (datagram.size() > 65535) return QByteArray(); - quint16 num = channelId; - quint16 len = datagram.size(); + quint16 num = quint16(channelId); + quint16 len = quint16(datagram.size()); int plen = len; // in tcp mode, round to up to nearest 4 bytes - if(d->pool->mode() == StunTransaction::Tcp) - { + if (d->pool->mode() == StunTransaction::Tcp) { int remainder = plen % 4; - if(remainder != 0) + if (remainder != 0) plen += (4 - remainder); } QByteArray out(4 + plen, 0); StunUtil::write16((quint8 *)out.data(), num); StunUtil::write16((quint8 *)out.data() + 2, len); - memcpy(out.data() + 4, datagram.data(), datagram.size()); + memcpy(out.data() + 4, datagram.data(), size_t(datagram.size())); return out; - } - else - { + } else { StunMessage message; message.setClass(StunMessage::Indication); message.setMethod(StunTypes::Send); @@ -1371,13 +1167,12 @@ QByteArray StunAllocate::encode(const QByteArray &datagram, const QHostAddress & { StunMessage::Attribute a; - a.type = StunTypes::XOR_PEER_ADDRESS; - a.value = StunTypes::createXorPeerAddress(addr, port, message.magic(), message.id()); + a.type = StunTypes::XOR_PEER_ADDRESS; + a.value = StunTypes::createXorPeerAddress(addr, message.magic(), message.id()); list += a; } - if(d->dfState == StunAllocate::Private::DF_Supported) - { + if (d->dfState == StunAllocate::Private::DF_Supported) { StunMessage::Attribute a; a.type = StunTypes::DONT_FRAGMENT; list += a; @@ -1385,7 +1180,7 @@ QByteArray StunAllocate::encode(const QByteArray &datagram, const QHostAddress & { StunMessage::Attribute a; - a.type = StunTypes::DATA; + a.type = StunTypes::DATA; a.value = datagram; list += a; } @@ -1396,58 +1191,50 @@ QByteArray StunAllocate::encode(const QByteArray &datagram, const QHostAddress & } } -QByteArray StunAllocate::decode(const QByteArray &encoded, QHostAddress *addr, int *port) +QByteArray StunAllocate::decode(const QByteArray &encoded, TransportAddress &addr) { - if(encoded.size() < 4) + if (encoded.size() < 4) return QByteArray(); quint16 num = StunUtil::read16((const quint8 *)encoded.data()); quint16 len = StunUtil::read16((const quint8 *)encoded.data() + 2); - if(encoded.size() - 4 < (int)len) + if (encoded.size() - 4 < (int)len) return QByteArray(); - if(!d->getAddressPort(num, addr, port)) + if (!d->getAddressPort(num, addr)) return QByteArray(); return encoded.mid(4, len); } -QByteArray StunAllocate::decode(const StunMessage &encoded, QHostAddress *addr, int *port) +QByteArray StunAllocate::decode(const StunMessage &encoded, TransportAddress &addr) { - QHostAddress paddr; - quint16 pport; + TransportAddress paddr; - if(!StunTypes::parseXorPeerAddress(encoded.attribute(StunTypes::XOR_PEER_ADDRESS), encoded.magic(), encoded.id(), &paddr, &pport)) + if (!StunTypes::parseXorPeerAddress(encoded.attribute(StunTypes::XOR_PEER_ADDRESS), encoded.magic(), encoded.id(), + paddr)) return QByteArray(); QByteArray data = encoded.attribute(StunTypes::DATA); - if(data.isNull()) + if (data.isNull()) return QByteArray(); - *addr = paddr; - *port = pport; + addr = paddr; return data; } -QString StunAllocate::errorString() const -{ - return d->errorString; -} +QString StunAllocate::errorString() const { return d->errorString; } -bool StunAllocate::containsChannelData(const quint8 *data, int size) -{ - return (check_channelData(data, size) != -1 ? true : false); -} +bool StunAllocate::containsChannelData(const quint8 *data, int size) { return check_channelData(data, size) != -1; } QByteArray StunAllocate::readChannelData(const quint8 *data, int size) { int len = check_channelData(data, size); - if(len != -1) + if (len != -1) return QByteArray((const char *)data, len); else return QByteArray(); } - -} +} // namespace XMPP #include "stunallocate.moc" diff --git a/src/irisnet/noncore/stunallocate.h b/src/irisnet/noncore/stunallocate.h index 9653184b..dca6bf70 100644 --- a/src/irisnet/noncore/stunallocate.h +++ b/src/irisnet/noncore/stunallocate.h @@ -19,91 +19,64 @@ #ifndef STUNALLOCATE_H #define STUNALLOCATE_H -#include -#include #include +#include +#include + +#include "transportaddress.h" class QByteArray; namespace XMPP { - class StunMessage; class StunTransactionPool; +class TransportAddress; -class StunAllocate : public QObject -{ +class StunAllocate : public QObject { Q_OBJECT public: - enum Error - { - ErrorGeneric, - ErrorTimeout, - ErrorAuth, - ErrorRejected, - ErrorProtocol, - ErrorCapacity, - ErrorMismatch - }; + enum Error { ErrorGeneric, ErrorTimeout, ErrorAuth, ErrorRejected, ErrorProtocol, ErrorCapacity, ErrorMismatch }; - class Channel - { + class Channel { public: - QHostAddress address; - int port; - - Channel(const QHostAddress &_address, int _port) : - address(_address), - port(_port) - { - } - - inline bool operator==(const Channel &other) - { - if(address == other.address && port == other.port) - return true; - else - return false; - } - - inline bool operator!=(const Channel &other) - { - return !operator==(other); - } + TransportAddress address; + + Channel(const TransportAddress &_address) : address(_address) { } + + inline bool operator==(const Channel &other) const { return address == other.address; } + inline bool operator!=(const Channel &other) const { return !operator==(other); } }; - StunAllocate(StunTransactionPool *pool); + StunAllocate(XMPP::StunTransactionPool *pool); ~StunAllocate(); void setClientSoftwareNameAndVersion(const QString &str); void start(); - void start(const QHostAddress &addr, int port); // use addr association + void start(const TransportAddress &addr); // use addr association void stop(); QString serverSoftwareNameAndVersion() const; - QHostAddress reflexiveAddress() const; - int reflexivePort() const; - - QHostAddress relayedAddress() const; - int relayedPort() const; + const TransportAddress &reflexiveAddress() const; + const TransportAddress &relayedAddress() const; QList permissions() const; - void setPermissions(const QList &perms); + void setPermissions(const QList &perms); QList channels() const; - void setChannels(const QList &channels); + void setChannels(const QList &channels); - int packetHeaderOverhead(const QHostAddress &addr, int port) const; + int packetHeaderOverhead(const TransportAddress &addr) const; - QByteArray encode(const QByteArray &datagram, const QHostAddress &addr, int port); - QByteArray decode(const QByteArray &encoded, QHostAddress *addr = 0, int *port = 0); - QByteArray decode(const StunMessage &encoded, QHostAddress *addr = 0, int *port = 0); + QByteArray encode(const QByteArray &datagram, const TransportAddress &addr); + QByteArray decode(const QByteArray &encoded, TransportAddress &addr); + QByteArray decode(const StunMessage &encoded, TransportAddress &addr); QString errorString() const; - static bool containsChannelData(const quint8 *data, int size); + static bool containsChannelData(const quint8 *data, int size); static QByteArray readChannelData(const quint8 *data, int size); signals: @@ -127,7 +100,6 @@ class StunAllocate : public QObject friend class Private; Private *d; }; +} // namespace XMPP -} - -#endif +#endif // STUNALLOCATE_H diff --git a/src/irisnet/noncore/stunbinding.cpp b/src/irisnet/noncore/stunbinding.cpp index dd1fcc8f..b40de926 100644 --- a/src/irisnet/noncore/stunbinding.cpp +++ b/src/irisnet/noncore/stunbinding.cpp @@ -18,72 +18,66 @@ #include "stunbinding.h" -#include #include "stunmessage.h" -#include "stuntypes.h" #include "stuntransaction.h" +#include "stuntypes.h" +#include "transportaddress.h" namespace XMPP { - -class StunBinding::Private : public QObject -{ +class StunBinding::Private : public QObject { Q_OBJECT public: - StunBinding *q; - StunTransactionPool *pool; - StunTransaction *trans; - QHostAddress stunAddr; - int stunPort; - QHostAddress addr; - int port; - QString errorString; - bool use_extPriority, use_extIceControlling, use_extIceControlled; - quint32 extPriority; - bool extUseCandidate; - quint64 extIceControlling, extIceControlled; + StunBinding *q; + StunTransactionPool::Ptr pool; + std::unique_ptr trans; + TransportAddress stunAddr; + TransportAddress addr; + QString errorString; + bool use_extPriority = false, use_extIceControlling = false, use_extIceControlled = false; + quint32 extPriority = 0; + bool extUseCandidate = false; + quint64 extIceControlling = 0, extIceControlled = 0; QString stuser, stpass; - bool fpRequired; - - Private(StunBinding *_q) : - QObject(_q), - q(_q), - pool(0), - trans(0), - use_extPriority(false), - use_extIceControlling(false), - use_extIceControlled(false), - extUseCandidate(false), - fpRequired(false) - { - } + bool fpRequired = false; - ~Private() - { - delete trans; - } + Private(StunBinding *_q) : QObject(_q), q(_q) { } + + ~Private() { } - void start(const QHostAddress &_addr = QHostAddress(), int _port = -1) + void start(const TransportAddress &_addr = TransportAddress()) { Q_ASSERT(!trans); stunAddr = _addr; - stunPort = _port; - trans = new StunTransaction(this); - connect(trans, SIGNAL(createMessage(QByteArray)), SLOT(trans_createMessage(QByteArray))); - connect(trans, SIGNAL(finished(XMPP::StunMessage)), SLOT(trans_finished(XMPP::StunMessage))); - connect(trans, SIGNAL(error(XMPP::StunTransaction::Error)), SLOT(trans_error(XMPP::StunTransaction::Error))); + trans.reset(new StunTransaction()); + connect(trans.get(), &StunTransaction::createMessage, this, &Private::trans_createMessage); + connect(trans.get(), &StunTransaction::finished, this, &Private::trans_finished); + connect(trans.get(), &StunTransaction::error, this, &Private::trans_error); - if(!stuser.isEmpty()) - { + if (!stuser.isEmpty()) { trans->setShortTermUsername(stuser); trans->setShortTermPassword(stpass); } trans->setFingerprintRequired(fpRequired); - trans->start(pool, stunAddr, stunPort); + trans->start(pool.data(), stunAddr); + } + + void cancel() + { + if (!trans) + return; + auto t = trans.release(); + t->disconnect(this); + t->cancel(); // will self-delete the transaction either on incoming or timeout + // just in case those too + addr = TransportAddress(); + errorString.clear(); + + // now the binding can be reused } private slots: @@ -95,33 +89,29 @@ private slots: QList list; - if(use_extPriority) - { + if (use_extPriority) { StunMessage::Attribute a; - a.type = StunTypes::PRIORITY; + a.type = StunTypes::PRIORITY; a.value = StunTypes::createPriority(extPriority); list += a; } - if(extUseCandidate) - { + if (extUseCandidate) { StunMessage::Attribute a; a.type = StunTypes::USE_CANDIDATE; list += a; } - if(use_extIceControlling) - { + if (use_extIceControlling) { StunMessage::Attribute a; - a.type = StunTypes::ICE_CONTROLLING; + a.type = StunTypes::ICE_CONTROLLING; a.value = StunTypes::createIceControlling(extIceControlling); list += a; } - if(use_extIceControlled) - { + if (use_extIceControlled) { StunMessage::Attribute a; - a.type = StunTypes::ICE_CONTROLLED; + a.type = StunTypes::ICE_CONTROLLED; a.value = StunTypes::createIceControlled(extIceControlled); list += a; } @@ -133,16 +123,13 @@ private slots: void trans_finished(const XMPP::StunMessage &response) { - delete trans; - trans = 0; + trans.reset(); - bool error = false; - int code; + bool error = false; + int code; QString reason; - if(response.mclass() == StunMessage::ErrorResponse) - { - if(!StunTypes::parseErrorCode(response.attribute(StunTypes::ERROR_CODE), &code, &reason)) - { + if (response.mclass() == StunMessage::ErrorResponse) { + if (!StunTypes::parseErrorCode(response.attribute(StunTypes::ERROR_CODE), &code, &reason)) { errorString = "Unable to parse ERROR-CODE in error response."; emit q->error(StunBinding::ErrorProtocol); return; @@ -151,44 +138,34 @@ private slots: error = true; } - if(error) - { + if (error) { errorString = reason; - if(code == StunTypes::RoleConflict) + if (code == StunTypes::RoleConflict) emit q->error(StunBinding::ErrorConflict); else emit q->error(StunBinding::ErrorRejected); return; } - QHostAddress saddr; - quint16 sport = 0; + TransportAddress saddr; QByteArray val; val = response.attribute(StunTypes::XOR_MAPPED_ADDRESS); - if(!val.isNull()) - { - if(!StunTypes::parseXorMappedAddress(val, response.magic(), response.id(), &saddr, &sport)) - { + if (!val.isNull()) { + if (!StunTypes::parseXorMappedAddress(val, response.magic(), response.id(), saddr)) { errorString = "Unable to parse XOR-MAPPED-ADDRESS response."; emit q->error(StunBinding::ErrorProtocol); return; } - } - else - { + } else { val = response.attribute(StunTypes::MAPPED_ADDRESS); - if(!val.isNull()) - { - if(!StunTypes::parseMappedAddress(val, &saddr, &sport)) - { + if (!val.isNull()) { + if (!StunTypes::parseMappedAddress(val, saddr)) { errorString = "Unable to parse MAPPED-ADDRESS response."; emit q->error(StunBinding::ErrorProtocol); return; } - } - else - { + } else { errorString = "Response does not contain XOR-MAPPED-ADDRESS or MAPPED-ADDRESS."; emit q->error(StunBinding::ErrorProtocol); return; @@ -196,103 +173,70 @@ private slots: } addr = saddr; - port = sport; emit q->success(); } void trans_error(XMPP::StunTransaction::Error e) { - delete trans; - trans = 0; + trans.reset(); - if(e == StunTransaction::ErrorTimeout) - { + if (e == StunTransaction::ErrorTimeout) { errorString = "Request timed out."; emit q->error(StunBinding::ErrorTimeout); - } - else - { + } else { errorString = "Generic transaction error."; emit q->error(StunBinding::ErrorGeneric); } } }; -StunBinding::StunBinding(StunTransactionPool *pool) : - QObject(pool) +StunBinding::StunBinding(StunTransactionPool *pool) : QObject(pool) { - d = new Private(this); - d->pool = pool; + d = new Private(this); + d->pool = pool->sharedFromThis(); } -StunBinding::~StunBinding() -{ - delete d; -} +StunBinding::~StunBinding() { delete d; } void StunBinding::setPriority(quint32 i) { d->use_extPriority = true; - d->extPriority = i; + d->extPriority = i; } -void StunBinding::setUseCandidate(bool enabled) -{ - d->extUseCandidate = enabled; -} +quint32 StunBinding::priority() const { return d->extPriority; } + +void StunBinding::setUseCandidate(bool enabled) { d->extUseCandidate = enabled; } + +bool StunBinding::useCandidate() const { return d->extUseCandidate; } void StunBinding::setIceControlling(quint64 i) { d->use_extIceControlling = true; - d->extIceControlling = i; + d->extIceControlling = i; } void StunBinding::setIceControlled(quint64 i) { d->use_extIceControlled = true; - d->extIceControlled = i; -} - -void StunBinding::setShortTermUsername(const QString &username) -{ - d->stuser = username; + d->extIceControlled = i; } -void StunBinding::setShortTermPassword(const QString &password) -{ - d->stpass = password; -} +void StunBinding::setShortTermUsername(const QString &username) { d->stuser = username; } -void StunBinding::setFingerprintRequired(bool enabled) -{ - d->fpRequired = enabled; -} +void StunBinding::setShortTermPassword(const QString &password) { d->stpass = password; } -void StunBinding::start() -{ - d->start(); -} +void StunBinding::setFingerprintRequired(bool enabled) { d->fpRequired = enabled; } -void StunBinding::start(const QHostAddress &addr, int port) -{ - d->start(addr, port); -} +void StunBinding::start() { d->start(); } -QHostAddress StunBinding::reflexiveAddress() const -{ - return d->addr; -} +void StunBinding::start(const TransportAddress &addr) { d->start(addr); } -int StunBinding::reflexivePort() const -{ - return d->port; -} +void StunBinding::cancel() { d->cancel(); } -QString StunBinding::errorString() const -{ - return d->errorString; -} +const TransportAddress &StunBinding::reflexiveAddress() const { return d->addr; } -} +QString StunBinding::errorString() const { return d->errorString; } +} // namespace XMPP #include "stunbinding.moc" diff --git a/src/irisnet/noncore/stunbinding.h b/src/irisnet/noncore/stunbinding.h index 9bec8ae2..bc659d56 100644 --- a/src/irisnet/noncore/stunbinding.h +++ b/src/irisnet/noncore/stunbinding.h @@ -21,34 +21,26 @@ #include -class QHostAddress; - namespace XMPP { - class StunTransactionPool; +class TransportAddress; -class StunBinding : public QObject -{ +class StunBinding : public QObject { Q_OBJECT public: - enum Error - { - ErrorGeneric, - ErrorTimeout, - ErrorRejected, - ErrorProtocol, - ErrorConflict - }; + enum Error { ErrorGeneric, ErrorTimeout, ErrorRejected, ErrorProtocol, ErrorConflict }; StunBinding(StunTransactionPool *pool); ~StunBinding(); // for ICE-use only - void setPriority(quint32 i); - void setUseCandidate(bool enabled); - void setIceControlling(quint64 i); - void setIceControlled(quint64 i); + void setPriority(quint32 i); + quint32 priority() const; + void setUseCandidate(bool enabled); + bool useCandidate() const; + void setIceControlling(quint64 i); + void setIceControlled(quint64 i); void setShortTermUsername(const QString &username); void setShortTermPassword(const QString &password); @@ -56,10 +48,10 @@ class StunBinding : public QObject void setFingerprintRequired(bool enabled); void start(); - void start(const QHostAddress &addr, int port); // use addr association + void start(const XMPP::TransportAddress &addr); // use addr association + void cancel(); - QHostAddress reflexiveAddress() const; - int reflexivePort() const; + const TransportAddress &reflexiveAddress() const; // non-translatable diagnostic string for convenience QString errorString() const; @@ -75,7 +67,6 @@ class StunBinding : public QObject friend class Private; Private *d; }; +} // namespace XMPP -} - -#endif +#endif // STUNBINDING_H diff --git a/src/irisnet/noncore/stunmessage.cpp b/src/irisnet/noncore/stunmessage.cpp index c60f1ffc..24e3f225 100644 --- a/src/irisnet/noncore/stunmessage.cpp +++ b/src/irisnet/noncore/stunmessage.cpp @@ -18,118 +18,72 @@ #include "stunmessage.h" +#include "stunutil.h" + #include #include -#include "stunutil.h" -#define ENSURE_D { if(!d) d = new Private; } +#define ENSURE_D \ + { \ + if (!d) \ + d = new Private; \ + } namespace XMPP { - using namespace StunUtil; // some attribute types we need to explicitly support -enum -{ - AttribMessageIntegrity = 0x0008, - AttribFingerprint = 0x8028 -}; +enum { AttribMessageIntegrity = 0x0008, AttribFingerprint = 0x8028 }; // adapted from public domain source by Ross Williams and Eric Durbin -unsigned long crctable[256] = -{ - 0x00000000L, 0x77073096L, 0xEE0E612CL, 0x990951BAL, - 0x076DC419L, 0x706AF48FL, 0xE963A535L, 0x9E6495A3L, - 0x0EDB8832L, 0x79DCB8A4L, 0xE0D5E91EL, 0x97D2D988L, - 0x09B64C2BL, 0x7EB17CBDL, 0xE7B82D07L, 0x90BF1D91L, - 0x1DB71064L, 0x6AB020F2L, 0xF3B97148L, 0x84BE41DEL, - 0x1ADAD47DL, 0x6DDDE4EBL, 0xF4D4B551L, 0x83D385C7L, - 0x136C9856L, 0x646BA8C0L, 0xFD62F97AL, 0x8A65C9ECL, - 0x14015C4FL, 0x63066CD9L, 0xFA0F3D63L, 0x8D080DF5L, - 0x3B6E20C8L, 0x4C69105EL, 0xD56041E4L, 0xA2677172L, - 0x3C03E4D1L, 0x4B04D447L, 0xD20D85FDL, 0xA50AB56BL, - 0x35B5A8FAL, 0x42B2986CL, 0xDBBBC9D6L, 0xACBCF940L, - 0x32D86CE3L, 0x45DF5C75L, 0xDCD60DCFL, 0xABD13D59L, - 0x26D930ACL, 0x51DE003AL, 0xC8D75180L, 0xBFD06116L, - 0x21B4F4B5L, 0x56B3C423L, 0xCFBA9599L, 0xB8BDA50FL, - 0x2802B89EL, 0x5F058808L, 0xC60CD9B2L, 0xB10BE924L, - 0x2F6F7C87L, 0x58684C11L, 0xC1611DABL, 0xB6662D3DL, - 0x76DC4190L, 0x01DB7106L, 0x98D220BCL, 0xEFD5102AL, - 0x71B18589L, 0x06B6B51FL, 0x9FBFE4A5L, 0xE8B8D433L, - 0x7807C9A2L, 0x0F00F934L, 0x9609A88EL, 0xE10E9818L, - 0x7F6A0DBBL, 0x086D3D2DL, 0x91646C97L, 0xE6635C01L, - 0x6B6B51F4L, 0x1C6C6162L, 0x856530D8L, 0xF262004EL, - 0x6C0695EDL, 0x1B01A57BL, 0x8208F4C1L, 0xF50FC457L, - 0x65B0D9C6L, 0x12B7E950L, 0x8BBEB8EAL, 0xFCB9887CL, - 0x62DD1DDFL, 0x15DA2D49L, 0x8CD37CF3L, 0xFBD44C65L, - 0x4DB26158L, 0x3AB551CEL, 0xA3BC0074L, 0xD4BB30E2L, - 0x4ADFA541L, 0x3DD895D7L, 0xA4D1C46DL, 0xD3D6F4FBL, - 0x4369E96AL, 0x346ED9FCL, 0xAD678846L, 0xDA60B8D0L, - 0x44042D73L, 0x33031DE5L, 0xAA0A4C5FL, 0xDD0D7CC9L, - 0x5005713CL, 0x270241AAL, 0xBE0B1010L, 0xC90C2086L, - 0x5768B525L, 0x206F85B3L, 0xB966D409L, 0xCE61E49FL, - 0x5EDEF90EL, 0x29D9C998L, 0xB0D09822L, 0xC7D7A8B4L, - 0x59B33D17L, 0x2EB40D81L, 0xB7BD5C3BL, 0xC0BA6CADL, - 0xEDB88320L, 0x9ABFB3B6L, 0x03B6E20CL, 0x74B1D29AL, - 0xEAD54739L, 0x9DD277AFL, 0x04DB2615L, 0x73DC1683L, - 0xE3630B12L, 0x94643B84L, 0x0D6D6A3EL, 0x7A6A5AA8L, - 0xE40ECF0BL, 0x9309FF9DL, 0x0A00AE27L, 0x7D079EB1L, - 0xF00F9344L, 0x8708A3D2L, 0x1E01F268L, 0x6906C2FEL, - 0xF762575DL, 0x806567CBL, 0x196C3671L, 0x6E6B06E7L, - 0xFED41B76L, 0x89D32BE0L, 0x10DA7A5AL, 0x67DD4ACCL, - 0xF9B9DF6FL, 0x8EBEEFF9L, 0x17B7BE43L, 0x60B08ED5L, - 0xD6D6A3E8L, 0xA1D1937EL, 0x38D8C2C4L, 0x4FDFF252L, - 0xD1BB67F1L, 0xA6BC5767L, 0x3FB506DDL, 0x48B2364BL, - 0xD80D2BDAL, 0xAF0A1B4CL, 0x36034AF6L, 0x41047A60L, - 0xDF60EFC3L, 0xA867DF55L, 0x316E8EEFL, 0x4669BE79L, - 0xCB61B38CL, 0xBC66831AL, 0x256FD2A0L, 0x5268E236L, - 0xCC0C7795L, 0xBB0B4703L, 0x220216B9L, 0x5505262FL, - 0xC5BA3BBEL, 0xB2BD0B28L, 0x2BB45A92L, 0x5CB36A04L, - 0xC2D7FFA7L, 0xB5D0CF31L, 0x2CD99E8BL, 0x5BDEAE1DL, - 0x9B64C2B0L, 0xEC63F226L, 0x756AA39CL, 0x026D930AL, - 0x9C0906A9L, 0xEB0E363FL, 0x72076785L, 0x05005713L, - 0x95BF4A82L, 0xE2B87A14L, 0x7BB12BAEL, 0x0CB61B38L, - 0x92D28E9BL, 0xE5D5BE0DL, 0x7CDCEFB7L, 0x0BDBDF21L, - 0x86D3D2D4L, 0xF1D4E242L, 0x68DDB3F8L, 0x1FDA836EL, - 0x81BE16CDL, 0xF6B9265BL, 0x6FB077E1L, 0x18B74777L, - 0x88085AE6L, 0xFF0F6A70L, 0x66063BCAL, 0x11010B5CL, - 0x8F659EFFL, 0xF862AE69L, 0x616BFFD3L, 0x166CCF45L, - 0xA00AE278L, 0xD70DD2EEL, 0x4E048354L, 0x3903B3C2L, - 0xA7672661L, 0xD06016F7L, 0x4969474DL, 0x3E6E77DBL, - 0xAED16A4AL, 0xD9D65ADCL, 0x40DF0B66L, 0x37D83BF0L, - 0xA9BCAE53L, 0xDEBB9EC5L, 0x47B2CF7FL, 0x30B5FFE9L, - 0xBDBDF21CL, 0xCABAC28AL, 0x53B39330L, 0x24B4A3A6L, - 0xBAD03605L, 0xCDD70693L, 0x54DE5729L, 0x23D967BFL, - 0xB3667A2EL, 0xC4614AB8L, 0x5D681B02L, 0x2A6F2B94L, +unsigned long crctable[256] = { + 0x00000000L, 0x77073096L, 0xEE0E612CL, 0x990951BAL, 0x076DC419L, 0x706AF48FL, 0xE963A535L, 0x9E6495A3L, 0x0EDB8832L, + 0x79DCB8A4L, 0xE0D5E91EL, 0x97D2D988L, 0x09B64C2BL, 0x7EB17CBDL, 0xE7B82D07L, 0x90BF1D91L, 0x1DB71064L, 0x6AB020F2L, + 0xF3B97148L, 0x84BE41DEL, 0x1ADAD47DL, 0x6DDDE4EBL, 0xF4D4B551L, 0x83D385C7L, 0x136C9856L, 0x646BA8C0L, 0xFD62F97AL, + 0x8A65C9ECL, 0x14015C4FL, 0x63066CD9L, 0xFA0F3D63L, 0x8D080DF5L, 0x3B6E20C8L, 0x4C69105EL, 0xD56041E4L, 0xA2677172L, + 0x3C03E4D1L, 0x4B04D447L, 0xD20D85FDL, 0xA50AB56BL, 0x35B5A8FAL, 0x42B2986CL, 0xDBBBC9D6L, 0xACBCF940L, 0x32D86CE3L, + 0x45DF5C75L, 0xDCD60DCFL, 0xABD13D59L, 0x26D930ACL, 0x51DE003AL, 0xC8D75180L, 0xBFD06116L, 0x21B4F4B5L, 0x56B3C423L, + 0xCFBA9599L, 0xB8BDA50FL, 0x2802B89EL, 0x5F058808L, 0xC60CD9B2L, 0xB10BE924L, 0x2F6F7C87L, 0x58684C11L, 0xC1611DABL, + 0xB6662D3DL, 0x76DC4190L, 0x01DB7106L, 0x98D220BCL, 0xEFD5102AL, 0x71B18589L, 0x06B6B51FL, 0x9FBFE4A5L, 0xE8B8D433L, + 0x7807C9A2L, 0x0F00F934L, 0x9609A88EL, 0xE10E9818L, 0x7F6A0DBBL, 0x086D3D2DL, 0x91646C97L, 0xE6635C01L, 0x6B6B51F4L, + 0x1C6C6162L, 0x856530D8L, 0xF262004EL, 0x6C0695EDL, 0x1B01A57BL, 0x8208F4C1L, 0xF50FC457L, 0x65B0D9C6L, 0x12B7E950L, + 0x8BBEB8EAL, 0xFCB9887CL, 0x62DD1DDFL, 0x15DA2D49L, 0x8CD37CF3L, 0xFBD44C65L, 0x4DB26158L, 0x3AB551CEL, 0xA3BC0074L, + 0xD4BB30E2L, 0x4ADFA541L, 0x3DD895D7L, 0xA4D1C46DL, 0xD3D6F4FBL, 0x4369E96AL, 0x346ED9FCL, 0xAD678846L, 0xDA60B8D0L, + 0x44042D73L, 0x33031DE5L, 0xAA0A4C5FL, 0xDD0D7CC9L, 0x5005713CL, 0x270241AAL, 0xBE0B1010L, 0xC90C2086L, 0x5768B525L, + 0x206F85B3L, 0xB966D409L, 0xCE61E49FL, 0x5EDEF90EL, 0x29D9C998L, 0xB0D09822L, 0xC7D7A8B4L, 0x59B33D17L, 0x2EB40D81L, + 0xB7BD5C3BL, 0xC0BA6CADL, 0xEDB88320L, 0x9ABFB3B6L, 0x03B6E20CL, 0x74B1D29AL, 0xEAD54739L, 0x9DD277AFL, 0x04DB2615L, + 0x73DC1683L, 0xE3630B12L, 0x94643B84L, 0x0D6D6A3EL, 0x7A6A5AA8L, 0xE40ECF0BL, 0x9309FF9DL, 0x0A00AE27L, 0x7D079EB1L, + 0xF00F9344L, 0x8708A3D2L, 0x1E01F268L, 0x6906C2FEL, 0xF762575DL, 0x806567CBL, 0x196C3671L, 0x6E6B06E7L, 0xFED41B76L, + 0x89D32BE0L, 0x10DA7A5AL, 0x67DD4ACCL, 0xF9B9DF6FL, 0x8EBEEFF9L, 0x17B7BE43L, 0x60B08ED5L, 0xD6D6A3E8L, 0xA1D1937EL, + 0x38D8C2C4L, 0x4FDFF252L, 0xD1BB67F1L, 0xA6BC5767L, 0x3FB506DDL, 0x48B2364BL, 0xD80D2BDAL, 0xAF0A1B4CL, 0x36034AF6L, + 0x41047A60L, 0xDF60EFC3L, 0xA867DF55L, 0x316E8EEFL, 0x4669BE79L, 0xCB61B38CL, 0xBC66831AL, 0x256FD2A0L, 0x5268E236L, + 0xCC0C7795L, 0xBB0B4703L, 0x220216B9L, 0x5505262FL, 0xC5BA3BBEL, 0xB2BD0B28L, 0x2BB45A92L, 0x5CB36A04L, 0xC2D7FFA7L, + 0xB5D0CF31L, 0x2CD99E8BL, 0x5BDEAE1DL, 0x9B64C2B0L, 0xEC63F226L, 0x756AA39CL, 0x026D930AL, 0x9C0906A9L, 0xEB0E363FL, + 0x72076785L, 0x05005713L, 0x95BF4A82L, 0xE2B87A14L, 0x7BB12BAEL, 0x0CB61B38L, 0x92D28E9BL, 0xE5D5BE0DL, 0x7CDCEFB7L, + 0x0BDBDF21L, 0x86D3D2D4L, 0xF1D4E242L, 0x68DDB3F8L, 0x1FDA836EL, 0x81BE16CDL, 0xF6B9265BL, 0x6FB077E1L, 0x18B74777L, + 0x88085AE6L, 0xFF0F6A70L, 0x66063BCAL, 0x11010B5CL, 0x8F659EFFL, 0xF862AE69L, 0x616BFFD3L, 0x166CCF45L, 0xA00AE278L, + 0xD70DD2EEL, 0x4E048354L, 0x3903B3C2L, 0xA7672661L, 0xD06016F7L, 0x4969474DL, 0x3E6E77DBL, 0xAED16A4AL, 0xD9D65ADCL, + 0x40DF0B66L, 0x37D83BF0L, 0xA9BCAE53L, 0xDEBB9EC5L, 0x47B2CF7FL, 0x30B5FFE9L, 0xBDBDF21CL, 0xCABAC28AL, 0x53B39330L, + 0x24B4A3A6L, 0xBAD03605L, 0xCDD70693L, 0x54DE5729L, 0x23D967BFL, 0xB3667A2EL, 0xC4614AB8L, 0x5D681B02L, 0x2A6F2B94L, 0xB40BBE37L, 0xC30C8EA1L, 0x5A05DF1BL, 0x2D02EF8DL }; -class Crc32 -{ +class Crc32 { private: quint32 result; public: - Crc32() - { - clear(); - } + Crc32() { clear(); } - void clear() - { - result = 0xffffffff; - } + void clear() { result = 0xffffffff; } void update(const QByteArray &in) { - for(int n = 0; n < in.size(); ++n) + for (int n = 0; n < in.size(); ++n) result = (result >> 8) ^ (crctable[(result & 0xff) ^ (quint8)in[n]]); } - quint32 final() - { - return result ^= 0xffffffff; - } + quint32 final() { return result ^= 0xffffffff; } static quint32 process(const QByteArray &in) { @@ -146,36 +100,35 @@ static quint8 magic_cookie[4] = { 0x21, 0x12, 0xA4, 0x42 }; static int check_and_get_length(const QByteArray &buf) { // stun packets are at least 20 bytes - if(buf.size() < 20) + if (buf.size() < 20) return -1; // minimal 3-field check - // top 2 bits of packet must be 0 - if(buf[0] & 0xC0) + if (buf[0] < 0 || buf[0] > 3) // rfc7983 p.7 return -1; - const quint8 *p = (const quint8 *)buf.data(); - quint16 mlen = read16(p + 2); + const quint8 *p = (const quint8 *)buf.data(); + quint16 mlen = read16(p + 2); // bottom 2 bits of message length field must be 0 - if(mlen & 0x03) + if (mlen & 0x03) return -1; // (also, the message length should be a reasonable size) - if(mlen + 20 > buf.size()) + if (mlen + 20 > buf.size()) return -1; // magic cookie must be set - if(memcmp(p + 4, magic_cookie, 4) != 0) + if (memcmp(p + 4, magic_cookie, 4) != 0) return -1; return mlen; } -#define ATTRIBUTE_AREA_START 20 -#define ATTRIBUTE_AREA_MAX 65535 -#define ATTRIBUTE_VALUE_MAX 65531 +#define ATTRIBUTE_AREA_START 20 +#define ATTRIBUTE_AREA_MAX 65535 +#define ATTRIBUTE_VALUE_MAX 65531 // note: because the attribute area of the packet has a maximum size of // 2^16-1, and each attribute itself has a 4 byte header, it follows that @@ -185,9 +138,9 @@ static int check_and_get_length(const QByteArray &buf) static quint16 round_up_length(quint16 in) { Q_ASSERT(in <= ATTRIBUTE_VALUE_MAX); - quint16 out = in; - quint16 remainder = out % 4; - if(remainder != 0) + quint16 out = in; + quint16 remainder = quint16(out % 4); + if (remainder != 0) out += (4 - remainder); return out; } @@ -204,7 +157,7 @@ static int get_attribute_props(const QByteArray &buf, int offset, quint16 *type, const quint8 *p = (const quint8 *)buf.data(); // need at least 4 bytes for an attribute - if(offset + 4 > buf.size()) + if (offset + 4 > buf.size()) return -1; quint16 _type = read16(p + offset); @@ -215,11 +168,11 @@ static int get_attribute_props(const QByteArray &buf, int offset, quint16 *type, // get physical length. stun attributes are 4-byte aligned, and may // contain 0-3 bytes of padding. quint16 plen = round_up_length(_alen); - if(offset + plen > buf.size()) + if (offset + plen > buf.size()) return -1; *type = _type; - *len = _alen; + *len = _alen; return offset + plen; } @@ -228,22 +181,20 @@ static int get_attribute_props(const QByteArray &buf, int offset, quint16 *type, // len = take attribute value length (value is at offset + 4) // next = take offset of next attribute // returns offset of found attribute, -1 if not found -static int find_attribute(const QByteArray &buf, quint16 type, int *len, int *next = 0) +static int find_attribute(const QByteArray &buf, quint16 type, int *len, int *next = nullptr) { - int at = ATTRIBUTE_AREA_START; + int at = ATTRIBUTE_AREA_START; quint16 _type; - int _len; - int _next; + int _len; + int _next; - while(1) - { + while (1) { _next = get_attribute_props(buf, at, &_type, &_len); - if(_next == -1) + if (_next == -1) break; - if(_type == type) - { + if (_type == type) { *len = _len; - if(next) + if (next) *next = _next; return at; } @@ -261,13 +212,13 @@ static int find_attribute(const QByteArray &buf, quint16 type, int *len, int *ne // note: padding following attribute is zeroed out static int append_attribute_uninitialized(QByteArray *buf, quint16 type, int len) { - if(len > ATTRIBUTE_VALUE_MAX) + if (len > ATTRIBUTE_VALUE_MAX) return -1; quint16 alen = (quint16)len; quint16 plen = round_up_length(alen); - if((buf->size() - ATTRIBUTE_AREA_START) + 4 + plen > ATTRIBUTE_AREA_MAX) + if ((buf->size() - ATTRIBUTE_AREA_START) + 4 + plen > ATTRIBUTE_AREA_MAX) return -1; int at = buf->size(); @@ -278,7 +229,7 @@ static int append_attribute_uninitialized(QByteArray *buf, quint16 type, int len write16(p + at + 2, alen); // padding - for(int n = 0; n < plen - alen; ++n) + for (int n = 0; n < plen - alen; ++n) p[at + alen + n] = 0; return at; @@ -293,8 +244,8 @@ static quint32 fingerprint_calc(const quint8 *buf, int size) static QByteArray message_integrity_calc(const quint8 *buf, int size, const QByteArray &key) { QCA::MessageAuthenticationCode hmac("hmac(sha1)", key); - QByteArray region = QByteArray::fromRawData((const char *)buf, size); - QByteArray result = hmac.process(region).toByteArray(); + QByteArray region = QByteArray::fromRawData((const char *)buf, size); + QByteArray result = hmac.process(region).toByteArray(); Q_ASSERT(result.size() == 20); return result; } @@ -306,16 +257,13 @@ static bool fingerprint_check(const QByteArray &buf) { int at, len; at = find_attribute(buf, AttribFingerprint, &len); - if(at == -1 || len != 4) // value must be 4 bytes + if (at == -1 || len != 4) // value must be 4 bytes return false; - const quint8 *p = (const quint8 *)buf.data(); - quint32 fpval = read32(p + at + 4); - quint32 fpcalc = fingerprint_calc(p, at); - if(fpval == fpcalc) - return true; - else - return false; + const quint8 *p = (const quint8 *)buf.data(); + quint32 fpval = read32(p + at + 4); + quint32 fpcalc = fingerprint_calc(p, at); + return fpval == fpcalc; } // copy the input buffer and prepare for message integrity checking. the @@ -331,14 +279,14 @@ static bool message_integrity_prep(const QByteArray &buf, QByteArray *out, int * { int at, len, next; at = find_attribute(buf, AttribMessageIntegrity, &len, &next); - if(at == -1 || len != 20) // value must be 20 bytes + if (at == -1 || len != 20) // value must be 20 bytes return false; // prepare new attribute area size int i = next - ATTRIBUTE_AREA_START; // new value must be divisible by 4 - if(i % 4 != 0) + if (i % 4 != 0) return false; // copy truncated packet @@ -359,22 +307,18 @@ static bool message_integrity_prep(const QByteArray &buf, QByteArray *out, int * // returns true if correct static bool message_integrity_check(const QByteArray &buf, int offset, const QByteArray &key) { - QByteArray mival = QByteArray::fromRawData(buf.data() + offset + 4, 20); + QByteArray mival = QByteArray::fromRawData(buf.data() + offset + 4, 20); QByteArray micalc = message_integrity_calc((const quint8 *)buf.data(), offset, key); - if(mival == micalc) - return true; - else - return false; + return mival == micalc; } -class StunMessage::Private : public QSharedData -{ +class StunMessage::Private : public QSharedData { public: StunMessage::Class mclass; - quint16 method; - quint8 magic[4]; - quint8 id[12]; - QList attribs; + quint16 method; + quint8 magic[4]; + quint8 id[12]; + QList attribs; Private() { @@ -385,30 +329,19 @@ class StunMessage::Private : public QSharedData } }; -StunMessage::StunMessage() : - d(0) -{ -} +StunMessage::StunMessage() : d(nullptr) { } -StunMessage::StunMessage(const StunMessage &from) : - d(from.d) -{ -} +StunMessage::StunMessage(const StunMessage &from) : d(from.d) { } -StunMessage::~StunMessage() -{ -} +StunMessage::~StunMessage() { } -StunMessage & StunMessage::operator=(const StunMessage &from) +StunMessage &StunMessage::operator=(const StunMessage &from) { d = from.d; return *this; } -bool StunMessage::isNull() const -{ - return (d ? false : true); -} +bool StunMessage::isNull() const { return (d ? false : true); } StunMessage::Class StunMessage::mclass() const { @@ -444,14 +377,24 @@ QByteArray StunMessage::attribute(quint16 type) const { Q_ASSERT(d); - foreach(const Attribute &i, d->attribs) - { - if(i.type == type) + for (const Attribute &i : d->attribs) { + if (i.type == type) return i.value; } return QByteArray(); } +bool StunMessage::hasAttribute(quint16 type) const +{ + Q_ASSERT(d); + + for (const Attribute &i : d->attribs) { + if (i.type == type) + return true; + } + return false; +} + void StunMessage::setClass(Class mclass) { ENSURE_D @@ -488,33 +431,33 @@ QByteArray StunMessage::toBinary(int validationFlags, const QByteArray &key) con // header QByteArray buf(20, 0); - quint8 *p = (quint8 *)buf.data(); + quint8 *p = (quint8 *)buf.data(); quint8 classbits = 0; - if(d->mclass == Request) + if (d->mclass == Request) classbits = 0; // 00 - else if(d->mclass == Indication) + else if (d->mclass == Indication) classbits = 1; // 01 - else if(d->mclass == SuccessResponse) + else if (d->mclass == SuccessResponse) classbits = 2; // 10 - else if(d->mclass == ErrorResponse) + else if (d->mclass == ErrorResponse) classbits = 3; // 11 else Q_ASSERT(0); // method bits are split into 3 sections quint16 m1, m2, m3; - m1 = d->method & 0x0f80; // M7-11 + m1 = quint16(d->method & 0x0f80); // M7-11 m1 <<= 2; - m2 = d->method & 0x0070; // M4-6 + m2 = quint16(d->method & 0x0070); // M4-6 m2 <<= 1; - m3 = d->method & 0x000f; // M0-3 + m3 = quint16(d->method & 0x000f); // M0-3 // class bits are split into 2 sections quint16 c1, c2; - c1 = classbits & 0x02; // C1 + c1 = quint16(classbits & 0x02); // C1 c1 <<= 7; - c2 = classbits & 0x01; // C0 + c2 = quint16(classbits & 0x01); // C0 c2 <<= 4; quint16 type = m1 | m2 | m3 | c1 | c2; @@ -523,31 +466,29 @@ QByteArray StunMessage::toBinary(int validationFlags, const QByteArray &key) con memcpy(p + 4, d->magic, 4); memcpy(p + 8, d->id, 12); - foreach(const Attribute &i, d->attribs) - { + for (const Attribute &i : d->attribs) { int at = append_attribute_uninitialized(&buf, i.type, i.value.size()); - if(at == -1) + if (at == -1) return QByteArray(); p = (quint8 *)buf.data(); // follow the resize - memcpy(buf.data() + at + 4, i.value.data(), i.value.size()); + memcpy(buf.data() + at + 4, i.value.data(), size_t(i.value.size())); } // set attribute area size - write16(p + 2, buf.size() - ATTRIBUTE_AREA_START); + write16(p + 2, quint16(buf.size() - ATTRIBUTE_AREA_START)); - if(validationFlags & MessageIntegrity) - { + if (validationFlags & MessageIntegrity) { quint16 alen = 20; // size of hmac(sha1) - int at = append_attribute_uninitialized(&buf, AttribMessageIntegrity, alen); - if(at == -1) + int at = append_attribute_uninitialized(&buf, AttribMessageIntegrity, alen); + if (at == -1) return QByteArray(); p = (quint8 *)buf.data(); // follow the resize // set attribute area size to include the new attribute - write16(p + 2, buf.size() - ATTRIBUTE_AREA_START); + write16(p + 2, quint16(buf.size() - ATTRIBUTE_AREA_START)); // now calculate the hash and fill in the value QByteArray result = message_integrity_calc(p, at, key); @@ -555,17 +496,16 @@ QByteArray StunMessage::toBinary(int validationFlags, const QByteArray &key) con memcpy(p + at + 4, result.data(), alen); } - if(validationFlags & Fingerprint) - { + if (validationFlags & Fingerprint) { quint16 alen = 4; // size of crc32 - int at = append_attribute_uninitialized(&buf, AttribFingerprint, alen); - if(at == -1) + int at = append_attribute_uninitialized(&buf, AttribFingerprint, alen); + if (at == -1) return QByteArray(); p = (quint8 *)buf.data(); // follow the resize // set attribute area size to include the new attribute - write16(p + 2, buf.size() - ATTRIBUTE_AREA_START); + write16(p + 2, quint16(buf.size() - ATTRIBUTE_AREA_START)); // now calculate the fingerprint and fill in the value quint32 fpcalc = fingerprint_calc(p, at); @@ -575,21 +515,19 @@ QByteArray StunMessage::toBinary(int validationFlags, const QByteArray &key) con return buf; } -StunMessage StunMessage::fromBinary(const QByteArray &a, ConvertResult *result, int validationFlags, const QByteArray &key) +StunMessage StunMessage::fromBinary(const QByteArray &a, ConvertResult *result, int validationFlags, + const QByteArray &key) { int mlen = check_and_get_length(a); - if(mlen == -1) - { - if(result) + if (mlen == -1) { + if (result) *result = ErrorFormat; return StunMessage(); } - if(validationFlags & Fingerprint) - { - if(!fingerprint_check(a)) - { - if(result) + if (validationFlags & Fingerprint) { + if (!fingerprint_check(a)) { + if (result) *result = ErrorFingerprint; return StunMessage(); } @@ -597,24 +535,20 @@ StunMessage StunMessage::fromBinary(const QByteArray &a, ConvertResult *result, QByteArray in; - if(validationFlags & MessageIntegrity) - { + if (validationFlags & MessageIntegrity) { int offset; - if(!message_integrity_prep(a, &in, &offset)) - { - if(result) + if (!message_integrity_prep(a, &in, &offset)) { + if (result) *result = ErrorMessageIntegrity; return StunMessage(); } - if(!message_integrity_check(in, offset, key)) - { - if(result) + if (!message_integrity_check(in, offset, key)) { + if (result) *result = ErrorMessageIntegrity; return StunMessage(); } - } - else + } else in = a; // all validating complete, now just parse the packet @@ -623,28 +557,28 @@ StunMessage StunMessage::fromBinary(const QByteArray &a, ConvertResult *result, // method bits are split into 3 sections quint16 m1, m2, m3; - m1 = p[0] & 0x3e; // M7-11 + m1 = quint16(p[0] & 0x3e); // M7-11 m1 <<= 6; - m2 = p[1] & 0xe0; // M4-6 + m2 = quint16(p[1] & 0xe0); // M4-6 m2 >>= 1; - m3 = p[1] & 0x0f; // M0-3 + m3 = quint16(p[1] & 0x0f); // M0-3 // class bits are split into 2 sections quint8 c1, c2; - c1 = p[0] & 0x01; // C1 + c1 = quint8(p[0] & 0x01); // C1 c1 <<= 1; - c2 = p[1] & 0x10; // C0 + c2 = quint8(p[1] & 0x10); // C0 c2 >>= 4; - quint16 method = m1 | m2 | m3; - quint8 classbits = c1 | c2; + quint16 method = m1 | m2 | m3; + quint8 classbits = c1 | c2; Class mclass; - if(classbits == 0) // 00 + if (classbits == 0) // 00 mclass = Request; - else if(classbits == 1) // 01 + else if (classbits == 1) // 01 mclass = Indication; - else if(classbits == 2) // 10 + else if (classbits == 2) // 10 mclass = SuccessResponse; else // 11 mclass = ErrorResponse; @@ -656,19 +590,18 @@ StunMessage StunMessage::fromBinary(const QByteArray &a, ConvertResult *result, out.setId(p + 8); QList list; - int at = ATTRIBUTE_AREA_START; - while(1) - { + int at = ATTRIBUTE_AREA_START; + while (1) { quint16 type; - int len; - int next; + int len; + int next; next = get_attribute_props(in, at, &type, &len); - if(next == -1) + if (next == -1) break; Attribute attrib; - attrib.type = type; + attrib.type = type; attrib.value = in.mid(at + 4, len); list += attrib; @@ -676,15 +609,12 @@ StunMessage StunMessage::fromBinary(const QByteArray &a, ConvertResult *result, } out.setAttributes(list); - if(result) + if (result) *result = ConvertGood; return out; } -bool StunMessage::isProbablyStun(const QByteArray &a) -{ - return (check_and_get_length(a) != -1 ? true : false); -} +bool StunMessage::isProbablyStun(const QByteArray &a) { return check_and_get_length(a) != -1; } StunMessage::Class StunMessage::extractClass(const QByteArray &in) { @@ -692,19 +622,19 @@ StunMessage::Class StunMessage::extractClass(const QByteArray &in) // class bits are split into 2 sections quint8 c1, c2; - c1 = p[0] & 0x01; // C1 + c1 = quint8(p[0] & 0x01); // C1 c1 <<= 1; - c2 = p[1] & 0x10; // C0 + c2 = quint8(p[1] & 0x10); // C0 c2 >>= 4; quint8 classbits = c1 | c2; Class mclass; - if(classbits == 0) // 00 + if (classbits == 0) // 00 mclass = Request; - else if(classbits == 1) // 01 + else if (classbits == 1) // 01 mclass = Indication; - else if(classbits == 2) // 10 + else if (classbits == 2) // 10 mclass = SuccessResponse; else // 11 mclass = ErrorResponse; @@ -715,17 +645,16 @@ StunMessage::Class StunMessage::extractClass(const QByteArray &in) bool StunMessage::containsStun(const quint8 *data, int size) { // check_and_get_length does a full packet check so it works even on a stream - return (check_and_get_length(QByteArray::fromRawData((const char *)data, size)) != -1 ? true : false); + return check_and_get_length(QByteArray::fromRawData((const char *)data, size)) != -1; } QByteArray StunMessage::readStun(const quint8 *data, int size) { - QByteArray in = QByteArray::fromRawData((const char *)data, size); - int mlen = check_and_get_length(in); - if(mlen != -1) + QByteArray in = QByteArray::fromRawData((const char *)data, size); + int mlen = check_and_get_length(in); + if (mlen != -1) return QByteArray((const char *)data, mlen + 20); else return QByteArray(); } - -} +} // namespace XMPP diff --git a/src/irisnet/noncore/stunmessage.h b/src/irisnet/noncore/stunmessage.h index 5c2369bd..dd2ba937 100644 --- a/src/irisnet/noncore/stunmessage.h +++ b/src/irisnet/noncore/stunmessage.h @@ -24,65 +24,50 @@ #include namespace XMPP { - -class StunMessage -{ +class StunMessage { public: - enum Class - { - Request, - SuccessResponse, - ErrorResponse, - Indication - }; + enum Class { Request, SuccessResponse, ErrorResponse, Indication }; - enum ValidationFlags - { - Fingerprint = 0x01, + enum ValidationFlags { + Fingerprint = 0x01, // you must have the hmac(sha1) algorithm in QCA to use MessageIntegrity = 0x02 }; - enum ConvertResult - { - ConvertGood, - ErrorFormat, - ErrorFingerprint, - ErrorMessageIntegrity, - ErrorConvertUnknown = 64 - }; + enum ConvertResult { ConvertGood, ErrorFormat, ErrorFingerprint, ErrorMessageIntegrity, ErrorConvertUnknown = 64 }; - class Attribute - { + class Attribute { public: - quint16 type; + quint16 type; QByteArray value; }; StunMessage(); StunMessage(const StunMessage &from); ~StunMessage(); - StunMessage & operator=(const StunMessage &from); + StunMessage &operator=(const StunMessage &from); - bool isNull() const; - Class mclass() const; - quint16 method() const; - const quint8 *magic() const; // 4 bytes - const quint8 *id() const; // 12 bytes + bool isNull() const; + Class mclass() const; + quint16 method() const; + const quint8 *magic() const; // 4 bytes + const quint8 *id() const; // 12 bytes QList attributes() const; // returns the first instance or null QByteArray attribute(quint16 type) const; + bool hasAttribute(quint16 type) const; void setClass(Class mclass); void setMethod(quint16 method); void setMagic(const quint8 *magic); // 4 bytes - void setId(const quint8 *id); // 12 bytes + void setId(const quint8 *id); // 12 bytes void setAttributes(const QList &attribs); - QByteArray toBinary(int validationFlags = 0, const QByteArray &key = QByteArray()) const; - static StunMessage fromBinary(const QByteArray &a, ConvertResult *result = 0, int validationFlags = 0, const QByteArray &key = QByteArray()); + QByteArray toBinary(int validationFlags = 0, const QByteArray &key = QByteArray()) const; + static StunMessage fromBinary(const QByteArray &a, ConvertResult *result = nullptr, int validationFlags = 0, + const QByteArray &key = QByteArray()); // minimal 3-field check static bool isProbablyStun(const QByteArray &a); @@ -103,7 +88,6 @@ class StunMessage class Private; QSharedDataPointer d; }; +} // namespace XMPP -} - -#endif +#endif // STUNMESSAGE_H diff --git a/src/irisnet/noncore/stuntransaction.cpp b/src/irisnet/noncore/stuntransaction.cpp index 3927cf9e..24bcc43c 100644 --- a/src/irisnet/noncore/stuntransaction.cpp +++ b/src/irisnet/noncore/stuntransaction.cpp @@ -1,5 +1,6 @@ /* * Copyright (C) 2009 Barracuda Networks, Inc. + * Copyright (C) 2013-2021 Psi IM team * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -18,20 +19,22 @@ #include "stuntransaction.h" +#include "stunbinding.h" +#include "stunmessage.h" +#include "stuntypes.h" +#include "stunutil.h" +#include "transportaddress.h" + +#include #include #include #include #include #include -#include "stunutil.h" -#include "stunmessage.h" -#include "stuntypes.h" -#include "stunbinding.h" Q_DECLARE_METATYPE(XMPP::StunTransaction::Error) namespace XMPP { - // parse a stun message, optionally performing validity checks. the // StunMessage class itself provides parsing with validity or parsing // without validity, but it does not provide a way to do both together, @@ -44,42 +47,37 @@ static StunMessage parse_stun_message(const QByteArray &packet, int *validationF // parsing the packet, so we should be able to avoid most redundant // processing. fromBinary checks the fingerprint first, and we // can use that knowledge to avoid duplicating integrity checks. - int flags = 0; + int flags = 0; StunMessage::ConvertResult result; - StunMessage msg = StunMessage::fromBinary(packet, &result, StunMessage::MessageIntegrity | StunMessage::Fingerprint, key); - if(result == StunMessage::ErrorFingerprint) - { + StunMessage msg + = StunMessage::fromBinary(packet, &result, StunMessage::MessageIntegrity | StunMessage::Fingerprint, key); + if (result == StunMessage::ErrorFingerprint) { // if fingerprint fails, then it is the only thing that was // performed and we can skip it now. msg = StunMessage::fromBinary(packet, &result, StunMessage::MessageIntegrity, key); - if(result == StunMessage::ErrorMessageIntegrity) - { + if (result == StunMessage::ErrorMessageIntegrity) { // if message-integrity fails, then it is the only // thing that was performed and we can skip it now msg = StunMessage::fromBinary(packet, &result); - if(result == StunMessage::ConvertGood) + if (result == StunMessage::ConvertGood) flags = 0; else return msg; // null - } - else if(result == StunMessage::ConvertGood) + } else if (result == StunMessage::ConvertGood) flags = StunMessage::MessageIntegrity; else return msg; // null - } - else if(result == StunMessage::ErrorMessageIntegrity) - { + } else if (result == StunMessage::ErrorMessageIntegrity) { // fingerprint succeeded, but message-integrity failed. parse // without validation now (to skip redundant // fingerprint/message-integrity checks), and assume correct // fingerprint msg = StunMessage::fromBinary(packet, &result); - if(result == StunMessage::ConvertGood) + if (result == StunMessage::ConvertGood) flags = StunMessage::Fingerprint; else return msg; // null - } - else if(result == StunMessage::ConvertGood) + } else if (result == StunMessage::ConvertGood) flags = StunMessage::MessageIntegrity | StunMessage::Fingerprint; else return msg; // null @@ -88,116 +86,91 @@ static StunMessage parse_stun_message(const QByteArray &packet, int *validationF return msg; } -class StunTransactionPoolPrivate : public QObject -{ +class StunTransactionPoolPrivate : public QObject { Q_OBJECT public: - StunTransactionPool *q; - StunTransaction::Mode mode; - QSet transactions; - QHash transToId; - QHash idToTrans; - bool useLongTermAuth; - bool needLongTermAuth; - bool triedLongTermAuth; - QString user; - QCA::SecureArray pass; - QString realm; - QString nonce; - int debugLevel; - - StunTransactionPoolPrivate(StunTransactionPool *_q) : - QObject(_q), - q(_q), - useLongTermAuth(false), - needLongTermAuth(false), - triedLongTermAuth(false), - debugLevel(StunTransactionPool::DL_None) - { - } + StunTransactionPool *q; + StunTransaction::Mode mode; + QSet transactions; + QHash transToId; + QHash idToTrans; + bool useLongTermAuth = false; + bool needLongTermAuth = false; + QSet triedLongTermAuth; + QString user; + QCA::SecureArray pass; + QString realm; + QString nonce; + int debugLevel = StunTransactionPool::DL_None; + + StunTransactionPoolPrivate(StunTransactionPool *_q) : QObject(_q), q(_q) { } QByteArray generateId() const; - void insert(StunTransaction *trans); - void remove(StunTransaction *trans); - void transmit(StunTransaction *trans); + void insert(StunTransaction *trans); + void remove(StunTransaction *trans); + void transmit(StunTransaction *trans); }; //---------------------------------------------------------------------------- // StunTransaction //---------------------------------------------------------------------------- -class StunTransactionPrivate : public QObject -{ +class StunTransactionPrivate : public QObject { Q_OBJECT public: StunTransaction *q; - StunTransactionPool *pool; - bool active; - StunTransaction::Mode mode; - StunMessage origMessage; - QByteArray id; - QByteArray packet; - QHostAddress to_addr; - int to_port; - - int rto, rc, rm, ti; - int tries; - int last_interval; + StunTransactionPool::Ptr pool; + bool active = false; + bool cancelling = false; + StunTransaction::Mode mode; + StunMessage origMessage; + QByteArray id; + QByteArray packet; + TransportAddress to_addr; + + // defaults from RFC 5389 + int rto = 500, rc = 7, rm = 16, ti = 39500; + int tries; + int last_interval; QTimer *t; - QString stuser; - QString stpass; - bool fpRequired; - QByteArray key; - QTime time; - - StunTransactionPrivate(StunTransaction *_q) : - QObject(_q), - q(_q), - pool(0), - fpRequired(false) + QString stuser; + QString stpass; + bool fpRequired = false; + QByteArray key; + QElapsedTimer time; + + StunTransactionPrivate(StunTransaction *_q) : QObject(_q), q(_q) { qRegisterMetaType(); - active = false; - t = new QTimer(this); - connect(t, SIGNAL(timeout()), SLOT(t_timeout())); + connect(t, &QTimer::timeout, this, &StunTransactionPrivate::t_timeout); t->setSingleShot(true); - - // defaults from RFC 5389 - rto = 500; - rc = 7; - rm = 16; - ti = 39500; } ~StunTransactionPrivate() { - if(pool) + if (pool) pool->d->remove(q); t->disconnect(this); - t->setParent(0); + t->setParent(nullptr); t->deleteLater(); } - void start(StunTransactionPool *_pool, const QHostAddress &toAddress, int toPort) + void start(StunTransactionPool::Ptr _pool, const TransportAddress &toAddress) { - pool = _pool; - mode = pool->d->mode; + pool = _pool; + mode = pool->d->mode; to_addr = toAddress; - to_port = toPort; tryRequest(); } - void setMessage(const StunMessage &request) - { - origMessage = request; - } + void setMessage(const StunMessage &request) { origMessage = request; } void retry() { @@ -211,12 +184,11 @@ class StunTransactionPrivate : public QObject { emit q->createMessage(pool->d->generateId()); - if(origMessage.isNull()) - { + if (origMessage.isNull()) { // since a transaction is not cancelable nor reusable, // there's no DOR-SR issue here QMetaObject::invokeMethod(q, "error", Qt::QueuedConnection, - Q_ARG(XMPP::StunTransaction::Error, StunTransaction::ErrorGeneric)); + Q_ARG(XMPP::StunTransaction::Error, StunTransaction::ErrorGeneric)); return; } @@ -225,35 +197,34 @@ class StunTransactionPrivate : public QObject out.setClass(StunMessage::Request); id = QByteArray((const char *)out.id(), 12); - if(!stuser.isEmpty()) - { + if (!stuser.isEmpty()) { QList list = out.attributes(); - StunMessage::Attribute attr; + StunMessage::Attribute attr; attr.type = StunTypes::USERNAME; - attr.value = StunTypes::createUsername(QString::fromUtf8(StunUtil::saslPrep(stuser.toUtf8()).toByteArray())); + attr.value + = StunTypes::createUsername(QString::fromUtf8(StunUtil::saslPrep(stuser.toUtf8()).toByteArray())); list += attr; out.setAttributes(list); key = StunUtil::saslPrep(stpass.toUtf8()).toByteArray(); - } - else if(!pool->d->nonce.isEmpty()) - { + } else if (!pool->d->nonce.isEmpty()) { QList list = out.attributes(); { StunMessage::Attribute attr; - attr.type = StunTypes::USERNAME; - attr.value = StunTypes::createUsername(QString::fromUtf8(StunUtil::saslPrep(pool->d->user.toUtf8()).toByteArray())); + attr.type = StunTypes::USERNAME; + attr.value = StunTypes::createUsername( + QString::fromUtf8(StunUtil::saslPrep(pool->d->user.toUtf8()).toByteArray())); list += attr; } { StunMessage::Attribute attr; - attr.type = StunTypes::REALM; + attr.type = StunTypes::REALM; attr.value = StunTypes::createRealm(pool->d->realm); list += attr; } { StunMessage::Attribute attr; - attr.type = StunTypes::NONCE; + attr.type = StunTypes::NONCE; attr.value = StunTypes::createNonce(pool->d->nonce); list += attr; } @@ -269,34 +240,29 @@ class StunTransactionPrivate : public QObject key = QCA::Hash("md5").process(buf).toByteArray(); } - if(!key.isEmpty()) + if (!key.isEmpty()) packet = out.toBinary(StunMessage::MessageIntegrity | StunMessage::Fingerprint, key); else packet = out.toBinary(StunMessage::Fingerprint); - if(packet.isEmpty()) - { + if (packet.isEmpty()) { // since a transaction is not cancelable nor reusable, // there's no DOR-SR issue here QMetaObject::invokeMethod(q, "error", Qt::QueuedConnection, - Q_ARG(XMPP::StunTransaction::Error, StunTransaction::ErrorGeneric)); + Q_ARG(XMPP::StunTransaction::Error, StunTransaction::ErrorGeneric)); return; } active = true; - tries = 1; // we transmit immediately here, so count it + tries = 1; // we transmit immediately here, so count it - if(mode == StunTransaction::Udp) - { + if (mode == StunTransaction::Udp) { last_interval = rm * rto; t->start(rto); rto *= 2; - } - else if(mode == StunTransaction::Tcp) - { + } else if (mode == StunTransaction::Tcp) { t->start(ti); - } - else + } else Q_ASSERT(0); time.start(); @@ -307,113 +273,109 @@ class StunTransactionPrivate : public QObject private slots: void t_timeout() { - if(mode == StunTransaction::Tcp || tries == rc) - { + if (cancelling) { + q->deleteLater(); + return; + } + if (mode == StunTransaction::Tcp || tries == rc) { pool->d->remove(q); emit q->error(StunTransaction::ErrorTimeout); return; } ++tries; - if(tries == rc) - { + if (tries == rc) { t->start(last_interval); - } - else - { + } else { t->start(rto); rto *= 2; } + QString dbg; + if (to_addr.isValid()) + dbg += QString("to=(%1)").arg(to_addr); + + emit pool->debugLine(QString("stun transaction %1 timeout. retransmitting..").arg(dbg)); transmit(); } private: void transmit() { - if(pool->d->debugLevel >= StunTransactionPool::DL_Packet) - { + if (pool->d->debugLevel >= StunTransactionPool::DL_Packet) { QString str = QString("STUN SEND: elapsed=") + QString::number(time.elapsed()); - if(!to_addr.isNull()) - str += QString(" to=(") + to_addr.toString() + ';' + QString::number(to_port) + ')'; + if (to_addr.isValid()) + str += QString(" to=(%1)").arg(to_addr); emit pool->debugLine(str); StunMessage msg = StunMessage::fromBinary(packet); - emit pool->debugLine(StunTypes::print_packet_str(msg)); + emit pool->debugLine(StunTypes::print_packet_str(msg)); } pool->d->transmit(q); } - bool checkActiveAndFrom(const QHostAddress &from_addr, int from_port) + bool checkActiveAndFrom(const TransportAddress &from_addr) { - if(!active) - return false; - - if(!to_addr.isNull() && (to_addr != from_addr || to_port != from_port)) + if (!active) return false; - return true; + return !to_addr.isValid() || (to_addr == from_addr); } - void processIncoming(const StunMessage &msg, bool authed) + void processIncoming(const StunMessage &msg, bool authed, const TransportAddress &from_addr) { active = false; t->stop(); + if (cancelling) { + q->deleteLater(); + return; + } - if(pool->d->debugLevel >= StunTransactionPool::DL_Packet) - emit pool->debugLine(QString("matched incoming response to existing request. elapsed=") + QString::number(time.elapsed())); + if (pool->d->debugLevel >= StunTransactionPool::DL_Packet) + emit pool->debugLine(QString("matched incoming response to existing request. elapsed=") + + QString::number(time.elapsed())); // will be set to true when receiving an Unauthorized error bool unauthError = false; - if(msg.mclass() == StunMessage::ErrorResponse && pool->d->useLongTermAuth) - { + bool triedLongTermAuth = pool->d->triedLongTermAuth.contains(from_addr); + if (msg.mclass() == StunMessage::ErrorResponse && pool->d->useLongTermAuth) { // we'll handle certain error codes at this layer - int code; + int code; QString reason; - if(StunTypes::parseErrorCode(msg.attribute(StunTypes::ERROR_CODE), &code, &reason)) - { - if(code == StunTypes::Unauthorized) + if (StunTypes::parseErrorCode(msg.attribute(StunTypes::ERROR_CODE), &code, &reason)) { + if (code == StunTypes::Unauthorized) unauthError = true; - if(unauthError && !pool->d->triedLongTermAuth) - { + if (unauthError && !triedLongTermAuth) { QString realm; QString nonce; - if(StunTypes::parseRealm(msg.attribute(StunTypes::REALM), &realm) && - StunTypes::parseRealm(msg.attribute(StunTypes::NONCE), &nonce)) - { + if (StunTypes::parseRealm(msg.attribute(StunTypes::REALM), &realm) + && StunTypes::parseRealm(msg.attribute(StunTypes::NONCE), &nonce)) { // always set these to the latest received values, // which will be used for all transactions // once creds are provided. - if(pool->d->realm.isEmpty()) + if (pool->d->realm.isEmpty()) pool->d->realm = realm; pool->d->nonce = nonce; - if(!pool->d->needLongTermAuth) - { - if(!pool->d->user.isEmpty()) - { + if (!pool->d->needLongTermAuth) { + if (!pool->d->user.isEmpty()) { // creds already set? use them - pool->d->triedLongTermAuth = true; + pool->d->triedLongTermAuth.insert(from_addr); retry(); - } - else - { + } else { // else ask the user - pool->d->needLongTermAuth = true; - emit pool->needAuthParams(); + pool->d->triedLongTermAuth.insert(from_addr); + emit pool->needAuthParams(from_addr); } } return; } - } - else if(code == StunTypes::StaleNonce && pool->d->triedLongTermAuth) - { + } else if (code == StunTypes::StaleNonce && triedLongTermAuth) { QString nonce; - if(StunTypes::parseNonce(msg.attribute(StunTypes::NONCE), &nonce) && nonce != pool->d->nonce) - { + if (StunTypes::parseNonce(msg.attribute(StunTypes::NONCE), &nonce) && nonce != pool->d->nonce) { pool->d->nonce = nonce; retry(); return; @@ -423,7 +385,7 @@ private slots: } // require message integrity when auth is used - if(!unauthError && (!stuser.isEmpty() || pool->d->triedLongTermAuth) && !authed) + if (!unauthError && (!stuser.isEmpty() || triedLongTermAuth) && !authed) return; pool->d->remove(q); @@ -431,75 +393,66 @@ private slots: } public: - bool writeIncomingMessage(const StunMessage &msg, const QHostAddress &from_addr, int from_port) + bool writeIncomingMessage(const StunMessage &msg, const TransportAddress &from_addr) { - if(!checkActiveAndFrom(from_addr, from_port)) + if (!checkActiveAndFrom(from_addr)) return false; // if a StunMessage is passed directly to us then we assume // the user has authenticated the message as necessary - processIncoming(msg, true); + processIncoming(msg, true, from_addr); return true; } - bool writeIncomingMessage(const QByteArray &packet, bool *notStun, const QHostAddress &from_addr, int from_port) + bool writeIncomingMessage(const QByteArray &packet, bool *notStun, const TransportAddress &from_addr) { - if(!checkActiveAndFrom(from_addr, from_port)) - { + if (!checkActiveAndFrom(from_addr)) { // could be STUN, don't really know for sure *notStun = false; return false; } - int validationFlags = 0; - StunMessage msg = parse_stun_message(packet, &validationFlags, key); - if(msg.isNull()) - { + int validationFlags = 0; + StunMessage msg = parse_stun_message(packet, &validationFlags, key); + if (msg.isNull()) { // packet doesn't parse at all, surely not STUN *notStun = true; return false; } - if(fpRequired && !(validationFlags & StunMessage::Fingerprint)) - { + if (fpRequired && !(validationFlags & StunMessage::Fingerprint)) { // fingerprint failed when required. consider the // packet to be surely not STUN *notStun = true; return false; } - processIncoming(msg, (validationFlags & StunMessage::MessageIntegrity) ? true : false); + processIncoming(msg, (validationFlags & StunMessage::MessageIntegrity) != 0, from_addr); return true; } public slots: void continueAfterParams() { + if (cancelling) + return; retry(); } }; -StunTransaction::StunTransaction(QObject *parent) : - QObject(parent) -{ - d = new StunTransactionPrivate(this); -} +StunTransaction::StunTransaction(QObject *parent) : QObject(parent) { d = new StunTransactionPrivate(this); } -StunTransaction::~StunTransaction() -{ - delete d; -} +StunTransaction::~StunTransaction() { delete d; } -void StunTransaction::start(StunTransactionPool *pool, const QHostAddress &toAddress, int toPort) +void StunTransaction::start(StunTransactionPool *pool, const TransportAddress &toAddress) { Q_ASSERT(!d->active); - d->start(pool, toAddress, toPort); + d->start(pool->sharedFromThis(), toAddress); } -void StunTransaction::setMessage(const StunMessage &request) -{ - d->setMessage(request); -} +void StunTransaction::cancel() { d->cancelling = true; } + +void StunTransaction::setMessage(const StunMessage &request) { d->setMessage(request); } void StunTransaction::setRTO(int i) { @@ -525,20 +478,11 @@ void StunTransaction::setTi(int i) d->ti = i; } -void StunTransaction::setShortTermUsername(const QString &username) -{ - d->stuser = username; -} +void StunTransaction::setShortTermUsername(const QString &username) { d->stuser = username; } -void StunTransaction::setShortTermPassword(const QString &password) -{ - d->stpass = password; -} +void StunTransaction::setShortTermPassword(const QString &password) { d->stpass = password; } -void StunTransaction::setFingerprintRequired(bool enabled) -{ - d->fpRequired = enabled; -} +void StunTransaction::setFingerprintRequired(bool enabled) { d->fpRequired = enabled; } //---------------------------------------------------------------------------- // StunTransactionPool @@ -547,10 +491,9 @@ QByteArray StunTransactionPoolPrivate::generateId() const { QByteArray id; - do - { + do { id = QCA::Random::randomArray(12).toByteArray(); - } while(idToTrans.contains(id)); + } while (idToTrans.contains(id)); return id; } @@ -567,8 +510,7 @@ void StunTransactionPoolPrivate::insert(StunTransaction *trans) void StunTransactionPoolPrivate::remove(StunTransaction *trans) { - if(transactions.contains(trans)) - { + if (transactions.contains(trans)) { transactions.remove(trans); QByteArray id = transToId.value(trans); transToId.remove(trans); @@ -578,67 +520,61 @@ void StunTransactionPoolPrivate::remove(StunTransaction *trans) void StunTransactionPoolPrivate::transmit(StunTransaction *trans) { - emit q->outgoingMessage(trans->d->packet, trans->d->to_addr, trans->d->to_port); + emit q->outgoingMessage(trans->d->packet, trans->d->to_addr); } -StunTransactionPool::StunTransactionPool(StunTransaction::Mode mode, QObject *parent) : - QObject(parent) +StunTransactionPool::StunTransactionPool(StunTransaction::Mode mode) { - d = new StunTransactionPoolPrivate(this); + d = new StunTransactionPoolPrivate(this); d->mode = mode; } StunTransactionPool::~StunTransactionPool() { - qDeleteAll(findChildren()); // early remove of binding since they require alive pool (should fix one crash) + qDeleteAll( + findChildren()); // early remove of binding since they require alive pool (should fix one crash) delete d; } -StunTransaction::Mode StunTransactionPool::mode() const -{ - return d->mode; -} +StunTransaction::Mode StunTransactionPool::mode() const { return d->mode; } -bool StunTransactionPool::writeIncomingMessage(const StunMessage &msg, const QHostAddress &addr, int port) +bool StunTransactionPool::writeIncomingMessage(const StunMessage &msg, const TransportAddress &addr) { - if(d->debugLevel >= DL_Packet) - { + if (d->debugLevel >= DL_Packet) { QString str = "STUN RECV"; - if(!addr.isNull()) - str += QString(" from=(") + addr.toString() + ';' + QString::number(port) + ')'; + if (addr.isValid()) + str += QString(" from=(%1)").arg(addr); emit debugLine(str); emit debugLine(StunTypes::print_packet_str(msg)); } - QByteArray id = QByteArray::fromRawData((const char *)msg.id(), 12); + QByteArray id = QByteArray::fromRawData((const char *)msg.id(), 12); StunMessage::Class mclass = msg.mclass(); - if(mclass != StunMessage::SuccessResponse && mclass != StunMessage::ErrorResponse) + if (mclass != StunMessage::SuccessResponse && mclass != StunMessage::ErrorResponse) return false; StunTransaction *trans = d->idToTrans.value(id); - if(!trans) + if (!trans) return false; - return trans->d->writeIncomingMessage(msg, addr, port); + return trans->d->writeIncomingMessage(msg, addr); } -bool StunTransactionPool::writeIncomingMessage(const QByteArray &packet, bool *notStun, const QHostAddress &addr, int port) +bool StunTransactionPool::writeIncomingMessage(const QByteArray &packet, bool *notStun, const TransportAddress &addr) { - if(!StunMessage::isProbablyStun(packet)) - { + if (!StunMessage::isProbablyStun(packet)) { // basic stun check failed? surely not STUN - if(notStun) + if (notStun) *notStun = true; return false; } - if(d->debugLevel >= DL_Packet) - { + if (d->debugLevel >= DL_Packet) { StunMessage msg = StunMessage::fromBinary(packet); - QString str = "STUN RECV"; - if(!addr.isNull()) - str += QString(" from=(") + addr.toString() + ';' + QString::number(port) + ')'; + QString str = "STUN RECV"; + if (addr.isValid()) + str += QString(" from=(%1)").arg(addr); emit debugLine(str); emit debugLine(StunTypes::print_packet_str(msg)); } @@ -649,59 +585,41 @@ bool StunTransactionPool::writeIncomingMessage(const QByteArray &packet, bool *n StunMessage::Class mclass = StunMessage::extractClass(packet); - if(mclass != StunMessage::SuccessResponse && mclass != StunMessage::ErrorResponse) - { + if (mclass != StunMessage::SuccessResponse && mclass != StunMessage::ErrorResponse) { // could be STUN, don't really know for sure - if(notStun) + if (notStun) *notStun = false; return false; } StunTransaction *trans = d->idToTrans.value(id); - if(!trans) - { + if (!trans) { // could be STUN, don't really know for sure - if(notStun) + if (notStun) *notStun = false; return false; } bool _notStun = false; - bool ret = trans->d->writeIncomingMessage(packet, &_notStun, addr, port); - if(!ret && notStun) + bool ret = trans->d->writeIncomingMessage(packet, &_notStun, addr); + if (!ret && notStun) *notStun = _notStun; return ret; } -void StunTransactionPool::setLongTermAuthEnabled(bool enabled) -{ - d->useLongTermAuth = enabled; -} +void StunTransactionPool::setLongTermAuthEnabled(bool enabled) { d->useLongTermAuth = enabled; } -QString StunTransactionPool::realm() const -{ - return d->realm; -} +QString StunTransactionPool::realm() const { return d->realm; } -void StunTransactionPool::setUsername(const QString &username) -{ - d->user = username; -} +void StunTransactionPool::setUsername(const QString &username) { d->user = username; } -void StunTransactionPool::setPassword(const QCA::SecureArray &password) -{ - d->pass = password; -} +void StunTransactionPool::setPassword(const QCA::SecureArray &password) { d->pass = password; } -void StunTransactionPool::setRealm(const QString &realm) -{ - d->realm = realm; -} +void StunTransactionPool::setRealm(const QString &realm) { d->realm = realm; } -void StunTransactionPool::continueAfterParams() +void StunTransactionPool::continueAfterParams(const TransportAddress &addr) { - if(d->debugLevel >= DL_Info) - { + if (d->debugLevel >= DL_Info) { emit debugLine("continue after params:"); emit debugLine(QString(" U=[%1]").arg(d->user)); emit debugLine(QString(" P=[%1]").arg(d->pass.data())); @@ -711,35 +629,26 @@ void StunTransactionPool::continueAfterParams() Q_ASSERT(d->useLongTermAuth); Q_ASSERT(d->needLongTermAuth); - Q_ASSERT(!d->triedLongTermAuth); + Q_ASSERT(!d->triedLongTermAuth.contains(addr)); d->needLongTermAuth = false; - d->triedLongTermAuth = true; + d->triedLongTermAuth.insert(addr); - foreach(StunTransaction *trans, d->transactions) - { + for (StunTransaction *trans : std::as_const(d->transactions)) { // the only reason an inactive transaction would be in the // list is if it is waiting for an auth retry - if(!trans->d->active) - { + if (!trans->d->active && !trans->d->cancelling) { // use queued call to prevent all sorts of DOR-SS // nastiness - QMetaObject::invokeMethod(trans->d, "continueAfterParams", - Qt::QueuedConnection); + QMetaObject::invokeMethod(trans->d, "continueAfterParams", Qt::QueuedConnection); } } } -QByteArray StunTransactionPool::generateId() const -{ - return d->generateId(); -} +QByteArray StunTransactionPool::generateId() const { return d->generateId(); } -void StunTransactionPool::setDebugLevel(DebugLevel level) -{ - d->debugLevel = level; -} +void StunTransactionPool::setDebugLevel(DebugLevel level) { d->debugLevel = level; } -} +} // namespace XMPP #include "stuntransaction.moc" diff --git a/src/irisnet/noncore/stuntransaction.h b/src/irisnet/noncore/stuntransaction.h index badcd512..05558c12 100644 --- a/src/irisnet/noncore/stuntransaction.h +++ b/src/irisnet/noncore/stuntransaction.h @@ -19,21 +19,21 @@ #ifndef STUNTRANSACTION_H #define STUNTRANSACTION_H -#include #include -#include +#include +#include + +#include "transportaddress.h" namespace QCA { - class SecureArray; +class SecureArray; } namespace XMPP { - class StunMessage; - -class StunTransactionPrivate; class StunTransactionPool; class StunTransactionPoolPrivate; +class StunTransactionPrivate; // Notes: // @@ -59,31 +59,26 @@ class StunTransactionPoolPrivate; // - if short term or long term auth is used, then the request is authenticated // and the response is required to be authenticated. -class StunTransaction : public QObject -{ +class StunTransaction : public QObject { Q_OBJECT public: - enum Mode - { + enum Mode { Udp, // handle retransmissions Tcp // send once }; - enum Error - { - ErrorGeneric, - ErrorTimeout - }; + enum Error { ErrorGeneric, ErrorTimeout }; - StunTransaction(QObject *parent = 0); + StunTransaction(QObject *parent = nullptr); ~StunTransaction(); // toAddress/toPort are optional, to associate this request to a // specific endpoint // note: not DOR-DS safe. this function will cause the pool's // outgoingMessage() signal to be emitted. - void start(StunTransactionPool *pool, const QHostAddress &toAddress = QHostAddress(), int toPort = -1); + void start(StunTransactionPool *pool, const TransportAddress &toAddress = TransportAddress()); + void cancel(); // pass message with class unset. use transaction id from the // createMessage signal. @@ -128,19 +123,15 @@ class StunTransaction : public QObject // emitted as a direct result of calling certain member functions of this // class as well as any other class that might use it (such as StunBinding). // so, be careful with what you do in your retransmit slot. -class StunTransactionPool : public QObject -{ +class StunTransactionPool : public QObject, public QEnableSharedFromThis { Q_OBJECT public: - enum DebugLevel - { - DL_None, - DL_Info, - DL_Packet - }; + using Ptr = QSharedPointer; - StunTransactionPool(StunTransaction::Mode mode, QObject *parent = 0); + enum DebugLevel { DL_None, DL_Info, DL_Packet }; + + StunTransactionPool(StunTransaction::Mode mode); ~StunTransactionPool(); StunTransaction::Mode mode() const; @@ -149,7 +140,7 @@ class StunTransactionPool : public QObject // may cause a transaction to emit finished() or error() signals. // returns true if the message is owned by the pool, else false. - bool writeIncomingMessage(const StunMessage &msg, const QHostAddress &addr = QHostAddress(), int port = -1); + bool writeIncomingMessage(const StunMessage &msg, const TransportAddress &addr = TransportAddress()); // returns true if the packet is surely a STUN message and owned by the // pool, else false. a packet must be owned by the pool to be @@ -157,15 +148,16 @@ class StunTransactionPool : public QObject // not be a STUN message. *notStun will be set to true if the packet // is surely not STUN, or set to false if it is unclear whether the // packet is STUN or not. - bool writeIncomingMessage(const QByteArray &packet, bool *notStun = 0, const QHostAddress &addr = QHostAddress(), int port = -1); + bool writeIncomingMessage(const QByteArray &packet, bool *notStun = nullptr, + const TransportAddress &addr = TransportAddress()); void setLongTermAuthEnabled(bool enabled); QString realm() const; - void setUsername(const QString &username); - void setPassword(const QCA::SecureArray &password); - void setRealm(const QString &realm); - void continueAfterParams(); + void setUsername(const QString &username); + void setPassword(const QCA::SecureArray &password); + void setRealm(const QString &realm); + void continueAfterParams(const TransportAddress &addr); // for use with stun indications QByteArray generateId() const; @@ -183,9 +175,9 @@ class StunTransactionPool : public QObject // writeIncomingMessage() during outgoingMessage() could cause // a transaction's finished() or error() signals to emit during // start(), which would violate DOR-DS. - void outgoingMessage(const QByteArray &packet, const QHostAddress &addr, int port); + void outgoingMessage(const QByteArray &packet, const TransportAddress &addr); - void needAuthParams(); + void needAuthParams(const TransportAddress &); // not DOR-SS/DS safe void debugLine(const QString &line); @@ -199,7 +191,6 @@ class StunTransactionPool : public QObject friend class StunTransactionPoolPrivate; StunTransactionPoolPrivate *d; }; +} // namespace XMPP -} - -#endif +#endif // STUNTRANSACTION_H diff --git a/src/irisnet/noncore/stuntypes.cpp b/src/irisnet/noncore/stuntypes.cpp index 0e730d4c..4a35daff 100644 --- a/src/irisnet/noncore/stuntypes.cpp +++ b/src/irisnet/noncore/stuntypes.cpp @@ -18,603 +18,523 @@ #include "stuntypes.h" -#include -#include #include "stunutil.h" +#include "transportaddress.h" + +#include +#include #define STRING_MAX_CHARS 127 #define STRING_MAX_BYTES 763 namespace XMPP { - using namespace StunUtil; - namespace StunTypes { + static void xorIPv4(QByteArray *in, const quint8 *magic) + { + quint8 *p = (quint8 *)in->data(); + p[2] ^= magic[0]; + p[3] ^= magic[1]; + for (int n = 0; n < 4; ++n) + p[n + 4] ^= magic[n]; + } -static void xorIPv4(QByteArray *in, const quint8 *magic) -{ - quint8 *p = (quint8 *)in->data(); - p[2] ^= magic[0]; - p[3] ^= magic[1]; - for(int n = 0; n < 4; ++n) - p[n + 4] ^= magic[n]; -} - -static void xorIPv6(QByteArray *in, const quint8 *magic, const quint8 *id) -{ - quint8 *p = (quint8 *)in->data(); - p[2] ^= magic[0]; - p[3] ^= magic[1]; - for(int n = 0; n < 4; ++n) - p[n + 4] ^= magic[n]; - for(int n = 0; n < 12; ++n) - p[n + 8] ^= id[n]; -} - -static bool validateString(const QByteArray &in, QString *out) -{ - if(in.size() <= STRING_MAX_BYTES) - { - QString s = QString::fromUtf8(in); - if(s.length() <= STRING_MAX_CHARS) - { - *out = s; - return true; + static void xorIPv6(QByteArray *in, const quint8 *magic, const quint8 *id) + { + quint8 *p = (quint8 *)in->data(); + p[2] ^= magic[0]; + p[3] ^= magic[1]; + for (int n = 0; n < 4; ++n) + p[n + 4] ^= magic[n]; + for (int n = 0; n < 12; ++n) + p[n + 8] ^= id[n]; + } + + static bool validateString(const QByteArray &in, QString *out) + { + if (in.size() <= STRING_MAX_BYTES) { + QString s = QString::fromUtf8(in); + if (s.length() <= STRING_MAX_CHARS) { + *out = s; + return true; + } } + + return false; } - return false; -} - -QByteArray createMappedAddress(const QHostAddress &addr, quint16 port) -{ - QByteArray out; - - if(addr.protocol() == QAbstractSocket::IPv6Protocol) - { - out = QByteArray(20, 0); - out[1] = 0x02; // IPv6 - Q_IPV6ADDR addr6 = addr.toIPv6Address(); - memcpy(out.data() + 4, addr6.c, 16); - } - else if(addr.protocol() == QAbstractSocket::IPv4Protocol) - { - out = QByteArray(8, 0); - out[1] = 0x01; // IPv4 - write32((quint8 *)out.data() + 4, addr.toIPv4Address()); - } - else - Q_ASSERT(0); - - write16((quint8 *)out.data() + 2, port); - return out; -} - -QByteArray createUsername(const QString &username) -{ - return username.left(STRING_MAX_CHARS).toUtf8(); -} - -QByteArray createErrorCode(int code, const QString &reason) -{ - QByteArray out(4, 0); - - int ih = code / 100; - int il = code % 100; - ih &= 0x07; // keep only lower 3 bits - - unsigned char ch = (unsigned char)ih; - unsigned char cl = (unsigned char)il; - out[2] = ch; - out[3] = cl; - out += reason.left(STRING_MAX_CHARS).toUtf8(); - return out; -} - -QByteArray createUnknownAttributes(const QList &typeList) -{ - if(typeList.isEmpty()) - return QByteArray(); - - QByteArray out(typeList.count() * 2, 0); - for(int n = 0; n < typeList.count(); ++n) - write16((quint8 *)out.data() + (n * 2), typeList[n]); - return out; -} - -QByteArray createRealm(const QString &realm) -{ - return realm.left(STRING_MAX_CHARS).toUtf8(); -} - -QByteArray createNonce(const QString &nonce) -{ - return nonce.left(STRING_MAX_CHARS).toUtf8(); -} - -QByteArray createXorMappedAddress(const QHostAddress &addr, quint16 port, const quint8 *magic, const quint8 *id) -{ - QByteArray out = createMappedAddress(addr, port); - if(addr.protocol() == QAbstractSocket::IPv6Protocol) - xorIPv6(&out, magic, id); - else // IPv4 - xorIPv4(&out, magic); - return out; -} - -QByteArray createChannelNumber(quint16 i) -{ - QByteArray val(4, 0); - write16((quint8 *)val.data(), i); - // bytes 2-3 are zeroed out - return val; -} - -QByteArray createLifetime(quint32 i) -{ - QByteArray val(4, 0); - write32((quint8 *)val.data(), i); - return val; -} - -QByteArray createXorPeerAddress(const QHostAddress &addr, quint16 port, const quint8 *magic, const quint8 *id) -{ - return createXorMappedAddress(addr, port, magic, id); -} - -QByteArray createXorRelayedAddress(const QHostAddress &addr, quint16 port, const quint8 *magic, const quint8 *id) -{ - return createXorMappedAddress(addr, port, magic, id); -} - -QByteArray createEvenPort(bool reserve) -{ - QByteArray val(1, 0); - unsigned char c = 0; - if(reserve) - c |= 0x80; // set high bit - val[0] = c; - return val; -} - -QByteArray createRequestedTransport(quint8 proto) -{ - QByteArray val(4, 0); - val[0] = proto; - // bytes 1-3 are zeroed out - return val; -} - -QByteArray createReservationToken(const QByteArray &token) -{ - Q_ASSERT(token.size() == 8); - return token; -} - -QByteArray createPriority(quint32 i) -{ - QByteArray val(4, 0); - write32((quint8 *)val.data(), i); - return val; -} - -QByteArray createSoftware(const QString &str) -{ - return str.left(STRING_MAX_CHARS).toUtf8(); -} - -QByteArray createAlternateServer(const QHostAddress &addr, quint16 port) -{ - return createMappedAddress(addr, port); -} - -QByteArray createIceControlled(quint64 i) -{ - QByteArray val(8, 0); - write64((quint8 *)val.data(), i); - return val; -} - -QByteArray createIceControlling(quint64 i) -{ - QByteArray val(8, 0); - write64((quint8 *)val.data(), i); - return val; -} - -bool parseMappedAddress(const QByteArray &val, QHostAddress *addr, quint16 *port) -{ - if(val[1] == 0x02 && val.size() == 20) // IPv6 - { - *port = read16((const quint8 *)val.data() + 2); - QByteArray buf = val.mid(4); - *addr = QHostAddress((quint8 *)buf.data()); - return true; + QByteArray createMappedAddress(const TransportAddress &addr) + { + QByteArray out; + + if (addr.addr.protocol() == QAbstractSocket::IPv6Protocol) { + out = QByteArray(20, 0); + out[1] = 0x02; // IPv6 + Q_IPV6ADDR addr6 = addr.addr.toIPv6Address(); + memcpy(out.data() + 4, addr6.c, 16); + } else if (addr.addr.protocol() == QAbstractSocket::IPv4Protocol) { + out = QByteArray(8, 0); + out[1] = 0x01; // IPv4 + write32((quint8 *)out.data() + 4, addr.addr.toIPv4Address()); + } else + Q_ASSERT(0); + + write16((quint8 *)out.data() + 2, addr.port); + return out; } - else if(val[1] == 0x01 && val.size() == 8) // IPv4 + + QByteArray createUsername(const QString &username) { return username.left(STRING_MAX_CHARS).toUtf8(); } + + QByteArray createErrorCode(int code, const QString &reason) { - *port = read16((const quint8 *)val.data() + 2); - *addr = QHostAddress(read32((const quint8 *)val.data() + 4)); - return true; + QByteArray out(4, 0); + + int ih = code / 100; + int il = code % 100; + ih &= 0x07; // keep only lower 3 bits + + unsigned char ch = (unsigned char)ih; + unsigned char cl = (unsigned char)il; + out[2] = ch; + out[3] = cl; + out += reason.left(STRING_MAX_CHARS).toUtf8(); + return out; } - else - return false; -} -bool parseUsername(const QByteArray &val, QString *username) -{ - return validateString(val, username); -} + QByteArray createUnknownAttributes(const QList &typeList) + { + if (typeList.isEmpty()) + return QByteArray(); + + QByteArray out(typeList.count() * 2, 0); + for (int n = 0; n < typeList.count(); ++n) + write16((quint8 *)out.data() + (n * 2), typeList[n]); + return out; + } -bool parseErrorCode(const QByteArray &val, int *code, QString *reason) -{ - if(val.size() < 4) - return false; + QByteArray createRealm(const QString &realm) { return realm.left(STRING_MAX_CHARS).toUtf8(); } - unsigned char ch = (unsigned char)val[2]; - unsigned char cl = (unsigned char)val[3]; - int ih = ch & 0x07; // lower 3 bits - int x = ih * 100 + (int)cl; + QByteArray createNonce(const QString &nonce) { return nonce.left(STRING_MAX_CHARS).toUtf8(); } - QString str; - if(validateString(val.mid(4), &str)) + QByteArray createXorMappedAddress(const TransportAddress &addr, const quint8 *magic, const quint8 *id) { - *code = x; - *reason = str; - return true; + QByteArray out = createMappedAddress(addr); + if (addr.addr.protocol() == QAbstractSocket::IPv6Protocol) + xorIPv6(&out, magic, id); + else // IPv4 + xorIPv4(&out, magic); + return out; } - return false; -} + QByteArray createChannelNumber(quint16 i) + { + QByteArray val(4, 0); + write16((quint8 *)val.data(), i); + // bytes 2-3 are zeroed out + return val; + } -bool parseUnknownAttributes(const QByteArray &val, QList *typeList) -{ - if(val.size() % 2 != 0) - return false; + QByteArray createLifetime(quint32 i) + { + QByteArray val(4, 0); + write32((quint8 *)val.data(), i); + return val; + } - typeList->clear(); - int count = val.size() / 2; - for(int n = 0; n < count; ++n) - typeList->append(read16((const quint8 *)val.data() + (n * 2))); - return true; -} - -bool parseRealm(const QByteArray &val, QString *realm) -{ - return validateString(val, realm); -} - -bool parseNonce(const QByteArray &val, QString *nonce) -{ - return validateString(val, nonce); -} - -bool parseXorMappedAddress(const QByteArray &val, const quint8 *magic, const quint8 *id, QHostAddress *addr, quint16 *port) -{ - if(val.size() < 4) - return false; + QByteArray createXorPeerAddress(const TransportAddress &addr, const quint8 *magic, const quint8 *id) + { + return createXorMappedAddress(addr, magic, id); + } - QByteArray buf; - if(val[1] == 0x02 && val.size() == 20) // IPv6 + QByteArray createXorRelayedAddress(const TransportAddress &addr, const quint8 *magic, const quint8 *id) { - buf = val; - xorIPv6(&buf, magic, id); + return createXorMappedAddress(addr, magic, id); } - else if(val[1] == 0x01 && val.size() == 8) // IPv4 + + QByteArray createEvenPort(bool reserve) { - buf = val; - xorIPv4(&buf, magic); + QByteArray val(1, 0); + unsigned char c = 0; + if (reserve) + c |= 0x80; // set high bit + val[0] = c; + return val; } - else - return false; - return parseMappedAddress(buf, addr, port); -} + QByteArray createRequestedTransport(quint8 proto) + { + QByteArray val(4, 0); + val[0] = proto; + // bytes 1-3 are zeroed out + return val; + } -bool parseChannelNumber(const QByteArray &val, quint16 *i) -{ - if(val.size() != 4) - return false; + QByteArray createReservationToken(const QByteArray &token) + { + Q_ASSERT(token.size() == 8); + return token; + } - const quint8 *p = (const quint8 *)val.data(); - *i = read16(p); - return true; -} + QByteArray createPriority(quint32 i) + { + QByteArray val(4, 0); + write32((quint8 *)val.data(), i); + return val; + } -bool parseLifetime(const QByteArray &val, quint32 *i) -{ - if(val.size() != 4) - return false; + QByteArray createSoftware(const QString &str) { return str.left(STRING_MAX_CHARS).toUtf8(); } - const quint8 *p = (const quint8 *)val.data(); - *i = read32(p); - return true; -} + QByteArray createAlternateServer(const TransportAddress &addr) { return createMappedAddress(addr); } -bool parseXorPeerAddress(const QByteArray &val, const quint8 *magic, const quint8 *id, QHostAddress *addr, quint16 *port) -{ - return parseXorMappedAddress(val, magic, id, addr, port); -} + QByteArray createIceControlled(quint64 i) + { + QByteArray val(8, 0); + write64((quint8 *)val.data(), i); + return val; + } -bool parseXorRelayedAddress(const QByteArray &val, const quint8 *magic, const quint8 *id, QHostAddress *addr, quint16 *port) -{ - return parseXorMappedAddress(val, magic, id, addr, port); -} + QByteArray createIceControlling(quint64 i) + { + QByteArray val(8, 0); + write64((quint8 *)val.data(), i); + return val; + } -bool parseEvenPort(const QByteArray &val, bool *reserve) -{ - if(val.size() != 1) - return false; + bool parseMappedAddress(const QByteArray &val, TransportAddress &addr) + { + if (val[1] == 0x02 && val.size() == 20) // IPv6 + { + addr.port = read16((const quint8 *)val.data() + 2); + QByteArray buf = val.mid(4); + addr.addr = QHostAddress((quint8 *)buf.data()); + return true; + } else if (val[1] == 0x01 && val.size() == 8) // IPv4 + { + addr.port = read16((const quint8 *)val.data() + 2); + addr.addr = QHostAddress(read32((const quint8 *)val.data() + 4)); + return true; + } else + return false; + } - unsigned char c = val[0]; - if(c & 0x80) - *reserve = true; - else - *reserve = false; + bool parseUsername(const QByteArray &val, QString *username) { return validateString(val, username); } - return true; -} + bool parseErrorCode(const QByteArray &val, int *code, QString *reason) + { + if (val.size() < 4) + return false; + + unsigned char ch = (unsigned char)val[2]; + unsigned char cl = (unsigned char)val[3]; + int ih = ch & 0x07; // lower 3 bits + int x = ih * 100 + (int)cl; + + QString str; + if (validateString(val.mid(4), &str)) { + *code = x; + *reason = str; + return true; + } -bool parseRequestedTransport(const QByteArray &val, quint8 *proto) -{ - if(val.size() != 4) return false; + } - *proto = val[0]; - return true; -} + bool parseUnknownAttributes(const QByteArray &val, QList *typeList) + { + if (val.size() % 2 != 0) + return false; -bool parseReservationToken(const QByteArray &val, QByteArray *token) -{ - if(val.size() != 8) - return false; + typeList->clear(); + int count = val.size() / 2; + for (int n = 0; n < count; ++n) + typeList->append(read16((const quint8 *)val.data() + (n * 2))); + return true; + } - *token = val; - return true; -} + bool parseRealm(const QByteArray &val, QString *realm) { return validateString(val, realm); } -bool parsePriority(const QByteArray &val, quint32 *i) -{ - if(val.size() != 4) - return false; + bool parseNonce(const QByteArray &val, QString *nonce) { return validateString(val, nonce); } - const quint8 *p = (const quint8 *)val.data(); - *i = read32(p); - return true; -} - -bool parseSoftware(const QByteArray &val, QString *str) -{ - *str = QString::fromUtf8(val); - return true; -} - -bool parseAlternateServer(const QByteArray &val, QHostAddress *addr, quint16 *port) -{ - return parseMappedAddress(val, addr, port); -} - -bool parseIceControlled(const QByteArray &val, quint64 *i) -{ - if(val.size() != 8) - return false; + bool parseXorMappedAddress(const QByteArray &val, const quint8 *magic, const quint8 *id, TransportAddress &addr) + { + if (val.size() < 4) + return false; - const quint8 *p = (const quint8 *)val.data(); - *i = read64(p); - return true; -} + QByteArray buf; + if (val[1] == 0x02 && val.size() == 20) // IPv6 + { + buf = val; + xorIPv6(&buf, magic, id); + } else if (val[1] == 0x01 && val.size() == 8) // IPv4 + { + buf = val; + xorIPv4(&buf, magic); + } else + return false; -bool parseIceControlling(const QByteArray &val, quint64 *i) -{ - if(val.size() != 8) - return false; + return parseMappedAddress(buf, addr); + } - const quint8 *p = (const quint8 *)val.data(); - *i = read64(p); - return true; -} - -#define METHOD_ENTRY(x) \ - { x, #x } - -struct MethodEntry -{ - Method method; - const char *str; -} method_table[] = -{ - METHOD_ENTRY(Binding), - METHOD_ENTRY(Allocate), - METHOD_ENTRY(Refresh), - METHOD_ENTRY(Send), - METHOD_ENTRY(Data), - METHOD_ENTRY(CreatePermission), - METHOD_ENTRY(ChannelBind), - { (Method)-1, 0 } -}; - -QString methodToString(int method) -{ - for(int n = 0; method_table[n].str; ++n) - { - if(method_table[n].method == (Method)method) - return QString::fromLatin1(method_table[n].str); - } - - return QString(); -} - -#define ATTRIB_ENTRY(x) \ - { x, #x } - -struct AttribEntry -{ - Attribute type; - const char *str; -} attrib_table[] = -{ - ATTRIB_ENTRY(MAPPED_ADDRESS), - ATTRIB_ENTRY(USERNAME), - ATTRIB_ENTRY(MESSAGE_INTEGRITY), - ATTRIB_ENTRY(ERROR_CODE), - ATTRIB_ENTRY(UNKNOWN_ATTRIBUTES), - ATTRIB_ENTRY(REALM), - ATTRIB_ENTRY(NONCE), - ATTRIB_ENTRY(XOR_MAPPED_ADDRESS), - ATTRIB_ENTRY(CHANNEL_NUMBER), - ATTRIB_ENTRY(LIFETIME), - ATTRIB_ENTRY(XOR_PEER_ADDRESS), - ATTRIB_ENTRY(DATA), - ATTRIB_ENTRY(XOR_RELAYED_ADDRESS), - ATTRIB_ENTRY(EVEN_PORT), - ATTRIB_ENTRY(REQUESTED_TRANSPORT), - ATTRIB_ENTRY(DONT_FRAGMENT), - ATTRIB_ENTRY(RESERVATION_TOKEN), - ATTRIB_ENTRY(PRIORITY), - ATTRIB_ENTRY(USE_CANDIDATE), - ATTRIB_ENTRY(SOFTWARE), - ATTRIB_ENTRY(ALTERNATE_SERVER), - ATTRIB_ENTRY(FINGERPRINT), - ATTRIB_ENTRY(ICE_CONTROLLED), - ATTRIB_ENTRY(ICE_CONTROLLING), - { (Attribute)-1, 0 } -}; - -QString attributeTypeToString(int type) -{ - for(int n = 0; attrib_table[n].str; ++n) - { - if(attrib_table[n].type == (Attribute)type) - { - QString name = QString::fromLatin1(attrib_table[n].str); - name.replace('_', '-'); - return name; + bool parseChannelNumber(const QByteArray &val, quint16 *i) + { + if (val.size() != 4) + return false; + + const quint8 *p = (const quint8 *)val.data(); + *i = read16(p); + return true; + } + + bool parseLifetime(const QByteArray &val, quint32 *i) + { + if (val.size() != 4) + return false; + + const quint8 *p = (const quint8 *)val.data(); + *i = read32(p); + return true; + } + + bool parseXorPeerAddress(const QByteArray &val, const quint8 *magic, const quint8 *id, TransportAddress &addr) + { + return parseXorMappedAddress(val, magic, id, addr); + } + + bool parseXorRelayedAddress(const QByteArray &val, const quint8 *magic, const quint8 *id, TransportAddress &addr) + { + return parseXorMappedAddress(val, magic, id, addr); + } + + bool parseEvenPort(const QByteArray &val, bool *reserve) + { + if (val.size() != 1) + return false; + + unsigned char c = static_cast(val[0]); + *reserve = (c & 0x80) != 0; + + return true; + } + + bool parseRequestedTransport(const QByteArray &val, quint8 *proto) + { + if (val.size() != 4) + return false; + + *proto = quint8(val[0]); + return true; + } + + bool parseReservationToken(const QByteArray &val, QByteArray *token) + { + if (val.size() != 8) + return false; + + *token = val; + return true; + } + + bool parsePriority(const QByteArray &val, quint32 *i) + { + if (val.size() != 4) + return false; + + const quint8 *p = (const quint8 *)val.data(); + *i = read32(p); + return true; + } + + bool parseSoftware(const QByteArray &val, QString *str) + { + *str = QString::fromUtf8(val); + return true; + } + + bool parseAlternateServer(const QByteArray &val, TransportAddress &addr) { return parseMappedAddress(val, addr); } + + bool parseIceControlled(const QByteArray &val, quint64 *i) + { + if (val.size() != 8) + return false; + + const quint8 *p = (const quint8 *)val.data(); + *i = read64(p); + return true; + } + + bool parseIceControlling(const QByteArray &val, quint64 *i) + { + if (val.size() != 8) + return false; + + const quint8 *p = (const quint8 *)val.data(); + *i = read64(p); + return true; + } + +#define METHOD_ENTRY(x) \ + { \ + x, #x \ + } + + struct MethodEntry { + Method method; + const char *str; + } method_table[] + = { METHOD_ENTRY(Binding), METHOD_ENTRY(Allocate), METHOD_ENTRY(Refresh), METHOD_ENTRY(Send), + METHOD_ENTRY(Data), METHOD_ENTRY(CreatePermission), METHOD_ENTRY(ChannelBind), { (Method)-1, nullptr } }; + + QString methodToString(int method) + { + for (int n = 0; method_table[n].str; ++n) { + if (method_table[n].method == (Method)method) + return QString::fromLatin1(method_table[n].str); } + + return QString(); } - return QString(); -} +#define ATTRIB_ENTRY(x) \ + { \ + x, #x \ + } -static QString quoted(const QString &in) -{ - return QString("\"") + in + '\"'; -} + struct AttribEntry { + Attribute type; + const char *str; + } attrib_table[] = { ATTRIB_ENTRY(MAPPED_ADDRESS), + ATTRIB_ENTRY(USERNAME), + ATTRIB_ENTRY(MESSAGE_INTEGRITY), + ATTRIB_ENTRY(ERROR_CODE), + ATTRIB_ENTRY(UNKNOWN_ATTRIBUTES), + ATTRIB_ENTRY(REALM), + ATTRIB_ENTRY(NONCE), + ATTRIB_ENTRY(XOR_MAPPED_ADDRESS), + ATTRIB_ENTRY(CHANNEL_NUMBER), + ATTRIB_ENTRY(LIFETIME), + ATTRIB_ENTRY(XOR_PEER_ADDRESS), + ATTRIB_ENTRY(DATA), + ATTRIB_ENTRY(XOR_RELAYED_ADDRESS), + ATTRIB_ENTRY(EVEN_PORT), + ATTRIB_ENTRY(REQUESTED_TRANSPORT), + ATTRIB_ENTRY(DONT_FRAGMENT), + ATTRIB_ENTRY(RESERVATION_TOKEN), + ATTRIB_ENTRY(PRIORITY), + ATTRIB_ENTRY(USE_CANDIDATE), + ATTRIB_ENTRY(SOFTWARE), + ATTRIB_ENTRY(ALTERNATE_SERVER), + ATTRIB_ENTRY(FINGERPRINT), + ATTRIB_ENTRY(ICE_CONTROLLED), + ATTRIB_ENTRY(ICE_CONTROLLING), + { (Attribute)-1, nullptr } }; + + QString attributeTypeToString(int type) + { + for (int n = 0; attrib_table[n].str; ++n) { + if (attrib_table[n].type == (Attribute)type) { + QString name = QString::fromLatin1(attrib_table[n].str); + name.replace('_', '-'); + return name; + } + } -QString attributeValueToString(int type, const QByteArray &val, const quint8 *magic, const quint8 *id) -{ - switch((Attribute)type) + return QString(); + } + + static QString quoted(const QString &in) { return QString("\"") + in + '\"'; } + + QString attributeValueToString(int type, const QByteArray &val, const quint8 *magic, const quint8 *id) { - case MAPPED_ADDRESS: - { - QHostAddress addr; - quint16 port; - if(parseMappedAddress(val, &addr, &port)) - return addr.toString() + ';' + QString::number(port); + switch ((Attribute)type) { + case MAPPED_ADDRESS: { + TransportAddress addr; + if (parseMappedAddress(val, addr)) + return QString(addr); break; } - case USERNAME: - { + case USERNAME: { QString str; - if(parseUsername(val, &str)) + if (parseUsername(val, &str)) return quoted(str); break; } - case MESSAGE_INTEGRITY: - { + case MESSAGE_INTEGRITY: { return QCA::arrayToHex(val); } - case ERROR_CODE: - { - int code; + case ERROR_CODE: { + int code; QString reason; - if(parseErrorCode(val, &code, &reason)) - { + if (parseErrorCode(val, &code, &reason)) { QString out = QString::number(code); - if(!reason.isEmpty()) + if (!reason.isEmpty()) out += QString(", ") + quoted(reason); return out; } break; } - case UNKNOWN_ATTRIBUTES: - { + case UNKNOWN_ATTRIBUTES: { QList typeList; - if(parseUnknownAttributes(val, &typeList)) - { - if(!typeList.isEmpty()) - { + if (parseUnknownAttributes(val, &typeList)) { + if (!typeList.isEmpty()) { QStringList strList; - foreach(quint16 i, typeList) - strList += QString().sprintf("0x%04x", i); + for (quint16 i : std::as_const(typeList)) + strList += QString::asprintf("0x%04x", i); return strList.join(", "); - } - else + } else return "(None)"; } break; } - case REALM: - { + case REALM: { QString str; - if(parseRealm(val, &str)) + if (parseRealm(val, &str)) return quoted(str); break; } - case NONCE: - { + case NONCE: { QString str; - if(parseNonce(val, &str)) + if (parseNonce(val, &str)) return quoted(str); break; } - case XOR_MAPPED_ADDRESS: - { - QHostAddress addr; - quint16 port; - if(parseXorMappedAddress(val, magic, id, &addr, &port)) - return addr.toString() + ';' + QString::number(port); + case XOR_MAPPED_ADDRESS: { + TransportAddress addr; + if (parseXorMappedAddress(val, magic, id, addr)) + return QString(addr); break; } - case CHANNEL_NUMBER: - { + case CHANNEL_NUMBER: { quint16 i; - if(parseChannelNumber(val, &i)) - return QString().sprintf("0x%04x", (int)i); + if (parseChannelNumber(val, &i)) + return QString::asprintf("0x%04x", (int)i); break; } - case LIFETIME: - { + case LIFETIME: { quint32 i; - if(parseLifetime(val, &i)) + if (parseLifetime(val, &i)) return QString::number(i); break; } - case XOR_PEER_ADDRESS: - { + case XOR_PEER_ADDRESS: { return attributeValueToString(XOR_MAPPED_ADDRESS, val, magic, id); } - case DATA: - { + case DATA: { return QString("len=%1, ").arg(val.size()) + QCA::arrayToHex(val); } - case XOR_RELAYED_ADDRESS: - { + case XOR_RELAYED_ADDRESS: { return attributeValueToString(XOR_MAPPED_ADDRESS, val, magic, id); } - case EVEN_PORT: - { + case EVEN_PORT: { bool reserve; - if(parseEvenPort(val, &reserve)) + if (parseEvenPort(val, &reserve)) return QString("reserve=") + (reserve ? "true" : "false"); break; } - case REQUESTED_TRANSPORT: - { + case REQUESTED_TRANSPORT: { quint8 proto; - if(parseRequestedTransport(val, &proto)) - { + if (parseRequestedTransport(val, &proto)) { QString str = QString::number((int)proto); - if(proto == 17) // UDP + if (proto == 17) // UDP str += " (UDP)"; else str += " (Unknown)"; @@ -622,115 +542,97 @@ QString attributeValueToString(int type, const QByteArray &val, const quint8 *ma } break; } - case DONT_FRAGMENT: - { + case DONT_FRAGMENT: { return QString(""); } - case RESERVATION_TOKEN: - { + case RESERVATION_TOKEN: { QByteArray token; - if(parseReservationToken(val, &token)) + if (parseReservationToken(val, &token)) return QCA::arrayToHex(token); break; } - case PRIORITY: - { + case PRIORITY: { quint32 i; - if(parsePriority(val, &i)) + if (parsePriority(val, &i)) return QString::number(i); break; } - case USE_CANDIDATE: - { + case USE_CANDIDATE: { return QString(""); } - case SOFTWARE: - { + case SOFTWARE: { QString out; - if(parseSoftware(val, &out)) + if (parseSoftware(val, &out)) return quoted(out); break; } - case ALTERNATE_SERVER: - { + case ALTERNATE_SERVER: { return attributeValueToString(MAPPED_ADDRESS, val, magic, id); } - case FINGERPRINT: - { + case FINGERPRINT: { return QCA::arrayToHex(val); } - case ICE_CONTROLLED: - { + case ICE_CONTROLLED: { quint64 i; - if(parseIceControlled(val, &i)) + if (parseIceControlled(val, &i)) return QString::number(i); break; } - case ICE_CONTROLLING: - { + case ICE_CONTROLLING: { quint64 i; - if(parseIceControlling(val, &i)) + if (parseIceControlling(val, &i)) return QString::number(i); break; } - } - - return QString(); -} - -QString print_packet_str(const StunMessage &message) -{ - QString out; - - QString mclass; - if(message.mclass() == StunMessage::Request) - mclass = "Request"; - else if(message.mclass() == StunMessage::SuccessResponse) - mclass = "Response (Success)"; - else if(message.mclass() == StunMessage::ErrorResponse) - mclass = "Response (Error)"; - else if(message.mclass() == StunMessage::Indication) - mclass = "Indication"; - else - Q_ASSERT(0); - - out += QString("Class: %1\n").arg(mclass); - out += QString("Method: %1\n").arg(methodToString(message.method())); - out += QString("Transaction id: %1\n").arg(QCA::arrayToHex(QByteArray((const char *)message.id(), 12))); - out += "Attributes:"; - QList attribs = message.attributes(); - if(!attribs.isEmpty()) - { - foreach(const StunMessage::Attribute &a, attribs) - { - out += '\n'; - - QString name = attributeTypeToString(a.type); - if(!name.isNull()) - { - QString val = attributeValueToString(a.type, a.value, message.magic(), message.id()); - if(val.isNull()) - val = QString("Unable to parse %1 bytes").arg(a.value.size()); - - out += QString(" %1").arg(name); - if(!val.isEmpty()) - out += QString(" = %1").arg(val); - } - else - out += QString().sprintf(" Unknown attribute (0x%04x) of %d bytes", a.type, a.value.size()); } - } - else - out += "\n (None)"; - return out; -} + return QString(); + } -void print_packet(const StunMessage &message) -{ - printf("%s\n", qPrintable(print_packet_str(message))); -} + QString print_packet_str(const StunMessage &message) + { + QString out; + + QString mclass; + if (message.mclass() == StunMessage::Request) + mclass = "Request"; + else if (message.mclass() == StunMessage::SuccessResponse) + mclass = "Response (Success)"; + else if (message.mclass() == StunMessage::ErrorResponse) + mclass = "Response (Error)"; + else if (message.mclass() == StunMessage::Indication) + mclass = "Indication"; + else + Q_ASSERT(0); + + out += QString("Class: %1\n").arg(mclass); + out += QString("Method: %1\n").arg(methodToString(message.method())); + out += QString("Transaction id: %1\n").arg(QCA::arrayToHex(QByteArray((const char *)message.id(), 12))); + out += "Attributes:"; + QList attribs = message.attributes(); + if (!attribs.isEmpty()) { + for (const StunMessage::Attribute &a : attribs) { + out += '\n'; + + QString name = attributeTypeToString(a.type); + if (!name.isNull()) { + QString val = attributeValueToString(a.type, a.value, message.magic(), message.id()); + if (val.isNull()) + val = QString("Unable to parse %1 bytes").arg(a.value.size()); + + out += QString(" %1").arg(name); + if (!val.isEmpty()) + out += QString(" = %1").arg(val); + } else + out += QString::asprintf(" Unknown attribute (0x%04x) of %lld bytes", a.type, + qsizetype(a.value.size())); + } + } else + out += "\n (None)"; -} + return out; + } -} + void print_packet(const StunMessage &message) { printf("%s\n", qPrintable(print_packet_str(message))); } +} // namespace StunTypes +} // namespace XMPP diff --git a/src/irisnet/noncore/stuntypes.h b/src/irisnet/noncore/stuntypes.h index e0179910..a2a0c15a 100644 --- a/src/irisnet/noncore/stuntypes.h +++ b/src/irisnet/noncore/stuntypes.h @@ -19,126 +19,144 @@ #ifndef STUNTYPES_H #define STUNTYPES_H -#include +#include "stunmessage.h" + #include -#include #include -#include "stunmessage.h" +#include +#include namespace XMPP { - +class TransportAddress; namespace StunTypes { - -enum Method -{ - Binding = 0x001, - Allocate = 0x003, - Refresh = 0x004, - Send = 0x006, - Data = 0x007, - CreatePermission = 0x008, - ChannelBind = 0x009 -}; - -enum Attribute -{ - MAPPED_ADDRESS = 0x0001, - USERNAME = 0x0006, - MESSAGE_INTEGRITY = 0x0008, - ERROR_CODE = 0x0009, - UNKNOWN_ATTRIBUTES = 0x000a, - REALM = 0x0014, - NONCE = 0x0015, - XOR_MAPPED_ADDRESS = 0x0020, - CHANNEL_NUMBER = 0x000c, - LIFETIME = 0x000d, - XOR_PEER_ADDRESS = 0x0012, - DATA = 0x0013, - XOR_RELAYED_ADDRESS = 0x0016, - EVEN_PORT = 0x0018, - REQUESTED_TRANSPORT = 0x0019, - DONT_FRAGMENT = 0x001a, - RESERVATION_TOKEN = 0x0022, - - PRIORITY = 0x0024, - USE_CANDIDATE = 0x0025, - - SOFTWARE = 0x8022, - ALTERNATE_SERVER = 0x8023, - FINGERPRINT = 0x8028, - - ICE_CONTROLLED = 0x8029, - ICE_CONTROLLING = 0x802a -}; - -enum Error -{ - TryAlternate = 300, - BadRequest = 400, - Unauthorized = 401, - UnknownAttribute = 420, - StaleNonce = 438, - ServerError = 500, - - Forbidden = 403, - AllocationMismatch = 437, - WrongCredentials = 441, - UnsupportedTransportProtocol = 442, - AllocationQuotaReached = 486, - InsufficientCapacity = 508, - - RoleConflict = 487 -}; - -QByteArray createMappedAddress(const QHostAddress &addr, quint16 port); -QByteArray createUsername(const QString &username); -QByteArray createErrorCode(int code, const QString &reason); -QByteArray createUnknownAttributes(const QList &typeList); -QByteArray createRealm(const QString &realm); -QByteArray createNonce(const QString &nonce); -QByteArray createXorMappedAddress(const QHostAddress &addr, quint16 port, const quint8 *magic, const quint8 *id); -QByteArray createChannelNumber(quint16 i); -QByteArray createLifetime(quint32 i); -QByteArray createXorPeerAddress(const QHostAddress &addr, quint16 port, const quint8 *magic, const quint8 *id); -QByteArray createXorRelayedAddress(const QHostAddress &addr, quint16 port, const quint8 *magic, const quint8 *id); -QByteArray createEvenPort(bool reserve); -QByteArray createRequestedTransport(quint8 proto); -QByteArray createReservationToken(const QByteArray &token); -QByteArray createPriority(quint32 i); -QByteArray createSoftware(const QString &str); -QByteArray createAlternateServer(const QHostAddress &addr, quint16 port); -QByteArray createIceControlled(quint64 i); -QByteArray createIceControlling(quint64 i); - -bool parseMappedAddress(const QByteArray &val, QHostAddress *addr, quint16 *port); -bool parseUsername(const QByteArray &val, QString *username); -bool parseErrorCode(const QByteArray &val, int *code, QString *reason); -bool parseUnknownAttributes(const QByteArray &val, QList *typeList); -bool parseRealm(const QByteArray &val, QString *realm); -bool parseNonce(const QByteArray &val, QString *nonce); -bool parseXorMappedAddress(const QByteArray &val, const quint8 *magic, const quint8 *id, QHostAddress *addr, quint16 *port); -bool parseChannelNumber(const QByteArray &val, quint16 *i); -bool parseLifetime(const QByteArray &val, quint32 *i); -bool parseXorPeerAddress(const QByteArray &val, const quint8 *magic, const quint8 *id, QHostAddress *addr, quint16 *port); -bool parseXorRelayedAddress(const QByteArray &val, const quint8 *magic, const quint8 *id, QHostAddress *addr, quint16 *port); -bool parseEvenPort(const QByteArray &val, bool *reserve); -bool parseRequestedTransport(const QByteArray &val, quint8 *proto); -bool parseReservationToken(const QByteArray &val, QByteArray *token); -bool parsePriority(const QByteArray &val, quint32 *i); -bool parseSoftware(const QByteArray &val, QString *str); -bool parseAlternateServer(const QByteArray &val, QHostAddress *addr, quint16 *port); -bool parseIceControlled(const QByteArray &val, quint64 *i); -bool parseIceControlling(const QByteArray &val, quint64 *i); - -QString methodToString(int method); -QString attributeTypeToString(int type); -QString attributeValueToString(int type, const QByteArray &val, const quint8 *magic, const quint8 *id); - -QString print_packet_str(const StunMessage &message); -void print_packet(const StunMessage &message); - -} - -} - -#endif + enum Method { + Binding = 0x001, + Allocate = 0x003, + Refresh = 0x004, + Send = 0x006, + Data = 0x007, + CreatePermission = 0x008, + ChannelBind = 0x009 + }; + + enum Attribute { + MAPPED_ADDRESS = 0x0001, + USERNAME = 0x0006, + MESSAGE_INTEGRITY = 0x0008, + ERROR_CODE = 0x0009, + UNKNOWN_ATTRIBUTES = 0x000a, + CHANNEL_NUMBER = 0x000c, + LIFETIME = 0x000d, + XOR_PEER_ADDRESS = 0x0012, + DATA = 0x0013, /* rfc8656 */ + REALM = 0x0014, + NONCE = 0x0015, + XOR_RELAYED_ADDRESS = 0x0016, + REQUESTED_ADDRESS_FAMILY = 0x0017, /* not implemented [RFC8656] */ + EVEN_PORT = 0x0018, + REQUESTED_TRANSPORT = 0x0019, + DONT_FRAGMENT = 0x001a, + ACCESS_TOKEN = 0x001b, /* not implemented rfc7635 */ + MESSAGE_INTEGRITY_SHA256 = 0x001c, /* not implemented [RFC8489] */ + PASSWORD_ALGORITHM = 0x001d, /* not implemented [RFC8489] */ + USERHASH = 0x001e, /* not implemented [RFC8489] */ + XOR_MAPPED_ADDRESS = 0x0020, + RESERVATION_TOKEN = 0x0022, + + PRIORITY = 0x0024, + USE_CANDIDATE = 0x0025, + + RESPONSE_PORT = 0x0027, /* not implemented */ + CONNECTION_ID = 0x002a, /* not implemented rfc6062 */ + + ADDITIONAL_ADDRESS_FAMILY = 0x8000, /* not implemented [RFC8656] */ + ADDRESS_ERROR_CODE = 0x8001, /* not implemented [RFC8656] */ + PASSWORD_ALGORITHMS = 0x8002, /* not implemented [RFC8489] */ + ALTERNATE_DOMAIN = 0x8003, /* not implemented [RFC8489] */ + ICMP = 0x8004, /* not implemented [RFC8656] */ + + SOFTWARE = 0x8022, + ALTERNATE_SERVER = 0x8023, + TRANSACTION_TRANSMIT_COUNTER = 0x8025, /* not implemented [RFC7982] */ + CACHE_TIMEOUT = 0x8027, /* not implemented [RFC5780] */ + FINGERPRINT = 0x8028, + + ICE_CONTROLLED = 0x8029, + ICE_CONTROLLING = 0x802a, + + RESPONSE_ORIGIN = 0x802b, /* not implemented [RFC5780] */ + OTHER_ADDRESS = 0x802c, /* not implemented [RFC5780] */ + ECN_CHECK = 0x802d, /* not implemented [RFC6679] */ + THIRD_PARTY_AUTHORIZATION = 0x802e, /* not implemented [RFC7635] */ + MOBILITY_TICKET = 0x8030 /* not implemented [RFC8016] */ + }; + + enum Error { + TryAlternate = 300, + BadRequest = 400, + Unauthorized = 401, + UnknownAttribute = 420, + StaleNonce = 438, + ServerError = 500, + + Forbidden = 403, + AllocationMismatch = 437, + WrongCredentials = 441, + UnsupportedTransportProtocol = 442, + AllocationQuotaReached = 486, + InsufficientCapacity = 508, + + RoleConflict = 487 + }; + + QByteArray createMappedAddress(const XMPP::TransportAddress &addr); + QByteArray createUsername(const QString &username); + QByteArray createErrorCode(int code, const QString &reason); + QByteArray createUnknownAttributes(const QList &typeList); + QByteArray createRealm(const QString &realm); + QByteArray createNonce(const QString &nonce); + QByteArray createXorMappedAddress(const XMPP::TransportAddress &addr, const quint8 *magic, const quint8 *id); + QByteArray createChannelNumber(quint16 i); + QByteArray createLifetime(quint32 i); + QByteArray createXorPeerAddress(const XMPP::TransportAddress &addr, const quint8 *magic, const quint8 *id); + QByteArray createXorRelayedAddress(const XMPP::TransportAddress &addr, const quint8 *magic, const quint8 *id); + QByteArray createEvenPort(bool reserve); + QByteArray createRequestedTransport(quint8 proto); + QByteArray createReservationToken(const QByteArray &token); + QByteArray createPriority(quint32 i); + QByteArray createSoftware(const QString &str); + QByteArray createAlternateServer(const XMPP::TransportAddress &addr); + QByteArray createIceControlled(quint64 i); + QByteArray createIceControlling(quint64 i); + + bool parseMappedAddress(const QByteArray &val, TransportAddress &addr); + bool parseUsername(const QByteArray &val, QString *username); + bool parseErrorCode(const QByteArray &val, int *code, QString *reason); + bool parseUnknownAttributes(const QByteArray &val, QList *typeList); + bool parseRealm(const QByteArray &val, QString *realm); + bool parseNonce(const QByteArray &val, QString *nonce); + bool parseXorMappedAddress(const QByteArray &val, const quint8 *magic, const quint8 *id, TransportAddress &addr); + bool parseChannelNumber(const QByteArray &val, quint16 *i); + bool parseLifetime(const QByteArray &val, quint32 *i); + bool parseXorPeerAddress(const QByteArray &val, const quint8 *magic, const quint8 *id, TransportAddress &addr); + bool parseXorRelayedAddress(const QByteArray &val, const quint8 *magic, const quint8 *id, TransportAddress &addr); + bool parseEvenPort(const QByteArray &val, bool *reserve); + bool parseRequestedTransport(const QByteArray &val, quint8 *proto); + bool parseReservationToken(const QByteArray &val, QByteArray *token); + bool parsePriority(const QByteArray &val, quint32 *i); + bool parseSoftware(const QByteArray &val, QString *str); + bool parseAlternateServer(const QByteArray &val, TransportAddress &addr); + bool parseIceControlled(const QByteArray &val, quint64 *i); + bool parseIceControlling(const QByteArray &val, quint64 *i); + + QString methodToString(int method); + QString attributeTypeToString(int type); + QString attributeValueToString(int type, const QByteArray &val, const quint8 *magic, const quint8 *id); + + QString print_packet_str(const StunMessage &message); + void print_packet(const StunMessage &message); + +} // namespace StunTypes +} // namespace XMPP + +#endif // STUNTYPES_H diff --git a/src/irisnet/noncore/stunutil.cpp b/src/irisnet/noncore/stunutil.cpp index 0dac62ea..3bd24b92 100644 --- a/src/irisnet/noncore/stunutil.cpp +++ b/src/irisnet/noncore/stunutil.cpp @@ -18,82 +18,77 @@ #include "stunutil.h" -namespace XMPP { +namespace XMPP { namespace StunUtil { + quint16 read16(const quint8 *in) + { + quint16 out = in[0]; + out <<= 8; + out += in[1]; + return out; + } -namespace StunUtil { + quint32 read32(const quint8 *in) + { + quint32 out = in[0]; + out <<= 8; + out += in[1]; + out <<= 8; + out += in[2]; + out <<= 8; + out += in[3]; + return out; + } -quint16 read16(const quint8 *in) -{ - quint16 out = in[0]; - out <<= 8; - out += in[1]; - return out; -} + quint64 read64(const quint8 *in) + { + quint64 out = in[0]; + out <<= 8; + out += in[1]; + out <<= 8; + out += in[2]; + out <<= 8; + out += in[3]; + out <<= 8; + out += in[4]; + out <<= 8; + out += in[5]; + out <<= 8; + out += in[6]; + out <<= 8; + out += in[7]; + return out; + } -quint32 read32(const quint8 *in) -{ - quint32 out = in[0]; - out <<= 8; - out += in[1]; - out <<= 8; - out += in[2]; - out <<= 8; - out += in[3]; - return out; -} + void write16(quint8 *out, quint16 i) + { + out[0] = quint8((i >> 8) & 0xff); + out[1] = quint8(i & 0xff); + } -quint64 read64(const quint8 *in) -{ - quint64 out = in[0]; - out <<= 8; - out += in[1]; - out <<= 8; - out += in[2]; - out <<= 8; - out += in[3]; - out <<= 8; - out += in[4]; - out <<= 8; - out += in[5]; - out <<= 8; - out += in[6]; - out <<= 8; - out += in[7]; - return out; -} + void write32(quint8 *out, quint32 i) + { + out[0] = quint8((i >> 24) & 0xff); + out[1] = quint8((i >> 16) & 0xff); + out[2] = quint8((i >> 8) & 0xff); + out[3] = quint8(i & 0xff); + } -void write16(quint8 *out, quint16 i) -{ - out[0] = (i >> 8) & 0xff; - out[1] = i & 0xff; -} + void write64(quint8 *out, quint64 i) + { + out[0] = quint8((i >> 56) & 0xff); + out[1] = quint8((i >> 48) & 0xff); + out[2] = quint8((i >> 40) & 0xff); + out[3] = quint8((i >> 32) & 0xff); + out[4] = quint8((i >> 24) & 0xff); + out[5] = quint8((i >> 16) & 0xff); + out[6] = quint8((i >> 8) & 0xff); + out[7] = quint8(i & 0xff); + } -void write32(quint8 *out, quint32 i) -{ - out[0] = (i >> 24) & 0xff; - out[1] = (i >> 16) & 0xff; - out[2] = (i >> 8) & 0xff; - out[3] = i & 0xff; -} - -void write64(quint8 *out, quint64 i) -{ - out[0] = (i >> 56) & 0xff; - out[1] = (i >> 48) & 0xff; - out[2] = (i >> 40) & 0xff; - out[3] = (i >> 32) & 0xff; - out[4] = (i >> 24) & 0xff; - out[5] = (i >> 16) & 0xff; - out[6] = (i >> 8) & 0xff; - out[7] = i & 0xff; -} - -QCA::SecureArray saslPrep(const QCA::SecureArray &in) -{ - // TODO - return in; -} - -} - -} + QCA::SecureArray saslPrep(const QCA::SecureArray &in) + { + // TODO + return in; + } +} // namespace StunUtil +} // namespace XMPP diff --git a/src/irisnet/noncore/stunutil.h b/src/irisnet/noncore/stunutil.h index fac82f00..88aab1fc 100644 --- a/src/irisnet/noncore/stunutil.h +++ b/src/irisnet/noncore/stunutil.h @@ -21,22 +21,19 @@ #include -namespace XMPP { +namespace XMPP { namespace StunUtil { + quint16 read16(const quint8 *in); + quint32 read32(const quint8 *in); + quint64 read64(const quint8 *in); -namespace StunUtil { + void write16(quint8 *out, quint16 i); + void write32(quint8 *out, quint32 i); + void write64(quint8 *out, quint64 i); -quint16 read16(const quint8 *in); -quint32 read32(const quint8 *in); -quint64 read64(const quint8 *in); + QCA::SecureArray saslPrep(const QCA::SecureArray &in); -void write16(quint8 *out, quint16 i); -void write32(quint8 *out, quint32 i); -void write64(quint8 *out, quint64 i); +} // namespace StunUtil -QCA::SecureArray saslPrep(const QCA::SecureArray &in); +} // namespace XMPP -} - -} - -#endif +#endif // STUNUTIL_H diff --git a/src/irisnet/noncore/tcpportreserver.cpp b/src/irisnet/noncore/tcpportreserver.cpp new file mode 100644 index 00000000..ef6bcda6 --- /dev/null +++ b/src/irisnet/noncore/tcpportreserver.cpp @@ -0,0 +1,247 @@ +/* + * tcpportreserver.cpp - a utility to bind local tcp server sockets + * Copyright (C) 2019 Sergey Ilinykh + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#include "tcpportreserver.h" + +#include "ice176.h" + +#include +#include +#include + +namespace XMPP { +TcpPortDiscoverer::TcpPortDiscoverer(TcpPortScope *scope) : QObject(scope), scope(scope) { } + +bool TcpPortDiscoverer::setExternalHost(const QString &extHost, quint16 extPort, const QHostAddress &localAddr, + quint16 localPort) +{ + if (!(typeMask & TcpPortServer::NatAssited)) { + return false; // seems like we don't need nat-assited + } + auto server = scope->bind(localAddr, localPort); + if (!server) { + return false; + } + TcpPortServer::Port p; + p.portType = TcpPortServer::NatAssited; + p.publishHost = extHost; + p.publishPort = extPort; + server->setPortInfo(p); + servers.append(server); + if (started) + emit portAvailable(); + return true; +} + +TcpPortServer::PortTypes TcpPortDiscoverer::inProgressPortTypes() const +{ + return {}; // same as for stop() +} + +bool TcpPortDiscoverer::isDepleted() const +{ + if (started) { + return servers.size() == 0; // TODO and no active subdiscoveries + } else { + return typeMask == 0; // otherwise we will tell after start + } +} + +TcpPortServer::PortTypes TcpPortDiscoverer::setTypeMask(TcpPortServer::PortTypes mask) +{ + typeMask = mask; + // drop ready ports if any + auto it = std::remove_if(servers.begin(), servers.end(), [mask](auto &s) { return !(s->portType() & mask); }); + servers.erase(it, servers.end()); + + TcpPortServer::PortTypes pendingTypes; + for (auto &s : servers) + pendingTypes |= s->portType(); + + // TODO drop pending subdiscoveries too and update pendingType when implemented + return pendingTypes; +} + +void TcpPortDiscoverer::addLocalServers() +{ + QList listenAddrs; + auto const interfaces = QNetworkInterface::allInterfaces(); + for (const QNetworkInterface &ni : interfaces) { + if (!(ni.flags() & (QNetworkInterface::IsUp | QNetworkInterface::IsRunning))) { + continue; + } + if (ni.flags() & QNetworkInterface::IsLoopBack) { + continue; + } + auto const &addrs = ni.addressEntries(); + for (const QNetworkAddressEntry &na : addrs) { + QHostAddress h = na.ip(); + if (h.isLoopback()) { + continue; + } + + // don't put the same address in twice. + // this also means that if there are + // two link-local ipv6 interfaces + // with the exact same address, we + // only use the first one + if (listenAddrs.contains(h)) + continue; +#if QT_VERSION >= QT_VERSION_CHECK(5, 11, 0) + if (h.protocol() == QAbstractSocket::IPv6Protocol && h.isLinkLocal()) +#else + if (h.protocol() == QAbstractSocket::IPv6Protocol && XMPP::Ice176::isIPv6LinkLocalAddress(h)) +#endif + h.setScopeId(ni.name()); + listenAddrs += h; + } + } + + for (auto &h : listenAddrs) { + auto server = scope->bind(h, 0); + if (!server) { + continue; + } + TcpPortServer::Port p; + p.portType = TcpPortServer::Direct; + QHostAddress addr = server->serverAddress(); + addr.setScopeId(QString()); + p.publishHost = addr.toString(); + p.publishPort = server->serverPort(); + server->setPortInfo(p); + servers.append(server); + } +} + +void TcpPortDiscoverer::start() +{ + started = true; + if (typeMask & TcpPortServer::Direct) + addLocalServers(); + + if (servers.size()) + emit portAvailable(); +} + +void TcpPortDiscoverer::stop() +{ + // nothing really to do here. but if we invent extension interface it can call stop on subdisco +} + +QList TcpPortDiscoverer::takeServers() +{ + auto ret = servers; + servers.clear(); + for (auto &p : ret) { + p->disconnect(this); + } + return ret; +} + +// -------------------------------------------------------------------------- +// TcpPortScope +// -------------------------------------------------------------------------- +struct TcpPortScope::Private { + QHash, QWeakPointer> servers; +}; + +TcpPortScope::TcpPortScope() : d(new Private) { } + +TcpPortScope::~TcpPortScope() { } + +TcpPortDiscoverer *TcpPortScope::disco() +{ + auto discoverer = new TcpPortDiscoverer(this); + // clang-format off + QMetaObject::invokeMethod(parent(), "newDiscoverer", Q_ARG(TcpPortDiscoverer*,discoverer)); + // clang-format on + QMetaObject::invokeMethod(discoverer, "start", Qt::QueuedConnection); + return discoverer; +} + +QList TcpPortScope::allServers() const +{ + QList ret; + for (auto &s : d->servers) { + auto sl = s.lock(); + if (sl) { + ret.append(sl); + } + } + return ret; +} + +void TcpPortScope::destroyServer(TcpPortServer *server) { delete server; } + +TcpPortServer::Ptr TcpPortScope::bind(const QHostAddress &addr, quint16 port) +{ + if (port) { + auto srv = d->servers.value(qMakePair(addr, port)).toStrongRef(); + if (srv) { + return srv; + } + } + auto socket = new QTcpServer(this); + if (!socket->listen(addr, port)) { + delete socket; + return TcpPortServer::Ptr(); + } + auto server = makeServer(socket); + + TcpPortServer::Ptr shared(server, [](TcpPortServer *s) { + auto scope = qobject_cast(s->parent()); + if (scope) { + scope->d->servers.remove(qMakePair(s->serverAddress(), s->serverPort())); + scope->destroyServer(s); + } else { + delete s; + } + }); + d->servers.insert(qMakePair(socket->serverAddress(), socket->serverPort()), shared.toWeakRef()); + + return shared; +} + +// -------------------------------------------------------------------------- +// TcpPortScope +// -------------------------------------------------------------------------- +TcpPortReserver::TcpPortReserver(QObject *parent) : QObject(parent) { } + +TcpPortReserver::~TcpPortReserver() { } + +TcpPortScope *TcpPortReserver::scope(const QString &id) +{ + return findChild(id, Qt::FindDirectChildrenOnly); +} + +void TcpPortReserver::registerScope(const QString &id, TcpPortScope *scope) +{ + scope->setObjectName(id); + scope->setParent(this); +} + +TcpPortScope *TcpPortReserver::unregisterScope(const QString &id) +{ + auto s = scope(id); + if (s) { + s->setParent(nullptr); + } + return s; +} +} // namespace XMPP diff --git a/src/irisnet/noncore/tcpportreserver.h b/src/irisnet/noncore/tcpportreserver.h new file mode 100644 index 00000000..bece3be4 --- /dev/null +++ b/src/irisnet/noncore/tcpportreserver.h @@ -0,0 +1,158 @@ +/* + * tcpportreserver.cpp - a utility to bind local tcp server sockets + * Copyright (C) 2019 Sergey Ilinykh + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#ifndef TCPPORTRESERVER_H +#define TCPPORTRESERVER_H + +#include +#include +#include +#include + +namespace XMPP { +class TcpPortServer : public QObject { + Q_OBJECT +public: + using Ptr = QSharedPointer; + + enum PortType { NoType = 0x0, Direct = 0x1, NatAssited = 0x2, Tunneled = 0x4 }; + Q_DECLARE_FLAGS(PortTypes, PortType) + + struct Port { + PortType portType = NoType; + QString publishHost; + quint16 publishPort = 0; + QVariant meta; + }; + + inline TcpPortServer(QTcpServer *serverSocket) : serverSocket(serverSocket) { } + inline void setPortInfo(const Port &port) { this->port = port; } + inline QHostAddress serverAddress() const { return serverSocket->serverAddress(); } + inline quint16 serverPort() const { return serverSocket->serverPort(); } + inline const QString &publishHost() const { return port.publishHost; } + inline quint16 publishPort() const { return port.publishPort; } + inline PortType portType() const { return port.portType; } + inline const QVariant &meta() const { return port.meta; } + +protected: + QTcpServer *serverSocket = nullptr; + Port port; +}; + +class TcpPortScope; +/** + * @brief The TcpPortDiscoverer class + * + * Discovers / starts listening on a set of unique tcp ports. + */ +class TcpPortDiscoverer : public QObject { + Q_OBJECT +public: + TcpPortDiscoverer(TcpPortScope *scope); + bool setExternalHost(const QString &extHost, quint16 extPort, const QHostAddress &localIp, quint16 localPort); + + TcpPortServer::PortTypes inProgressPortTypes() const; + bool isDepleted() const; + + /** + * @brief setTypeMask sets expected port types mask and frees unnecessary resources + * @param mask + * @return remaining port types + */ + XMPP::TcpPortServer::PortTypes setTypeMask(TcpPortServer::PortTypes mask); + + /** + * @brief takeServers takes all discovered servers + * @return + */ + QList takeServers(); +public slots: + void start(); // it's autocalled after outside world is notified about this new discoverer + void stop(); +signals: + void portAvailable(); + +private: + void addLocalServers(); + + bool started = false; + TcpPortServer::PortTypes typeMask + = TcpPortServer::PortTypes(TcpPortServer::Direct | TcpPortServer::NatAssited | TcpPortServer::Tunneled); + TcpPortScope *scope = nullptr; + QList servers; +}; + +class TcpPortReserver; +/** + * @brief The TcpPortScope class + * + * Handles scopes of ports. For example just S5B dedicated ports. + * There only on scope instance per scope id + */ +class TcpPortScope : public QObject { + Q_OBJECT +public: + TcpPortScope(); + ~TcpPortScope(); + TcpPortDiscoverer *disco(); + QList allServers() const; + +protected: + virtual TcpPortServer *makeServer(QTcpServer *socket) = 0; + virtual void destroyServer(TcpPortServer *server); + +private: + friend class TcpPortDiscoverer; + TcpPortServer::Ptr bind(const QHostAddress &addr, quint16 port); + +private: + struct Private; + std::unique_ptr d; +}; + +/** + * @brief The TcpPortReserver class + * This class should have the only instance per application + */ +class TcpPortReserver : public QObject { + Q_OBJECT +public: + explicit TcpPortReserver(QObject *parent = nullptr); + ~TcpPortReserver(); + + /** + * @brief scope returns a registered scope corresponding to scope id + * @param id + * @return scope + * @note Do not reparent the object + */ + TcpPortScope *scope(const QString &id); + + void registerScope(const QString &id, TcpPortScope *scope); + TcpPortScope *unregisterScope(const QString &id); +signals: + void newDiscoverer(TcpPortDiscoverer *discoverer); + +public slots: +}; +} // namespace XMPP + +Q_DECLARE_OPERATORS_FOR_FLAGS(XMPP::TcpPortServer::PortTypes) + +#endif // TCPPORTRESERVER_H diff --git a/src/irisnet/noncore/transportaddress.h b/src/irisnet/noncore/transportaddress.h new file mode 100644 index 00000000..fec62a3c --- /dev/null +++ b/src/irisnet/noncore/transportaddress.h @@ -0,0 +1,38 @@ +#ifndef TRANSPORTADDRESS_H +#define TRANSPORTADDRESS_H + +#include + +namespace XMPP { + +class TransportAddress { +public: + QHostAddress addr; + quint16 port = 0; + + TransportAddress() = default; + TransportAddress(const QHostAddress &_addr, quint16 _port) : addr(_addr), port(_port) { } + + bool isValid() const { return !addr.isNull(); } + bool operator==(const TransportAddress &other) const { return addr == other.addr && port == other.port; } + + inline bool operator!=(const TransportAddress &other) const { return !operator==(other); } + inline operator QString() const + { + return QString(QLatin1String("%1:%2")).arg(addr.toString(), QString::number(port)); + } +}; + +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) + +inline uint qHash(const TransportAddress &key, uint seed = 0) +#else +inline size_t qHash(const TransportAddress &key, size_t seed = 0) +#endif +{ + return ::qHash(key.addr, seed) ^ ::qHash(key.port, seed); +} + +} + +#endif // TRANSPORTADDRESS_H diff --git a/src/irisnet/noncore/turnclient.cpp b/src/irisnet/noncore/turnclient.cpp index 61bf9769..e0a0bfe2 100644 --- a/src/irisnet/noncore/turnclient.cpp +++ b/src/irisnet/noncore/turnclient.cpp @@ -18,66 +18,46 @@ #include "turnclient.h" -#include -#include "stuntypes.h" -#include "stunmessage.h" -#include "stuntransaction.h" -#include "stunallocate.h" -#include "objectsession.h" -#include "bytestream.h" #include "bsocket.h" +#include "bytestream.h" #include "httpconnect.h" +#include "objectsession.h" #include "socks.h" +#include "stunallocate.h" +#include "stunmessage.h" +#include "stuntransaction.h" +#include "stuntypes.h" -namespace XMPP { +#include +namespace XMPP { //---------------------------------------------------------------------------- // TurnClient::Proxy //---------------------------------------------------------------------------- -TurnClient::Proxy::Proxy() -{ - t = None; -} +TurnClient::Proxy::Proxy() : t(None) { } -TurnClient::Proxy::~Proxy() -{ -} +TurnClient::Proxy::~Proxy() { } -int TurnClient::Proxy::type() const -{ - return t; -} +int TurnClient::Proxy::type() const { return t; } -QString TurnClient::Proxy::host() const -{ - return v_host; -} +QString TurnClient::Proxy::host() const { return v_host; } -quint16 TurnClient::Proxy::port() const -{ - return v_port; -} +quint16 TurnClient::Proxy::port() const { return v_port; } -QString TurnClient::Proxy::user() const -{ - return v_user; -} +QString TurnClient::Proxy::user() const { return v_user; } -QString TurnClient::Proxy::pass() const -{ - return v_pass; -} +QString TurnClient::Proxy::pass() const { return v_pass; } void TurnClient::Proxy::setHttpConnect(const QString &host, quint16 port) { - t = HttpConnect; + t = HttpConnect; v_host = host; v_port = port; } void TurnClient::Proxy::setSocks(const QString &host, quint16 port) { - t = Socks; + t = Socks; v_host = host; v_port = port; } @@ -91,132 +71,97 @@ void TurnClient::Proxy::setUserPass(const QString &user, const QString &pass) //---------------------------------------------------------------------------- // TurnClient //---------------------------------------------------------------------------- -class TurnClient::Private : public QObject -{ +class TurnClient::Private : public QObject { Q_OBJECT public: - TurnClient *q; - Proxy proxy; - QString clientSoftware; - TurnClient::Mode mode; - QHostAddress serverAddr; - int serverPort; - ObjectSession sess; - ByteStream *bs; - QCA::TLS *tls; - bool tlsHandshaken; - QByteArray inStream; - bool udp; - StunTransactionPool *pool; - StunAllocate *allocate; - bool allocateStarted; - QString user; - QCA::SecureArray pass; - QString realm; - int retryCount; - QString errorString; - int debugLevel; - - class WriteItem - { + TurnClient *q; + Proxy proxy; + QString clientSoftware; + TurnClient::Mode mode = PlainMode; + TransportAddress serverAddr; + ObjectSession sess; + ByteStream *bs = nullptr; + QCA::TLS *tls = nullptr; + bool tlsHandshaken = false; + QByteArray inStream; + bool udp = false; + StunTransactionPool::Ptr pool; + StunAllocate *allocate = nullptr; + bool allocateStarted = false; + QString user; + QCA::SecureArray pass; + QString realm; + int retryCount = 0; + QString errorString; + int debugLevel = 0; + + class WriteItem { public: - enum Type - { - Data, - Other - }; - - Type type; - int size; - QHostAddress addr; - int port; - - WriteItem(int _size) : - type(Other), - size(_size), - port(-1) - { - } + enum Type { Data, Other }; - WriteItem(int _size, const QHostAddress &_addr, int _port) : - type(Data), - size(_size), - addr(_addr), - port(_port) - { - } + Type type; + int size; + TransportAddress addr; + + WriteItem(int _size) : type(Other), size(_size) { } + + WriteItem(int _size, const TransportAddress &_addr) : type(Data), size(_size), addr(_addr) { } }; QList writeItems; - int writtenBytes; - bool stopping; + int writtenBytes; + bool stopping; - class Packet - { + class Packet { public: - QHostAddress addr; - int port; - QByteArray data; + TransportAddress addr; + QByteArray data; // for outbound bool requireChannel; - Packet() : - port(-1), - requireChannel(false) - { - } + Packet() : requireChannel(false) { } }; - QList in; - QList outPending; - int outPendingWrite; - QList desiredPerms; + QList in; + QList outPending; + int outPendingWrite; + QList desiredPerms; QList pendingChannels, desiredChannels; - class Written - { + class Written { public: - QHostAddress addr; - int port; - int count; + TransportAddress addr; + int count; }; Private(TurnClient *_q) : - QObject(_q), - q(_q), - sess(this), - bs(0), - tls(0), - udp(false), - pool(0), - allocate(0), - retryCount(0), - debugLevel(TurnClient::DL_None), - writtenBytes(0), - stopping(false), - outPendingWrite(0) + QObject(_q), q(_q), sess(this), bs(nullptr), tls(0), udp(false), pool(nullptr), allocate(nullptr), + retryCount(0), debugLevel(TurnClient::DL_None), writtenBytes(0), stopping(false), outPendingWrite(0) { } - ~Private() + ~Private() { cleanup(); } + + void unsetPool() { - cleanup(); + // in udp mode, we don't own the pool + if (!udp && pool) { + pool->disconnect(this); + pool.reset(); + } } void cleanup() { delete allocate; - allocate = 0; + allocate = nullptr; - // in udp mode, we don't own the pool - if(!udp) - delete pool; - pool = 0; + unsetPool(); delete tls; - tls = 0; + tls = nullptr; delete bs; bs = 0; @@ -229,7 +174,7 @@ class TurnClient::Private : public QObject retryCount = 0; writeItems.clear(); writtenBytes = 0; - stopping = false; + stopping = false; outPending.clear(); outPendingWrite = 0; desiredPerms.clear(); @@ -239,68 +184,56 @@ class TurnClient::Private : public QObject void do_connect() { - if(udp) - { + if (udp) { after_connected(); return; } - if(proxy.type() == Proxy::HttpConnect) - { + if (proxy.type() == Proxy::HttpConnect) { HttpConnect *s = new HttpConnect(this); - bs = s; - connect(s, SIGNAL(connected()), SLOT(bs_connected())); - connect(s, SIGNAL(error(int)), SLOT(bs_error(int))); - if(!proxy.user().isEmpty()) + bs = s; + connect(s, &HttpConnect::connected, this, &Private::bs_connected); + connect(s, &HttpConnect::error, this, &Private::bs_error); + if (!proxy.user().isEmpty()) s->setAuth(proxy.user(), proxy.pass()); - s->connectToHost(proxy.host(), proxy.port(), serverAddr.toString(), serverPort); - } - else if(proxy.type() == Proxy::Socks) - { + s->connectToHost(proxy.host(), proxy.port(), serverAddr.addr.toString(), serverAddr.port); + } else if (proxy.type() == Proxy::Socks) { SocksClient *s = new SocksClient(this); - bs = s; - connect(s, SIGNAL(connected()), SLOT(bs_connected())); - connect(s, SIGNAL(error(int)), SLOT(bs_error(int))); - if(!proxy.user().isEmpty()) + bs = s; + connect(s, &SocksClient::connected, this, &Private::bs_connected); + connect(s, &SocksClient::error, this, &Private::bs_error); + if (!proxy.user().isEmpty()) s->setAuth(proxy.user(), proxy.pass()); - s->connectToHost(proxy.host(), proxy.port(), serverAddr.toString(), serverPort); - } - else - { + s->connectToHost(proxy.host(), proxy.port(), serverAddr.addr.toString(), serverAddr.port); + } else { BSocket *s = new BSocket(this); - bs = s; - connect(s, SIGNAL(connected()), SLOT(bs_connected())); - connect(s, SIGNAL(error(int)), SLOT(bs_error(int))); - s->connectToHost(serverAddr.toString(), serverPort); + bs = s; + connect(s, &BSocket::connected, this, &Private::bs_connected); + connect(s, &BSocket::error, this, &Private::bs_error); + s->connectToHost(serverAddr.addr.toString(), serverAddr.port); } - connect(bs, SIGNAL(connectionClosed()), SLOT(bs_connectionClosed())); - connect(bs, SIGNAL(delayedCloseFinished()), SLOT(bs_delayedCloseFinished())); - connect(bs, SIGNAL(readyRead()), SLOT(bs_readyRead())); - connect(bs, SIGNAL(bytesWritten(qint64)), SLOT(bs_bytesWritten(qint64))); + connect(bs, &ByteStream::connectionClosed, this, &Private::bs_connectionClosed); + connect(bs, &ByteStream::delayedCloseFinished, this, &Private::bs_delayedCloseFinished); + connect(bs, &ByteStream::readyRead, this, &Private::bs_readyRead); + connect(bs, &ByteStream::bytesWritten, this, &Private::bs_bytesWritten); } void do_close() { stopping = true; - if(allocate && allocateStarted) - { - if(debugLevel >= TurnClient::DL_Info) + if (allocate && allocateStarted) { + if (debugLevel >= TurnClient::DL_Info) emit q->debugLine("Deallocating..."); allocate->stop(); - } - else - { + } else { delete allocate; - allocate = 0; + allocate = nullptr; - // in udp mode, we don't own the pool - if(!udp) - delete pool; - pool = 0; + unsetPool(); - if(udp) + if (udp) sess.defer(q, "closed"); else do_transport_close(); @@ -309,14 +242,11 @@ class TurnClient::Private : public QObject void do_transport_close() { - if(tls && tlsHandshaken) - { + if (tls && tlsHandshaken) { tls->close(); - } - else - { + } else { delete tls; - tls = 0; + tls = nullptr; do_sock_close(); } @@ -325,12 +255,11 @@ class TurnClient::Private : public QObject void do_sock_close() { bool waitForSignal = false; - if(bs->bytesToWrite() > 0) + if (bs->bytesToWrite() > 0) waitForSignal = true; bs->close(); - if(!waitForSignal) - { + if (!waitForSignal) { cleanup(); sess.defer(q, "closed"); } @@ -339,40 +268,38 @@ class TurnClient::Private : public QObject void after_connected() { // when retrying, pool will be non-null because we reuse it - if(!udp && !pool) - { - pool = new StunTransactionPool(StunTransaction::Tcp, this); + if (!udp && !pool) { + pool = StunTransactionPool::Ptr::create(StunTransaction::Tcp); pool->setDebugLevel((StunTransactionPool::DebugLevel)debugLevel); - connect(pool, SIGNAL(outgoingMessage(QByteArray,QHostAddress,int)), SLOT(pool_outgoingMessage(QByteArray,QHostAddress,int))); - connect(pool, SIGNAL(needAuthParams()), SLOT(pool_needAuthParams())); - connect(pool, SIGNAL(debugLine(QString)), SLOT(pool_debugLine(QString))); + connect(pool.data(), &StunTransactionPool::outgoingMessage, this, &Private::pool_outgoingMessage); + connect(pool.data(), &StunTransactionPool::needAuthParams, this, &Private::pool_needAuthParams); + connect(pool.data(), &StunTransactionPool::debugLine, this, &Private::pool_debugLine); pool->setLongTermAuthEnabled(true); - if(!user.isEmpty()) - { + if (!user.isEmpty()) { pool->setUsername(user); pool->setPassword(pass); - if(!realm.isEmpty()) + if (!realm.isEmpty()) pool->setRealm(realm); } } - allocate = new StunAllocate(pool); - connect(allocate, SIGNAL(started()), SLOT(allocate_started())); - connect(allocate, SIGNAL(stopped()), SLOT(allocate_stopped())); - connect(allocate, SIGNAL(error(XMPP::StunAllocate::Error)), SLOT(allocate_error(XMPP::StunAllocate::Error))); - connect(allocate, SIGNAL(permissionsChanged()), SLOT(allocate_permissionsChanged())); - connect(allocate, SIGNAL(channelsChanged()), SLOT(allocate_channelsChanged())); - connect(allocate, SIGNAL(debugLine(QString)), SLOT(allocate_debugLine(QString))); + allocate = new StunAllocate(pool.data()); + connect(allocate, &StunAllocate::started, this, &Private::allocate_started); + connect(allocate, &StunAllocate::stopped, this, &Private::allocate_stopped); + connect(allocate, &StunAllocate::error, this, &Private::allocate_error); + connect(allocate, &StunAllocate::permissionsChanged, this, &Private::allocate_permissionsChanged); + connect(allocate, &StunAllocate::channelsChanged, this, &Private::allocate_channelsChanged); + connect(allocate, &StunAllocate::debugLine, this, &Private::allocate_debugLine); allocate->setClientSoftwareNameAndVersion(clientSoftware); allocateStarted = false; - if(debugLevel >= TurnClient::DL_Info) + if (debugLevel >= TurnClient::DL_Info) emit q->debugLine("Allocating..."); // only use addr association in udp mode - if(udp) - allocate->start(serverAddr, serverPort); + if (udp) + allocate->start(serverAddr); else allocate->start(); } @@ -382,17 +309,15 @@ class TurnClient::Private : public QObject inStream += in; ObjectSessionWatcher watch(&sess); - while(1) - { + while (1) { QByteArray packet; // try to extract ChannelData or a STUN message from // the stream packet = StunAllocate::readChannelData((const quint8 *)inStream.data(), inStream.size()); - if(packet.isNull()) - { + if (packet.isNull()) { packet = StunMessage::readStun((const quint8 *)inStream.data(), inStream.size()); - if(packet.isNull()) + if (packet.isNull()) break; } @@ -401,7 +326,7 @@ class TurnClient::Private : public QObject // processDatagram may cause the session to be reset // or the object to be deleted processDatagram(packet); - if(!watch.isValid()) + if (!watch.isValid()) break; } } @@ -409,49 +334,39 @@ class TurnClient::Private : public QObject void processDatagram(const QByteArray &buf) { bool notStun; - if(!pool->writeIncomingMessage(buf, ¬Stun)) - { - QByteArray data; - QHostAddress fromAddr; - int fromPort; - - data = processNonPoolPacket(buf, notStun, &fromAddr, &fromPort); - if(!data.isNull()) - processDataPacket(data, fromAddr, fromPort); + if (!pool->writeIncomingMessage(buf, ¬Stun)) { + QByteArray data; + TransportAddress fromAddr; + + data = processNonPoolPacket(buf, notStun, fromAddr); + if (!data.isNull()) + processDataPacket(data, fromAddr); } } - QByteArray processNonPoolPacket(const QByteArray &buf, bool notStun, QHostAddress *addr, int *port) + QByteArray processNonPoolPacket(const QByteArray &buf, bool notStun, TransportAddress &addr) { - if(notStun) - { + if (notStun) { // not stun? maybe it is a data packet - QByteArray data = allocate->decode(buf, addr, port); - if(!data.isNull()) - { - if(debugLevel >= TurnClient::DL_Packet) + QByteArray data = allocate->decode(buf, addr); + if (!data.isNull()) { + if (debugLevel >= TurnClient::DL_Packet) emit q->debugLine("Received ChannelData-based data packet"); return data; } - } - else - { + } else { // packet might be stun not owned by pool. // let's see StunMessage message = StunMessage::fromBinary(buf); - if(!message.isNull()) - { - QByteArray data = allocate->decode(message, addr, port); + if (!message.isNull()) { + QByteArray data = allocate->decode(message, addr); - if(!data.isNull()) - { - if(debugLevel >= TurnClient::DL_Packet) + if (!data.isNull()) { + if (debugLevel >= TurnClient::DL_Packet) emit q->debugLine("Received STUN-based data packet"); return data; - } - else - { - if(debugLevel >= TurnClient::DL_Packet) + } else { + if (debugLevel >= TurnClient::DL_Packet) emit q->debugLine("Warning: server responded with an unexpected STUN packet, skipping."); } @@ -459,77 +374,67 @@ class TurnClient::Private : public QObject } } - if(debugLevel >= TurnClient::DL_Packet) - emit q->debugLine("Warning: server responded with what doesn't seem to be a STUN or data packet, skipping."); + if (debugLevel >= TurnClient::DL_Packet) + emit q->debugLine( + "Warning: server responded with what doesn't seem to be a STUN or data packet, skipping."); return QByteArray(); } - void processDataPacket(const QByteArray &buf, const QHostAddress &addr, int port) + void processDataPacket(const QByteArray &buf, const TransportAddress &addr) { Packet p; p.addr = addr; - p.port = port; p.data = buf; in += p; emit q->readyRead(); } - void writeOrQueue(const QByteArray &buf, const QHostAddress &addr, int port) + void writeOrQueue(const QByteArray &buf, const TransportAddress &addr) { Q_ASSERT(allocateStarted); - StunAllocate::Channel c(addr, port); - bool writeImmediately = false; - bool requireChannel = pendingChannels.contains(c) || desiredChannels.contains(c); + StunAllocate::Channel c(addr); + bool writeImmediately = false; + bool requireChannel = pendingChannels.contains(c) || desiredChannels.contains(c); QList actualPerms = allocate->permissions(); - if(actualPerms.contains(addr)) - { - if(requireChannel) - { + if (actualPerms.contains(addr.addr)) { + if (requireChannel) { QList actualChannels = allocate->channels(); - if(actualChannels.contains(c)) + if (actualChannels.contains(c)) writeImmediately = true; - } - else + } else writeImmediately = true; } - if(writeImmediately) - { - write(buf, addr, port); - } - else - { + if (writeImmediately) { + write(buf, addr); + } else { Packet p; - p.addr = addr; - p.port = port; - p.data = buf; + p.addr = addr; + p.data = buf; p.requireChannel = requireChannel; outPending += p; - ensurePermission(addr); + ensurePermission(addr.addr); } } void tryWriteQueued() { - QList actualPerms = allocate->permissions(); + QList actualPerms = allocate->permissions(); QList actualChannels = allocate->channels(); - for(int n = 0; n < outPending.count(); ++n) - { + for (int n = 0; n < outPending.count(); ++n) { const Packet &p = outPending[n]; - if(actualPerms.contains(p.addr)) - { - StunAllocate::Channel c(p.addr, p.port); - if(!p.requireChannel || actualChannels.contains(c)) - { + if (actualPerms.contains(p.addr.addr)) { + StunAllocate::Channel c(p.addr); + if (!p.requireChannel || actualChannels.contains(c)) { Packet po = outPending[n]; outPending.removeAt(n); --n; // adjust position - write(po.data, po.addr, po.port); + write(po.data, po.addr); } } } @@ -537,50 +442,41 @@ class TurnClient::Private : public QObject void tryChannelQueued() { - if(!pendingChannels.isEmpty()) - { - QList actualPerms = allocate->permissions(); + if (!pendingChannels.isEmpty()) { + QList actualPerms = allocate->permissions(); QList list; - for(int n = 0; n < pendingChannels.count(); ++n) - { - if(actualPerms.contains(pendingChannels[n].address)) - { + for (int n = 0; n < pendingChannels.count(); ++n) { + if (actualPerms.contains(pendingChannels[n].address.addr)) { list += pendingChannels[n]; pendingChannels.removeAt(n); --n; // adjust position } } - if(!list.isEmpty()) + if (!list.isEmpty()) ensureChannels(list); } } - void write(const QByteArray &buf, const QHostAddress &addr, int port) + void write(const QByteArray &buf, const TransportAddress &addr) { - QByteArray packet = allocate->encode(buf, addr, port); + QByteArray packet = allocate->encode(buf, addr); - if(debugLevel >= TurnClient::DL_Packet) - { + if (debugLevel >= TurnClient::DL_Packet) { StunMessage msg = StunMessage::fromBinary(packet); - if(!msg.isNull()) - { + if (!msg.isNull()) { emit q->debugLine("STUN SEND"); emit q->debugLine(StunTypes::print_packet_str(msg)); - } - else + } else emit q->debugLine("Sending ChannelData-based data packet"); } - writeItems += WriteItem(packet.size(), addr, port); + writeItems += WriteItem(packet.size(), addr); ++outPendingWrite; - if(udp) - { + if (udp) { emit q->outgoingDatagram(packet); - } - else - { - if(tls) + } else { + if (tls) tls->write(packet); else bs->write(packet); @@ -589,9 +485,8 @@ class TurnClient::Private : public QObject void ensurePermission(const QHostAddress &addr) { - if(!desiredPerms.contains(addr)) - { - if(debugLevel >= TurnClient::DL_Info) + if (!desiredPerms.contains(addr)) { + if (debugLevel >= TurnClient::DL_Info) emit q->debugLine(QString("Setting permission for peer address %1").arg(addr.toString())); desiredPerms += addr; @@ -603,29 +498,26 @@ class TurnClient::Private : public QObject void ensureChannels(const QList &channels) { bool changed = false; - foreach(const StunAllocate::Channel &c, channels) - { - if(!desiredChannels.contains(c)) - { - if(debugLevel >= TurnClient::DL_Info) - emit q->debugLine(QString("Setting channel for peer address/port %1;%2").arg(c.address.toString()).arg(c.port)); + for (const StunAllocate::Channel &c : channels) { + if (!desiredChannels.contains(c)) { + if (debugLevel >= TurnClient::DL_Info) + emit q->debugLine(QString("Setting channel for peer address/port %1").arg(c.address)); desiredChannels += c; changed = true; } } - if(changed) + if (changed) allocate->setChannels(desiredChannels); } - void addChannelPeer(const QHostAddress &addr, int port) + void addChannelPeer(const TransportAddress &addr) { - ensurePermission(addr); + ensurePermission(addr.addr); - StunAllocate::Channel c(addr, port); - if(!pendingChannels.contains(c) && !desiredChannels.contains(c)) - { + StunAllocate::Channel c(addr); + if (!pendingChannels.contains(c) && !desiredChannels.contains(c)) { pendingChannels += c; tryChannelQueued(); @@ -636,33 +528,25 @@ class TurnClient::Private : public QObject { QList writtenDests; - while(count > 0) - { + while (count > 0) { Q_ASSERT(!writeItems.isEmpty()); WriteItem wi = writeItems.takeFirst(); --count; - if(wi.type == WriteItem::Data) - { + if (wi.type == WriteItem::Data) { int at = -1; - for(int n = 0; n < writtenDests.count(); ++n) - { - if(writtenDests[n].addr == wi.addr && writtenDests[n].port == wi.port) - { + for (int n = 0; n < writtenDests.count(); ++n) { + if (writtenDests[n].addr == wi.addr) { at = n; break; } } - if(at != -1) - { + if (at != -1) { ++writtenDests[at].count; - } - else - { + } else { Written wr; - wr.addr = wi.addr; - wr.port = wi.port; + wr.addr = wi.addr; wr.count = 1; writtenDests += wr; } @@ -675,10 +559,9 @@ class TurnClient::Private : public QObject void emitPacketsWritten(const QList &writtenDests) { ObjectSessionWatcher watch(&sess); - foreach(const Written &wr, writtenDests) - { - emit q->packetsWritten(wr.count, wr.addr, wr.port); - if(!watch.isValid()) + for (const Written &wr : writtenDests) { + emit q->packetsWritten(wr.count, wr.addr); + if (!watch.isValid()) return; } } @@ -687,22 +570,21 @@ class TurnClient::Private : public QObject bool handleRetry() { ++retryCount; - if(retryCount < 3 && !stopping) - { - if(debugLevel >= TurnClient::DL_Info) + if (retryCount < 3 && !stopping) { + if (debugLevel >= TurnClient::DL_Info) emit q->debugLine("retrying..."); // start completely over, but retain the same pool // so the user isn't asked to auth again - int tmp_retryCount = retryCount; - StunTransactionPool *tmp_pool = pool; - pool = 0; + int tmp_retryCount = retryCount; + StunTransactionPool::Ptr tmp_pool = pool; + pool.reset(); cleanup(); retryCount = tmp_retryCount; - pool = tmp_pool; + pool = tmp_pool; do_connect(); return true; @@ -715,23 +597,21 @@ private slots: void bs_connected() { ObjectSessionWatcher watch(&sess); - emit q->connected(); - if(!watch.isValid()) + emit q->connected(); + if (!watch.isValid()) return; - if(mode == TurnClient::TlsMode) - { + if (mode == TurnClient::TlsMode) { tls = new QCA::TLS(this); - connect(tls, SIGNAL(handshaken()), SLOT(tls_handshaken())); - connect(tls, SIGNAL(readyRead()), SLOT(tls_readyRead())); - connect(tls, SIGNAL(readyReadOutgoing()), SLOT(tls_readyReadOutgoing())); - connect(tls, SIGNAL(error()), SLOT(tls_error())); + connect(tls, &QCA::TLS::handshaken, this, &Private::tls_handshaken); + connect(tls, &QCA::TLS::readyRead, this, &Private::tls_readyRead); + connect(tls, &QCA::TLS::readyReadOutgoing, this, &Private::tls_readyReadOutgoing); + connect(tls, &QCA::TLS::error, this, &Private::tls_error); tlsHandshaken = false; - if(debugLevel >= TurnClient::DL_Info) + if (debugLevel >= TurnClient::DL_Info) emit q->debugLine("TLS handshaking..."); tls->startClient(); - } - else + } else after_connected(); } @@ -752,7 +632,7 @@ private slots: { QByteArray buf = bs->readAll(); - if(tls) + if (tls) tls->writeIncoming(buf); else processStream(buf); @@ -760,11 +640,10 @@ private slots: void bs_bytesWritten(qint64 written) { - if(tls) - { + if (tls) { // convertBytesWritten() is unsafe to call unless // the TLS handshake is completed - if(!tlsHandshaken) + if (!tlsHandshaken) return; written = tls->convertBytesWritten(written); @@ -774,36 +653,28 @@ private slots: QList writtenDests; - while(writtenBytes > 0) - { + while (writtenBytes > 0) { Q_ASSERT(!writeItems.isEmpty()); - if(writtenBytes < writeItems.first().size) + if (writtenBytes < writeItems.first().size) break; WriteItem wi = writeItems.takeFirst(); writtenBytes -= wi.size; - if(wi.type == WriteItem::Data) - { + if (wi.type == WriteItem::Data) { int at = -1; - for(int n = 0; n < writtenDests.count(); ++n) - { - if(writtenDests[n].addr == wi.addr && writtenDests[n].port == wi.port) - { + for (int n = 0; n < writtenDests.count(); ++n) { + if (writtenDests[n].addr == wi.addr) { at = n; break; } } - if(at != -1) - { + if (at != -1) { ++writtenDests[at].count; - } - else - { + } else { Written wr; - wr.addr = wi.addr; - wr.port = wi.port; + wr.addr = wi.addr; wr.count = 1; writtenDests += wr; } @@ -816,41 +687,36 @@ private slots: void bs_error(int e) { TurnClient::Error te; - if(qobject_cast(bs)) - { - if(e == HttpConnect::ErrConnectionRefused) + if (qobject_cast(bs)) { + if (e == HttpConnect::ErrConnectionRefused) te = TurnClient::ErrorConnect; - else if(e == HttpConnect::ErrHostNotFound) + else if (e == HttpConnect::ErrHostNotFound) te = TurnClient::ErrorHostNotFound; - else if(e == HttpConnect::ErrProxyConnect) + else if (e == HttpConnect::ErrProxyConnect) te = TurnClient::ErrorProxyConnect; - else if(e == HttpConnect::ErrProxyNeg) + else if (e == HttpConnect::ErrProxyNeg) te = TurnClient::ErrorProxyNeg; - else if(e == HttpConnect::ErrProxyAuth) + else if (e == HttpConnect::ErrProxyAuth) te = TurnClient::ErrorProxyAuth; else te = TurnClient::ErrorStream; - } - else if(qobject_cast(bs)) - { - if(e == SocksClient::ErrConnectionRefused) + } else if (qobject_cast(bs)) { + if (e == SocksClient::ErrConnectionRefused) te = TurnClient::ErrorConnect; - else if(e == SocksClient::ErrHostNotFound) + else if (e == SocksClient::ErrHostNotFound) te = TurnClient::ErrorHostNotFound; - else if(e == SocksClient::ErrProxyConnect) + else if (e == SocksClient::ErrProxyConnect) te = TurnClient::ErrorProxyConnect; - else if(e == SocksClient::ErrProxyNeg) + else if (e == SocksClient::ErrProxyNeg) te = TurnClient::ErrorProxyNeg; - else if(e == SocksClient::ErrProxyAuth) + else if (e == SocksClient::ErrProxyAuth) te = TurnClient::ErrorProxyAuth; else te = TurnClient::ErrorStream; - } - else - { - if(e == BSocket::ErrConnectionRefused) + } else { + if (e == BSocket::ErrConnectionRefused) te = TurnClient::ErrorConnect; - else if(e == BSocket::ErrHostNotFound) + else if (e == BSocket::ErrHostNotFound) te = TurnClient::ErrorHostNotFound; else te = TurnClient::ErrorStream; @@ -866,28 +732,22 @@ private slots: tlsHandshaken = true; ObjectSessionWatcher watch(&sess); - emit q->tlsHandshaken(); - if(!watch.isValid()) + emit q->tlsHandshaken(); + if (!watch.isValid()) return; tls->continueAfterStep(); after_connected(); } - void tls_readyRead() - { - processStream(tls->read()); - } + void tls_readyRead() { processStream(tls->read()); } - void tls_readyReadOutgoing() - { - bs->write(tls->readOutgoing()); - } + void tls_readyReadOutgoing() { bs->write(tls->readOutgoing()); } void tls_closed() { delete tls; - tls = 0; + tls = nullptr; do_sock_close(); } @@ -899,34 +759,30 @@ private slots: emit q->error(TurnClient::ErrorTls); } - void pool_outgoingMessage(const QByteArray &packet, const QHostAddress &toAddress, int toPort) + void pool_outgoingMessage(const QByteArray &packet, const XMPP::TransportAddress &toAddress) { // we aren't using IP-associated transactions Q_UNUSED(toAddress); - Q_UNUSED(toPort); writeItems += WriteItem(packet.size()); - if(tls) + // emit q->debugLine(QString("Sending turn packet to: %1:%2") + // .arg(bs->abstractSocket()->peerAddress().toString()) + // .arg(bs->abstractSocket()->peerPort())); + if (tls) tls->write(packet); else bs->write(packet); } - void pool_needAuthParams() - { - emit q->needAuthParams(); - } + void pool_needAuthParams(const TransportAddress &addr) { emit q->needAuthParams(addr); } - void pool_debugLine(const QString &line) - { - emit q->debugLine(line); - } + void pool_debugLine(const QString &line) { emit q->debugLine(line); } void allocate_started() { allocateStarted = true; - if(debugLevel >= TurnClient::DL_Info) + if (debugLevel >= TurnClient::DL_Info) emit q->debugLine("Allocate started"); emit q->activated(); @@ -935,14 +791,11 @@ private slots: void allocate_stopped() { delete allocate; - allocate = 0; + allocate = nullptr; - // in udp mode, we don't own the pool - if(!udp) - delete pool; - pool = 0; + unsetPool(); - if(udp) + if (udp) emit q->closed(); else do_transport_close(); @@ -953,22 +806,20 @@ private slots: QString str = allocate->errorString(); TurnClient::Error te; - if(e == StunAllocate::ErrorAuth) + if (e == StunAllocate::ErrorAuth) te = TurnClient::ErrorAuth; - else if(e == StunAllocate::ErrorRejected) + else if (e == StunAllocate::ErrorRejected) te = TurnClient::ErrorRejected; - else if(e == StunAllocate::ErrorProtocol) + else if (e == StunAllocate::ErrorProtocol) te = TurnClient::ErrorProtocol; - else if(e == StunAllocate::ErrorCapacity) + else if (e == StunAllocate::ErrorCapacity) te = TurnClient::ErrorCapacity; - else if(e == StunAllocate::ErrorMismatch) - { - if(!udp && handleRetry()) + else if (e == StunAllocate::ErrorMismatch) { + if (!udp && handleRetry()) return; te = TurnClient::ErrorMismatch; - } - else + } else te = TurnClient::ErrorGeneric; cleanup(); @@ -978,7 +829,7 @@ private slots: void allocate_permissionsChanged() { - if(debugLevel >= TurnClient::DL_Info) + if (debugLevel >= TurnClient::DL_Info) emit q->debugLine("PermissionsChanged"); tryChannelQueued(); @@ -987,72 +838,53 @@ private slots: void allocate_channelsChanged() { - if(debugLevel >= TurnClient::DL_Info) + if (debugLevel >= TurnClient::DL_Info) emit q->debugLine("ChannelsChanged"); tryWriteQueued(); } - void allocate_debugLine(const QString &line) - { - emit q->debugLine(line); - } + void allocate_debugLine(const QString &line) { emit q->debugLine(line); } }; -TurnClient::TurnClient(QObject *parent) : - QObject(parent) -{ - d = new Private(this); -} +TurnClient::TurnClient(QObject *parent) : QObject(parent) { d = new Private(this); } -TurnClient::~TurnClient() -{ - delete d; -} +TurnClient::~TurnClient() { delete d; } -void TurnClient::setProxy(const Proxy &proxy) -{ - d->proxy = proxy; -} +void TurnClient::setProxy(const Proxy &proxy) { d->proxy = proxy; } -void TurnClient::setClientSoftwareNameAndVersion(const QString &str) -{ - d->clientSoftware = str; -} +void TurnClient::setClientSoftwareNameAndVersion(const QString &str) { d->clientSoftware = str; } -void TurnClient::connectToHost(StunTransactionPool *pool, const QHostAddress &addr, int port) +void TurnClient::connectToHost(StunTransactionPool *pool, const TransportAddress &addr) { d->serverAddr = addr; - d->serverPort = port; - d->udp = true; - d->pool = pool; + d->udp = true; + d->pool = pool->sharedFromThis(); d->in.clear(); d->do_connect(); } -void TurnClient::connectToHost(const QHostAddress &addr, int port, Mode mode) +void TurnClient::connectToHost(const TransportAddress &addr, Mode mode) { d->serverAddr = addr; - d->serverPort = port; - d->udp = false; - d->mode = mode; + d->udp = false; + d->mode = mode; d->in.clear(); d->do_connect(); } -QByteArray TurnClient::processIncomingDatagram(const QByteArray &buf, bool notStun, QHostAddress *addr, int *port) -{ - return d->processNonPoolPacket(buf, notStun, addr, port); -} +const TransportAddress &TurnClient::serverAddress() const { return d->serverAddr; } -void TurnClient::outgoingDatagramsWritten(int count) +QByteArray TurnClient::processIncomingDatagram(const QByteArray &buf, bool notStun, TransportAddress &addr) { - d->udp_datagramsWritten(count); + return d->processNonPoolPacket(buf, notStun, addr); } +void TurnClient::outgoingDatagramsWritten(int count) { d->udp_datagramsWritten(count); } + QString TurnClient::realm() const { - if(d->pool) + if (d->pool) return d->pool->realm(); else return d->realm; @@ -1061,85 +893,67 @@ QString TurnClient::realm() const void TurnClient::setUsername(const QString &username) { d->user = username; - if(d->pool) + if (d->pool) d->pool->setUsername(d->user); } void TurnClient::setPassword(const QCA::SecureArray &password) { d->pass = password; - if(d->pool) + if (d->pool) d->pool->setPassword(d->pass); } void TurnClient::setRealm(const QString &realm) { d->realm = realm; - if(d->pool) + if (d->pool) d->pool->setRealm(d->realm); } -void TurnClient::continueAfterParams() +void TurnClient::continueAfterParams(const TransportAddress &addr) { Q_ASSERT(d->pool); - d->pool->continueAfterParams(); + d->pool->continueAfterParams(addr); } -void TurnClient::close() -{ - d->do_close(); -} +void TurnClient::close() { d->do_close(); } -StunAllocate *TurnClient::stunAllocate() -{ - return d->allocate; -} +StunAllocate *TurnClient::stunAllocate() { return d->allocate; } -void TurnClient::addChannelPeer(const QHostAddress &addr, int port) -{ - d->addChannelPeer(addr, port); -} +void TurnClient::addChannelPeer(const TransportAddress &addr) { d->addChannelPeer(addr); } -int TurnClient::packetsToRead() const -{ - return d->in.count(); -} +int TurnClient::packetsToRead() const { return d->in.count(); } -int TurnClient::packetsToWrite() const -{ - return d->outPending.count() + d->outPendingWrite; -} +int TurnClient::packetsToWrite() const { return d->outPending.count() + d->outPendingWrite; } -QByteArray TurnClient::read(QHostAddress *addr, int *port) +QByteArray TurnClient::read(TransportAddress &addr) { - if(!d->in.isEmpty()) - { + if (!d->in.isEmpty()) { Private::Packet p = d->in.takeFirst(); - *addr = p.addr; - *port = p.port; + addr = p.addr; return p.data; - } - else + } else return QByteArray(); } -void TurnClient::write(const QByteArray &buf, const QHostAddress &addr, int port) -{ - d->writeOrQueue(buf, addr, port); -} +void TurnClient::write(const QByteArray &buf, const TransportAddress &addr) { d->writeOrQueue(buf, addr); } -QString TurnClient::errorString() const -{ - return d->errorString; -} +QString TurnClient::errorString() const { return d->errorString; } void TurnClient::setDebugLevel(DebugLevel level) { d->debugLevel = level; - if(d->pool) + if (d->pool) d->pool->setDebugLevel((StunTransactionPool::DebugLevel)level); } +void TurnClient::changeThread(QThread *thread) +{ + if (d->pool) + d->pool->moveToThread(thread); + moveToThread(thread); } +} // namespace XMPP #include "turnclient.moc" diff --git a/src/irisnet/noncore/turnclient.h b/src/irisnet/noncore/turnclient.h index 80a07e90..51d08c6d 100644 --- a/src/irisnet/noncore/turnclient.h +++ b/src/irisnet/noncore/turnclient.h @@ -19,27 +19,26 @@ #ifndef TURNCLIENT_H #define TURNCLIENT_H -#include #include -#include #include +#include +#include + +#include "transportaddress.h" namespace QCA { - class SecureArray; +class SecureArray; } namespace XMPP { - -class StunTransactionPool; class StunAllocate; +class StunTransactionPool; -class TurnClient : public QObject -{ +class TurnClient : public QObject { Q_OBJECT public: - enum Error - { + enum Error { ErrorGeneric, ErrorHostNotFound, ErrorConnect, @@ -67,34 +66,19 @@ class TurnClient : public QObject ErrorMismatch }; - enum Mode - { - PlainMode, - TlsMode - }; + enum Mode { PlainMode, TlsMode }; - enum DebugLevel - { - DL_None, - DL_Info, - DL_Packet - }; + enum DebugLevel { DL_None, DL_Info, DL_Packet }; // adapted from XMPP::AdvancedConnector - class Proxy - { + class Proxy { public: - enum - { - None, - HttpConnect, - Socks - }; + enum { None, HttpConnect, Socks }; Proxy(); ~Proxy(); - int type() const; + int type() const; QString host() const; quint16 port() const; QString user() const; @@ -105,13 +89,13 @@ class TurnClient : public QObject void setUserPass(const QString &user, const QString &pass); private: - int t; + int t; QString v_host; - quint16 v_port; + quint16 v_port = 0; QString v_user, v_pass; }; - TurnClient(QObject *parent = 0); + TurnClient(QObject *parent = nullptr); ~TurnClient(); void setProxy(const Proxy &proxy); @@ -123,56 +107,59 @@ class TurnClient : public QObject // outgoingDatagramsWritten(). authentication happens through the // pool and not through this class. the turn addr/port is optional, // and used only for addr association with the pool - void connectToHost(StunTransactionPool *pool, const QHostAddress &addr = QHostAddress(), int port = -1); + void connectToHost(StunTransactionPool *pool, const TransportAddress &addr = TransportAddress()); // for TCP and TCP-TLS - void connectToHost(const QHostAddress &addr, int port, Mode mode = PlainMode); + void connectToHost(const TransportAddress &addr, Mode mode = PlainMode); + + const TransportAddress &serverAddress() const; // for UDP, use this function to process incoming packets instead of // read(). - QByteArray processIncomingDatagram(const QByteArray &buf, bool notStun, QHostAddress *addr, int *port); + QByteArray processIncomingDatagram(const QByteArray &buf, bool notStun, TransportAddress &addr); // call after writing datagrams from outgoingDatagram. not DOR-DS safe void outgoingDatagramsWritten(int count); QString realm() const; - void setUsername(const QString &username); - void setPassword(const QCA::SecureArray &password); - void setRealm(const QString &realm); - void continueAfterParams(); + void setUsername(const QString &username); + void setPassword(const QCA::SecureArray &password); + void setRealm(const QString &realm); + void continueAfterParams(const TransportAddress &addr); void close(); StunAllocate *stunAllocate(); - void addChannelPeer(const QHostAddress &addr, int port); + void addChannelPeer(const TransportAddress &addr); int packetsToRead() const; int packetsToWrite() const; // TCP mode only - QByteArray read(QHostAddress *addr, int *port); + QByteArray read(TransportAddress &addr); // for UDP, this call may emit outgoingDatagram() immediately (not // DOR-DS safe) - void write(const QByteArray &buf, const QHostAddress &addr, int port); + void write(const QByteArray &buf, const TransportAddress &addr); QString errorString() const; void setDebugLevel(DebugLevel level); // default DL_None + void changeThread(QThread *thread); signals: void connected(); // tcp connected void tlsHandshaken(); void closed(); - void needAuthParams(); - void retrying(); // mismatch error received, starting all over + void needAuthParams(const TransportAddress &addr); + void retrying(); // mismatch error received, starting all over void activated(); // ready for read/write // TCP mode only void readyRead(); - void packetsWritten(int count, const QHostAddress &addr, int port); + void packetsWritten(int count, const TransportAddress &addr); void error(XMPP::TurnClient::Error e); // data packets to be sent to the TURN server, UDP mode only @@ -186,7 +173,6 @@ class TurnClient : public QObject friend class Private; Private *d; }; +} // namespace XMPP -} - -#endif +#endif // TURNCLIENT_H diff --git a/src/irisnet/noncore/udpportreserver.cpp b/src/irisnet/noncore/udpportreserver.cpp index f7e48ba4..e1d45f96 100644 --- a/src/irisnet/noncore/udpportreserver.cpp +++ b/src/irisnet/noncore/udpportreserver.cpp @@ -18,42 +18,34 @@ #include "udpportreserver.h" -#include #include +#include namespace XMPP { - -class UdpPortReserver::Private : public QObject -{ +class UdpPortReserver::Private : public QObject { Q_OBJECT public: - class Item - { + class Item { public: - int port; // port to reserve + int port; // port to reserve bool lent; // list of sockets for this port, one socket per address. // note that we may have sockets bound for addresses // we no longer care about, if we are currently lending // them out - QList sockList; + QList sockList; // keep track of which addresses we lent out QList lentAddrs; - Item() : - port(-1), - lent(false) - { - } + Item() : port(-1), lent(false) { } bool haveAddress(const QHostAddress &addr) const { - foreach(const QUdpSocket *sock, sockList) - { - if(sock->localAddress() == addr) + for (const QUdpSocket *sock : sockList) { + if (sock->localAddress() == addr) return true; } @@ -61,36 +53,32 @@ class UdpPortReserver::Private : public QObject } }; - UdpPortReserver *q; + UdpPortReserver *q; QList addrs; - QList ports; // sorted - QList items; // in order sorted by port + QList ports; // sorted. - Private(UdpPortReserver *_q) : - QObject(_q), - q(_q) - { - } + // addrs * ports = all available sockets + + /** + * @brief items kind of ports slice over all provided addresses. + * When we request binding on one port it means it will be bound on one item (all its addresses). + * The items are sorted by port. + */ + QList items; + + Private(UdpPortReserver *_q) : QObject(_q), q(_q) { } ~Private() { - bool lendingAny = false; - foreach(const Item &i, items) - { - if(i.lent) - { - lendingAny = true; - break; - } - } + + bool lendingAny = std::any_of(items.begin(), items.end(), [](auto const &i) { return i.lent; }); Q_ASSERT(!lendingAny); - if(lendingAny) + if (lendingAny) abort(); - foreach(const Item &i, items) - { - foreach(QUdpSocket *sock, i.sockList) + for (const Item &i : std::as_const(items)) { + for (QUdpSocket *sock : i.sockList) sock->deleteLater(); } } @@ -106,34 +94,28 @@ class UdpPortReserver::Private : public QObject void updatePorts(const QList &newPorts) { QList added; - foreach(int x, newPorts) - { + for (int x : newPorts) { bool found = false; - foreach(const Item &i, items) - { - if(i.port == x) - { + for (const Item &i : std::as_const(items)) { + if (i.port == x) { found = true; break; } } - if(!found) + if (!found) added += x; } ports = newPorts; // keep ports in sorted order - qSort(ports); + std::sort(ports.begin(), ports.end()); - foreach(int x, added) - { + for (int x : std::as_const(added)) { int insert_before = items.count(); - for(int n = 0; n < items.count(); ++n) - { - if(x < items[n].port) - { + for (int n = 0; n < items.count(); ++n) { + if (x < items[n].port) { insert_before = n; break; } @@ -151,14 +133,12 @@ class UdpPortReserver::Private : public QObject bool reservedAll() const { bool ok = true; - foreach(const Item &i, items) - { + for (const Item &i : items) { // skip ports we don't care about - if(!ports.contains(i.port)) + if (!ports.contains(i.port)) continue; - if(!isReserved(i)) - { + if (!isReserved(i)) { ok = false; break; } @@ -167,59 +147,50 @@ class UdpPortReserver::Private : public QObject return ok; } - QList borrowSockets(int portCount, QObject *parent) + QList borrowSockets(int portCount, QObject *parent) { Q_ASSERT(portCount > 0); - QList out; + QList out; - if(portCount > 1) - { + if (portCount > 1) { // first try to see if we can find something all in a // row, starting with best alignment to worst - for(int align = portCount; align >= 2; align /= 2) - { + for (int align = portCount; align >= 2; align /= 2) { int at = findConsecutive(portCount, align); - if(at != -1) - { - for(int n = 0; n < portCount; ++n) + if (at != -1) { + for (int n = 0; n < portCount; ++n) out += lendItem(&items[at + n], parent); break; } } - if(out.isEmpty()) - { + if (out.isEmpty()) { // otherwise, try splitting them up into // smaller consecutive chunks int chunks[2]; chunks[0] = portCount / 2 + (portCount % 2); chunks[1] = portCount / 2; - for(int n = 0; n < 2; ++n) + for (int n = 0; n < 2; ++n) out += borrowSockets(chunks[n], parent); } - } - else - { + } else { // take the next available port int at = findConsecutive(1, 1); - if(at != -1) + if (at != -1) out += lendItem(&items[at], parent); } return out; } - void returnSockets(const QList &sockList) + void returnSockets(const QList &sockList) { - foreach(QUdpSocket *sock, sockList) - { + for (QUdpSocket *sock : sockList) { int at = -1; - for(int n = 0; n < items.count(); ++n) - { - if(items[n].sockList.contains(sock)) - { + for (int n = 0; n < items.count(); ++n) { + if (items[n].sockList.contains(sock)) { at = n; break; } @@ -241,7 +212,7 @@ class UdpPortReserver::Private : public QObject connect(sock, SIGNAL(readyRead()), SLOT(sock_readyRead())); i.lentAddrs.removeAll(a); - if(i.lentAddrs.isEmpty()) + if (i.lentAddrs.isEmpty()) i.lent = false; } @@ -254,34 +225,30 @@ private slots: QUdpSocket *sock = static_cast(sender()); // eat all packets - while(sock->hasPendingDatagrams()) - sock->readDatagram(0, 0); + while (sock->hasPendingDatagrams()) + sock->readDatagram(nullptr, 0); } private: void tryBind() { - for(int n = 0; n < items.count(); ++n) - { + for (int n = 0; n < items.count(); ++n) { Item &i = items[n]; // skip ports we don't care about - if(!ports.contains(i.port)) + if (!ports.contains(i.port)) continue; QList neededAddrs; - foreach(const QHostAddress &a, addrs) - { - if(!i.haveAddress(a)) + for (const QHostAddress &a : std::as_const(addrs)) { + if (!i.haveAddress(a)) neededAddrs += a; } - foreach(const QHostAddress &a, neededAddrs) - { + for (const QHostAddress &a : std::as_const(neededAddrs)) { QUdpSocket *sock = new QUdpSocket(q); - if(!sock->bind(a, i.port)) - { + if (!sock->bind(a, quint16(i.port))) { delete sock; continue; } @@ -295,14 +262,12 @@ private slots: void tryCleanup() { - for(int n = 0; n < items.count(); ++n) - { + for (int n = 0; n < items.count(); ++n) { Item &i = items[n]; // don't care about this port anymore? - if(!i.lent && !ports.contains(i.port)) - { - foreach(QUdpSocket *sock, i.sockList) + if (!i.lent && !ports.contains(i.port)) { + for (QUdpSocket *sock : std::as_const(i.sockList)) sock->deleteLater(); items.removeAt(n); @@ -311,14 +276,12 @@ private slots: } // any addresses we don't care about? - for(int k = 0; k < i.sockList.count(); ++k) - { + for (int k = 0; k < i.sockList.count(); ++k) { QUdpSocket *sock = i.sockList[k]; QHostAddress a = sock->localAddress(); - if(!addrs.contains(a) && !i.lentAddrs.contains(a)) - { + if (!addrs.contains(a) && !i.lentAddrs.contains(a)) { sock->deleteLater(); i.sockList.removeAt(k); --k; // adjust position @@ -331,12 +294,11 @@ private slots: bool isReserved(const Item &i) const { // must have desired addrs to consider a port reserved - if(addrs.isEmpty()) + if (addrs.isEmpty()) return false; - foreach(const QHostAddress &a, addrs) - { - if(!i.haveAddress(a)) + for (const QHostAddress &a : addrs) { + if (!i.haveAddress(a)) return false; } @@ -345,17 +307,16 @@ private slots: bool isConsecutive(int at, int count) const { - if(at + count > items.count()) + if (at + count > items.count()) return false; - for(int n = 0; n < count; ++n) - { + for (int n = 0; n < count; ++n) { const Item &i = items[at + n]; - if(i.lent || !isReserved(i)) + if (i.lent || !isReserved(i)) return false; - if(n > 0 && (i.port != items[at + n - 1].port + 1)) + if (n > 0 && (i.port != items[at + n - 1].port + 1)) return false; } @@ -364,22 +325,20 @@ private slots: int findConsecutive(int count, int align) const { - for(int n = 0; n < items.count(); n += align) - { - if(isConsecutive(n, count)) + for (int n = 0; n < items.count(); n += align) { + if (isConsecutive(n, count)) return n; } return -1; } - QList lendItem(Item *i, QObject *parent) + QList lendItem(Item *i, QObject *parent) { - QList out; + QList out; i->lent = true; - foreach(QUdpSocket *sock, i->sockList) - { + for (QUdpSocket *sock : std::as_const(i->sockList)) { i->lentAddrs += sock->localAddress(); sock->disconnect(this); sock->setParent(parent); @@ -390,50 +349,30 @@ private slots: } }; -UdpPortReserver::UdpPortReserver(QObject *parent) : - QObject(parent) -{ - d = new Private(this); -} +UdpPortReserver::UdpPortReserver(QObject *parent) : QObject(parent) { d = new Private(this); } -UdpPortReserver::~UdpPortReserver() -{ - delete d; -} +UdpPortReserver::~UdpPortReserver() { delete d; } -void UdpPortReserver::setAddresses(const QList &addrs) -{ - d->updateAddresses(addrs); -} +void UdpPortReserver::setAddresses(const QList &addrs) { d->updateAddresses(addrs); } void UdpPortReserver::setPorts(int start, int len) { QList ports; - for(int n = 0; n < len; ++n) + for (int n = 0; n < len; ++n) ports += start + n; setPorts(ports); } -void UdpPortReserver::setPorts(const QList &ports) -{ - d->updatePorts(ports); -} +void UdpPortReserver::setPorts(const QList &ports) { d->updatePorts(ports); } -bool UdpPortReserver::reservedAll() const -{ - return d->reservedAll(); -} +bool UdpPortReserver::reservedAll() const { return d->reservedAll(); } -QList UdpPortReserver::borrowSockets(int portCount, QObject *parent) +QList UdpPortReserver::borrowSockets(int portCount, QObject *parent) { return d->borrowSockets(portCount, parent); } -void UdpPortReserver::returnSockets(const QList &sockList) -{ - d->returnSockets(sockList); -} - -} +void UdpPortReserver::returnSockets(const QList &sockList) { d->returnSockets(sockList); } +} // namespace XMPP #include "udpportreserver.moc" diff --git a/src/irisnet/noncore/udpportreserver.h b/src/irisnet/noncore/udpportreserver.h index 57eed035..9043b9cd 100644 --- a/src/irisnet/noncore/udpportreserver.h +++ b/src/irisnet/noncore/udpportreserver.h @@ -19,25 +19,23 @@ #ifndef UDPPORTRESERVER_H #define UDPPORTRESERVER_H -#include #include +#include class QHostAddress; class QUdpSocket; namespace XMPP { - // call both setAddresses() and setPorts() at least once for socket // reservations to occur. at any time you can update the list of addresses // (interfaces) and ports to reserve. note that the port must be available // on all addresses in order for it to get reserved. // note: you must return all sockets back to this class before destructing -class UdpPortReserver : public QObject -{ +class UdpPortReserver : public QObject { Q_OBJECT public: - UdpPortReserver(QObject *parent = 0); + UdpPortReserver(QObject *parent = nullptr); ~UdpPortReserver(); void setAddresses(const QList &addrs); @@ -58,15 +56,14 @@ class UdpPortReserver : public QObject // able to bind to a port for it to be considered reserved, this // function always returns a list with a size that is a multiple of // the number of addresses. - QList borrowSockets(int portCount, QObject *parent = 0); + QList borrowSockets(int portCount, QObject *parent = nullptr); - void returnSockets(const QList &sockList); + void returnSockets(const QList &sockList); private: class Private; Private *d; }; +} // namespace XMPP -} - -#endif +#endif // UDPPORTRESERVER_H diff --git a/src/jdns.cmake b/src/jdns.cmake new file mode 100644 index 00000000..a9fb359c --- /dev/null +++ b/src/jdns.cmake @@ -0,0 +1,52 @@ +add_definitions(-DJDNS_STATIC) +set(QJDns_LIBRARY qjdns) +include_directories( + src/jdns/include/jdns +) +set(jdns_SRCS + src/jdns/src/jdns/jdns.c + src/jdns/src/jdns/jdns_mdnsd.c + src/jdns/src/jdns/jdns_packet.c + src/jdns/src/jdns/jdns_sys.c + src/jdns/src/jdns/jdns_util.c +) +set(jdns_PUBLIC_HEADERS + src/jdns/include/jdns/jdns.h + src/jdns/include/jdns/jdns_export.h +) +set(jdns_HEADERS + src/jdns/src/jdns/jdns_packet.h + src/jdns/src/jdns/jdns_mdnsd.h + src/jdns/src/jdns/jdns_p.h +) + +add_library(jdns STATIC ${jdns_SRCS} ${jdns_HEADERS} ${jdns_PUBLIC_HEADERS}) + +if(WIN32) + target_link_libraries(jdns ws2_32 advapi32) +endif() +set(qjdns_MOC_HDRS + src/jdns/include/jdns/qjdns.h + src/jdns/include/jdns/qjdnsshared.h + src/jdns/src/qjdns/qjdns_p.h + src/jdns/src/qjdns/qjdnsshared_p.h +) + +set(qjdns_SRCS + src/jdns/src/qjdns/qjdns.cpp + src/jdns/src/qjdns/qjdns_sock.cpp + src/jdns/src/qjdns/qjdnsshared.cpp +) + +set(qjdns_PUBLIC_HEADERS + src/jdns/include/jdns/qjdns.h + src/jdns/include/jdns/qjdnsshared.h +) +set(qjdns_HEADERS + src/jdns/src/qjdns/qjdns_sock.h +) + +add_library(${QJDns_LIBRARY} STATIC ${qjdns_SRCS} ${qjdns_MOC_SRCS} ${qjdns_MOC_HDRS} ${qjdns_PUBLIC_HEADERS}) + +target_link_libraries(${QJDns_LIBRARY} Qt5::Core Qt5::Network) +target_link_libraries(${QJDns_LIBRARY} jdns) diff --git a/src/jdns/CMakeLists.txt b/src/jdns/CMakeLists.txt index 43b99914..13789762 100644 --- a/src/jdns/CMakeLists.txt +++ b/src/jdns/CMakeLists.txt @@ -74,7 +74,6 @@ set(CMAKE_INCLUDE_DIRECTORIES_PROJECT_BEFORE ON) set(JDNS_INCLUDEDIR "${CMAKE_CURRENT_SOURCE_DIR}/include/jdns" ) - #add extra search paths for libraries and includes set(LIB_SUFFIX "" CACHE STRING "Define suffix of directory name (32/64)" ) set(BIN_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/bin" CACHE STRING "Directory where binary will install") @@ -104,7 +103,6 @@ set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin" ) set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib" ) set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib" ) - # pkg-config if(NOT WIN32) configure_file(${CMAKE_CURRENT_SOURCE_DIR}/jdns.pc.in diff --git a/src/jdns/include/jdns/jdns.h b/src/jdns/include/jdns/jdns.h index defcbab8..f1bc5c86 100644 --- a/src/jdns/include/jdns/jdns.h +++ b/src/jdns/include/jdns/jdns.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005,2006 Justin Karneges + * Copyright (C) 2005-2006 Justin Karneges * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the @@ -505,4 +505,4 @@ JDNS_EXPORT void jdns_set_hold_ids_enabled(jdns_session_t *s, int enabled); } #endif -#endif +#endif // JDNS_H diff --git a/src/jdns/include/jdns/jdns_export.h b/src/jdns/include/jdns/jdns_export.h index 7a9ee396..c5d79a13 100644 --- a/src/jdns/include/jdns/jdns_export.h +++ b/src/jdns/include/jdns/jdns_export.h @@ -49,4 +49,4 @@ # define JDNS_EXPORT #endif -#endif +#endif // JDNS_EXPORT_H diff --git a/src/jdns/include/jdns/qjdns.h b/src/jdns/include/jdns/qjdns.h index b8c3cf92..13a26261 100644 --- a/src/jdns/include/jdns/qjdns.h +++ b/src/jdns/include/jdns/qjdns.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005,2006 Justin Karneges + * Copyright (C) 2005-2006 Justin Karneges * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the @@ -27,6 +27,7 @@ #define QJDNS_H #include "jdns_export.h" + #include #include @@ -156,4 +157,4 @@ class JDNS_EXPORT QJDns : public QObject Private *d; }; -#endif +#endif // QJDNS_H diff --git a/src/jdns/include/jdns/qjdnsshared.h b/src/jdns/include/jdns/qjdnsshared.h index e1e67a7a..335d91bb 100644 --- a/src/jdns/include/jdns/qjdnsshared.h +++ b/src/jdns/include/jdns/qjdnsshared.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006,2007 Justin Karneges + * Copyright (C) 2006-2007 Justin Karneges * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the @@ -27,9 +27,9 @@ #include "qjdns.h" class QJDnsShared; +class QJDnsSharedDebugPrivate; class QJDnsSharedPrivate; class QJDnsSharedRequestPrivate; -class QJDnsSharedDebugPrivate; /** \brief Collects debugging information from QJDnsShared @@ -511,4 +511,4 @@ QStringList finalDebugLines = jdnsSharedDebug.readDebugLines(); QJDnsSharedPrivate *d; }; -#endif +#endif // QJDNSSHARED_H diff --git a/src/jdns/src/jdns/CMakeLists.txt b/src/jdns/src/jdns/CMakeLists.txt index 3a68f930..7bf91584 100644 --- a/src/jdns/src/jdns/CMakeLists.txt +++ b/src/jdns/src/jdns/CMakeLists.txt @@ -6,7 +6,6 @@ set(jdns_SRCS jdns_util.c ) - set(jdns_PUBLIC_HEADERS "${JDNS_INCLUDEDIR}/jdns.h" "${JDNS_INCLUDEDIR}/jdns_export.h" diff --git a/src/jdns/src/jdns/jdns.c b/src/jdns/src/jdns/jdns.c index 52f223a4..3da4c25c 100644 --- a/src/jdns/src/jdns/jdns.c +++ b/src/jdns/src/jdns/jdns.c @@ -21,13 +21,12 @@ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +#include "jdns_mdnsd.h" #include "jdns_p.h" +#include "jdns_packet.h" #include -#include "jdns_packet.h" -#include "jdns_mdnsd.h" - #define JDNS_UDP_UNI_OUT_MAX 512 #define JDNS_UDP_UNI_IN_MAX 16384 #define JDNS_UDP_MUL_OUT_MAX 9000 diff --git a/src/jdns/src/jdns/jdns_mdnsd.c b/src/jdns/src/jdns/jdns_mdnsd.c index 3dd581d0..e818d5dd 100644 --- a/src/jdns/src/jdns/jdns_mdnsd.c +++ b/src/jdns/src/jdns/jdns_mdnsd.c @@ -1,6 +1,6 @@ /* * Copyright (C) 2005 Jeremie Miller - * Copyright (C) 2005,2006 Justin Karneges + * Copyright (C) 2005-2006 Justin Karneges * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the @@ -638,7 +638,6 @@ int _r_out(mdnsd d, jdns_packet_t *m, mdnsdr *list) return ret; } - mdnsd mdnsd_new(int class, int frame, int port, int (*time_now)(mdnsd d, void *arg), int (*rand_int)(mdnsd d, void *arg), void *arg) { //int i; diff --git a/src/jdns/src/jdns/jdns_mdnsd.h b/src/jdns/src/jdns/jdns_mdnsd.h index 2870dc70..d311c405 100644 --- a/src/jdns/src/jdns/jdns_mdnsd.h +++ b/src/jdns/src/jdns/jdns_mdnsd.h @@ -1,6 +1,6 @@ /* * Copyright (C) 2005 Jeremie Miller - * Copyright (C) 2005,2006 Justin Karneges + * Copyright (C) 2005-2006 Justin Karneges * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the @@ -116,5 +116,4 @@ void mdnsd_set_srv(mdnsd d, mdnsdr r, int priority, int weight, int port, char * // /////////// - -#endif +#endif // JDNS_MDNSD_H diff --git a/src/jdns/src/jdns/jdns_p.h b/src/jdns/src/jdns/jdns_p.h index 0ae7e948..ef2ac093 100644 --- a/src/jdns/src/jdns/jdns_p.h +++ b/src/jdns/src/jdns/jdns_p.h @@ -24,11 +24,14 @@ #ifndef JDNS_P_H #define JDNS_P_H +#include "jdns.h" +#include "jdns_packet.h" + +#include +#include #include #include #include -#include -#include #if defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__NT__) # define JDNS_OS_WIN @@ -50,17 +53,14 @@ # define JDNS_OS_MAC #endif -#ifdef JDNS_OS_WIN -# include -#endif - #ifdef JDNS_OS_UNIX -# include # include +# include #endif -#include "jdns.h" -#include "jdns_packet.h" +#ifdef JDNS_OS_WIN +# include +#endif // jdns_util.c void *jdns_alloc(int size); @@ -96,4 +96,4 @@ void jdns_response_remove_answer(jdns_response_t *r, int pos); #define _ustrlen(str) strlen((const char *)str) #define _ustrcmp(a, b) strcmp((const char *)a, (const char *)b) -#endif +#endif // JDNS_P_H diff --git a/src/jdns/src/jdns/jdns_packet.h b/src/jdns/src/jdns/jdns_packet.h index 113c55eb..23eb059a 100644 --- a/src/jdns/src/jdns/jdns_packet.h +++ b/src/jdns/src/jdns/jdns_packet.h @@ -113,4 +113,4 @@ void jdns_packet_delete(jdns_packet_t *a); int jdns_packet_import(jdns_packet_t **a, const unsigned char *data, int size); // 0 on fail int jdns_packet_export(jdns_packet_t *a, int maxsize); // 0 on fail -#endif +#endif // JDNS_PACKET_H diff --git a/src/jdns/src/jdns/jdns_sys.c b/src/jdns/src/jdns/jdns_sys.c index d755c1c4..bf345819 100644 --- a/src/jdns/src/jdns/jdns_sys.c +++ b/src/jdns/src/jdns/jdns_sys.c @@ -113,15 +113,14 @@ read /etc/hosts manually: #include "jdns_p.h" -#ifdef JDNS_OS_WIN -# include -#endif - #ifdef JDNS_OS_UNIX -# include # include -# include # include +# include +# include +#endif +#ifdef JDNS_OS_WIN +# include #endif #define string_indexOf jdns_string_indexOf diff --git a/src/jdns/src/jdns/jdns_util.c b/src/jdns/src/jdns/jdns_util.c index af8cd09a..701cd126 100644 --- a/src/jdns/src/jdns/jdns_util.c +++ b/src/jdns/src/jdns/jdns_util.c @@ -22,7 +22,6 @@ */ #include "jdns_p.h" - #include "jdns_packet.h" //---------------------------------------------------------------------------- diff --git a/src/jdns/src/qjdns/qjdns.cpp b/src/jdns/src/qjdns/qjdns.cpp index a6e61d15..4f7572ef 100644 --- a/src/jdns/src/qjdns/qjdns.cpp +++ b/src/jdns/src/qjdns/qjdns.cpp @@ -22,12 +22,10 @@ */ #include "qjdns_p.h" - -#include #include "qjdns_sock.h" -// for fprintf -#include +#include // for fprintf +#include // safeobj stuff, from qca diff --git a/src/jdns/src/qjdns/qjdns_p.h b/src/jdns/src/qjdns/qjdns_p.h index 0cdaa73f..94669c85 100644 --- a/src/jdns/src/qjdns/qjdns_p.h +++ b/src/jdns/src/qjdns/qjdns_p.h @@ -26,13 +26,14 @@ #include "jdns.h" #include "qjdns.h" + +#include #include -#include #include -#include +#include -class QUdpSocket; class QTimer; +class QUdpSocket; class SafeTimer : public QObject { @@ -131,4 +132,4 @@ private slots: static int cb_udp_write(jdns_session_t *, void *app, int handle, const jdns_address_t *addr, int port, unsigned char *buf, int bufsize); }; -#endif +#endif // QJDNS_P_H diff --git a/src/jdns/src/qjdns/qjdns_sock.cpp b/src/jdns/src/qjdns/qjdns_sock.cpp index 8c2991f3..09cd96a8 100644 --- a/src/jdns/src/qjdns/qjdns_sock.cpp +++ b/src/jdns/src/qjdns/qjdns_sock.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005,2006 Justin Karneges + * Copyright (C) 2005-2006 Justin Karneges * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the @@ -27,21 +27,19 @@ #include #include #include - -#ifdef Q_OS_WIN -# include -# include -#endif - #ifdef Q_OS_UNIX -# include -# include -# include -# include -# include +# include # include +# include +# include # include -# include +# include +# include +# include +#endif +#ifdef Q_OS_WIN +# include +# include #endif #ifdef Q_OS_HAIKU diff --git a/src/jdns/src/qjdns/qjdns_sock.h b/src/jdns/src/qjdns/qjdns_sock.h index 13d388d6..dcbf9b74 100644 --- a/src/jdns/src/qjdns/qjdns_sock.h +++ b/src/jdns/src/qjdns/qjdns_sock.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005,2006 Justin Karneges + * Copyright (C) 2005-2006 Justin Karneges * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the @@ -30,4 +30,4 @@ bool qjdns_sock_setTTL4(int s, int ttl); bool qjdns_sock_setTTL6(int s, int ttl); bool qjdns_sock_setIPv6Only(int s); -#endif +#endif // QJDNS_SOCK_H diff --git a/src/jdns/src/qjdns/qjdnsshared.cpp b/src/jdns/src/qjdns/qjdnsshared.cpp index 8c9ed3ca..a47f686f 100644 --- a/src/jdns/src/qjdns/qjdnsshared.cpp +++ b/src/jdns/src/qjdns/qjdnsshared.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006-2008 Justin Karneges + * Copyright (C) 2006-2008 Justin Karneges * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the @@ -100,8 +100,7 @@ static bool matchRecordExceptTtl(const QJDns::Record &a, const QJDns::Record &b) static void getHex(unsigned char in, char *hi, char *lo) { - QString str; - str.sprintf("%02x", in); + auto str = QString::asprintf("%02x", in); if (!str.isEmpty()) { *hi = str[0].toLatin1(); @@ -159,7 +158,6 @@ static inline uint qHash(const Handle &key) return ((h1 << 16) | (h1 >> 16)) ^ h2; } - //---------------------------------------------------------------------------- // JDnsShutdown //---------------------------------------------------------------------------- diff --git a/src/jdns/src/qjdns/qjdnsshared_p.h b/src/jdns/src/qjdns/qjdnsshared_p.h index 7cf2e729..98024460 100644 --- a/src/jdns/src/qjdns/qjdnsshared_p.h +++ b/src/jdns/src/qjdns/qjdnsshared_p.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006-2008 Justin Karneges + * Copyright (C) 2006-2008 Justin Karneges * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the @@ -27,13 +27,14 @@ // SafeTimer #include "qjdns_p.h" #include "qjdnsshared.h" -#include -#include -#include + +#include #include -#include +#include #include -#include +#include +#include +#include class JDnsShutdownAgent : public QObject { @@ -230,4 +231,4 @@ private slots: void lateTimer_timeout(); }; -#endif +#endif // QJDNSSHARED_P_H diff --git a/src/jdns/tools/jdns/main.cpp b/src/jdns/tools/jdns/main.cpp index 5259f5f0..5bd3bdb2 100644 --- a/src/jdns/tools/jdns/main.cpp +++ b/src/jdns/tools/jdns/main.cpp @@ -22,9 +22,11 @@ */ #include "main.h" + +#include "qjdns.h" + #include #include -#include "qjdns.h" QString dataToString(const QByteArray &buf) { @@ -216,7 +218,6 @@ void App::start() QTimer::singleShot(quit_time * 1000, this, SLOT(doShutdown())); } - void App::jdns_resultsReady(int id, const QJDns::Response &results) { printf("[%d] Results\n", id); diff --git a/src/jdns/tools/jdns/main.h b/src/jdns/tools/jdns/main.h index 018965e9..e208fe94 100644 --- a/src/jdns/tools/jdns/main.h +++ b/src/jdns/tools/jdns/main.h @@ -25,6 +25,7 @@ #define MAIN_H #include "qjdns.h" + #include #include diff --git a/src/libbase.pri b/src/libbase.pri deleted file mode 100644 index 62b58536..00000000 --- a/src/libbase.pri +++ /dev/null @@ -1,8 +0,0 @@ -IRIS_BASE = $$PWD/.. - -isEmpty(top_iris_builddir):top_iris_builddir = . -include($$top_iris_builddir/../conf.pri) - -include(../common.pri) - -QMAKE_MACOSX_DEPLOYMENT_TARGET = 10.9 diff --git a/src/src.pro b/src/src.pro deleted file mode 100644 index a5a5ca0c..00000000 --- a/src/src.pro +++ /dev/null @@ -1,10 +0,0 @@ -TEMPLATE = subdirs - -include(libbase.pri) - -sub_irisnet.subdir = irisnet -sub_xmpp.subdir = xmpp -sub_xmpp.depends = sub_irisnet - -SUBDIRS += sub_irisnet -!iris_bundle:SUBDIRS += sub_xmpp diff --git a/src/xmpp/CMakeLists.txt b/src/xmpp/CMakeLists.txt index 3ec1b6a9..5c4cffec 100644 --- a/src/xmpp/CMakeLists.txt +++ b/src/xmpp/CMakeLists.txt @@ -1,19 +1,16 @@ -add_definitions(-DXMPP_TEST) +cmake_minimum_required(VERSION 3.10.0) find_package(ZLIB REQUIRED) -include_directories( - ${CMAKE_CURRENT_BINARY_DIR} - ${CMAKE_CURRENT_LIST_DIR} - .. - ../irisnet/corelib - xmpp-core - xmpp-im - ${IDN_INCLUDE_DIRS} - ${ZLIB_INCLUDE_DIRS} - ${QCA_INCLUDES} - ) -set(HEADERS +if(IS_SUBPROJECT) + add_library(iris STATIC) +else() + add_library(iris) +endif() + +set(XMPP_JID_HEADERS jid/jid.h) + +set(XMPP_CORE_HEADERS xmpp-core/parser.h xmpp-core/protocol.h xmpp-core/sm.h @@ -21,6 +18,14 @@ set(HEADERS xmpp-core/xmlprotocol.h xmpp-core/xmpp_stanza.h + xmpp-core/compressionhandler.h + xmpp-core/securestream.h + xmpp-core/xmpp.h + xmpp-core/xmpp_clientstream.h + xmpp-core/xmpp_stream.h +) + +set(XMPP_IM_HEADERS xmpp-im/xmpp_address.h xmpp-im/xmpp_hash.h xmpp-im/xmpp_thumbs.h @@ -30,6 +35,7 @@ set(HEADERS xmpp-im/xmpp_chatstate.h xmpp-im/xmpp_discoitem.h xmpp-im/xmpp_features.h + xmpp-im/xmpp_form.h xmpp-im/xmpp_forwarding.h xmpp-im/xmpp_htmlelement.h xmpp-im/xmpp_httpauthrequest.h @@ -37,6 +43,7 @@ set(HEADERS xmpp-im/xmpp_liverosteritem.h xmpp-im/xmpp_message.h xmpp-im/xmpp_muc.h + xmpp-im/xmpp_reference.h xmpp-im/xmpp_pubsubitem.h xmpp-im/xmpp_pubsubretraction.h xmpp-im/xmpp_receipts.h @@ -51,32 +58,10 @@ set(HEADERS xmpp-im/xmpp_vcard.h xmpp-im/xmpp_xdata.h xmpp-im/xmpp_xmlcommon.h - - base/randomnumbergenerator.h - base/randrandomnumbergenerator.h - base/timezone.h - + xmpp-im/xmpp_encryption.h + xmpp-im/xmpp_externalservicediscovery.h + xmpp-im/stundisco.h xmpp-im/im.h - - jid/jid.h - - sasl/digestmd5proplist.h - sasl/digestmd5response.h - sasl/plainmessage.h - sasl/scramsha1message.h - sasl/scramsha1response.h - sasl/scramsha1signature.h - - blake2/blake2qt.h - blake2/blake2.h - blake2/blake2-impl.h - - xmpp-core/compressionhandler.h - xmpp-core/securestream.h - xmpp-core/xmpp.h - xmpp-core/xmpp_clientstream.h - xmpp-core/xmpp_stream.h - xmpp-im/xmpp_caps.h xmpp-im/filetransfer.h xmpp-im/httpfileupload.h @@ -90,14 +75,37 @@ set(HEADERS xmpp-im/xmpp_task.h xmpp-im/xmpp_tasks.h xmpp-im/jingle.h + xmpp-im/jingle-connection.h + xmpp-im/jingle-transport.h + xmpp-im/jingle-nstransportslist.h + xmpp-im/jingle-application.h + xmpp-im/jingle-session.h xmpp-im/jingle-ft.h + xmpp-im/jingle-ice.h xmpp-im/jingle-s5b.h + xmpp-im/jingle-ibb.h + xmpp-im/jingle-file.h + xmpp-im/jingle-sctp.h +) +set(XMPP_HEADERS_PRIVATE + sasl/digestmd5proplist.h + sasl/digestmd5response.h + sasl/plainmessage.h + sasl/scramsha1message.h + sasl/scramsha1response.h + sasl/scramsha1signature.h zlib/zlibcompressor.h zlib/zlibdecompressor.h - ) + blake2/blake2qt.h + base/timezone.h +) -set(SOURCES +target_sources(iris PRIVATE + ${XMPP_JID_HEADERS} + ${XMPP_CORE_HEADERS} + ${XMPP_IM_HEADERS} + ${XMPP_HEADERS_PRIVATE} xmpp-core/compressionhandler.cpp xmpp-core/connector.cpp xmpp-core/parser.cpp @@ -118,8 +126,10 @@ set(SOURCES xmpp-im/xmpp_carbons.cpp xmpp-im/xmpp_discoinfotask.cpp xmpp-im/xmpp_discoitem.cpp + xmpp-im/xmpp_hash.cpp xmpp-im/xmpp_ibb.cpp xmpp-im/xmpp_forwarding.cpp + xmpp-im/xmpp_reference.cpp xmpp-im/xmpp_serverinfomanager.cpp xmpp-im/xmpp_subsets.cpp xmpp-im/xmpp_task.cpp @@ -127,18 +137,28 @@ set(SOURCES xmpp-im/xmpp_vcard.cpp xmpp-im/xmpp_xdata.cpp xmpp-im/xmpp_xmlcommon.cpp - xmpp-im/jingle-s5b.cpp + xmpp-im/xmpp_encryption.cpp + xmpp-im/xmpp_externalservicediscovery.cpp + xmpp-im/stundisco.cpp + + xmpp-im/jingle.cpp + xmpp-im/jingle-connection.cpp + xmpp-im/jingle-session.cpp + xmpp-im/jingle-application.cpp + xmpp-im/jingle-transport.cpp + xmpp-im/jingle-nstransportslist.cpp xmpp-im/jingle-ft.cpp + xmpp-im/jingle-ice.cpp + xmpp-im/jingle-s5b.cpp + xmpp-im/jingle-ibb.cpp + xmpp-im/jingle-file.cpp - base/randomnumbergenerator.cpp base/timezone.cpp zlib/zlibcompressor.cpp zlib/zlibdecompressor.cpp blake2/blake2qt.cpp - blake2/blake2b-ref.c - blake2/blake2s-ref.c jid/jid.cpp @@ -154,20 +174,84 @@ set(SOURCES xmpp-im/s5b.cpp xmpp-im/xmpp_features.cpp - xmpp-im/jingle.cpp +) + +if(IRIS_ENABLE_JINGLE_SCTP) + target_compile_definitions(iris PRIVATE JINGLE_SCTP) + target_sources(iris PRIVATE + xmpp-im/jingle-sctp.cpp + xmpp-im/jingle-sctp-association_p.cpp + xmpp-im/jingle-sctp-association_p.h + xmpp-im/jingle-webrtc-datachannel_p.cpp + xmpp-im/jingle-webrtc-datachannel_p.h + ) +endif() + +if(B2_FOUND) + message(STATUS "Building with system blake2 library") + target_link_libraries(iris PRIVATE ${B2_LIBRARY}) +else() + if(NOT IRIS_BUNDLED_QCA) + message(STATUS "No system blake2 and bundled QCA is disabled. Expect slow hashing.") + endif() + target_sources(iris PRIVATE + blake2/blake2b-ref.c + blake2/blake2s-ref.c ) +endif() -add_library(iris - STATIC - ${HEADERS} - ${SOURCES} +target_link_libraries(iris + PRIVATE + $ + $ + ZLIB::ZLIB + PUBLIC + ${Qca_LIBRARY} + Qt${QT_VERSION_MAJOR}::Core Qt${QT_VERSION_MAJOR}::Gui Qt${QT_VERSION_MAJOR}::Network Qt${QT_VERSION_MAJOR}::Xml ) -if( SEPARATE_QJDNS AND WIN32) - set(EXTRA_LDFLAGS ws2_32) +if(IRIS_BUNDLED_QCA) + add_dependencies(iris QcaProject) + target_include_directories(iris PUBLIC ${Qca_INCLUDE_DIR}) + target_link_libraries(iris PUBLIC OpenSSL::SSL) +endif() + +if(IRIS_ENABLE_DEBUG) + target_compile_definitions(iris PRIVATE XMPP_TEST) endif() -target_link_libraries(iris ${IDN_LIBRARY} ${ZLIB_LIBRARY} ${EXTRA_LDFLAGS}) -target_link_libraries(iris irisnet Qt5::Core Qt5::Gui Qt5::Network Qt5::Xml ${qca_LIB}) -target_compile_definitions(iris INTERFACE IRISNET_STATIC) -target_include_directories(iris INTERFACE ../../include ../../include/iris ..) +target_compile_definitions(iris PUBLIC IRISNET_STATIC QSTRINGPREP_BUILDING) + +if(IS_SUBPROJECT) + target_include_directories(iris + PUBLIC + ${iris_SOURCE_DIR}/include + ${iris_SOURCE_DIR}/src + ${Qca_INCLUDE_DIR} + ) + target_include_directories(iris + PRIVATE + ${iris_SOURCE_DIR}/include/iris + ) +else() + target_include_directories(iris + PRIVATE + ${iris_SOURCE_DIR}/include + ${iris_SOURCE_DIR}/include/iris + ${iris_SOURCE_DIR}/src + ${Qca_INCLUDE_DIR} + ) +endif() +target_include_directories(iris PUBLIC $) + +if (IRIS_ENABLE_INSTALL) + install(FILES ${XMPP_JID_HEADERS} + DESTINATION ${IRIS_INSTALL_INCLUDEDIR}/xmpp/jid + ) + install(FILES ${XMPP_CORE_HEADERS} + DESTINATION ${IRIS_INSTALL_INCLUDEDIR}/xmpp/xmpp-core + ) + install(FILES ${XMPP_IM_HEADERS} + DESTINATION ${IRIS_INSTALL_INCLUDEDIR}/xmpp/xmpp-im + ) +endif() diff --git a/src/xmpp/base/base.pri b/src/xmpp/base/base.pri deleted file mode 100644 index e9c463d4..00000000 --- a/src/xmpp/base/base.pri +++ /dev/null @@ -1,11 +0,0 @@ -INCLUDEPATH += $$PWD/../.. -DEPENDPATH += $$PWD/../.. - -HEADERS += \ - $$PWD/randomnumbergenerator.h \ - $$PWD/randrandomnumbergenerator.h \ - $$PWD/timezone.h - -SOURCES += \ - $$PWD/randomnumbergenerator.cpp \ - $$PWD/timezone.cpp diff --git a/src/xmpp/base/randomnumbergenerator.h b/src/xmpp/base/randomnumbergenerator.h deleted file mode 100644 index 5d3fb337..00000000 --- a/src/xmpp/base/randomnumbergenerator.h +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright (C) 2008 Remko Troncon - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this library. If not, see . - * - */ - -#ifndef RANDOMNUMBERGENERATOR_H -#define RANDOMNUMBERGENERATOR_H - -namespace XMPP { - class RandomNumberGenerator - { - public: - virtual ~RandomNumberGenerator(); - - double generateNumberBetween(double a, double b) const; - - protected: - virtual double generateNumber() const = 0; - virtual double getMaximumGeneratedNumber() const = 0; - }; -} - -#endif diff --git a/src/xmpp/base/randrandomnumbergenerator.h b/src/xmpp/base/randrandomnumbergenerator.h deleted file mode 100644 index 6306e1ae..00000000 --- a/src/xmpp/base/randrandomnumbergenerator.h +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright (C) 2008 Remko Troncon - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this library. If not, see . - * - */ - -#ifndef RANDRANDOMNUMBERGENERATOR_H -#define RANDRANDOMNUMBERGENERATOR_H - -#include "xmpp/base/randomnumbergenerator.h" - -namespace XMPP { - class RandRandomNumberGenerator : public RandomNumberGenerator - { - public: - RandRandomNumberGenerator() {} - - virtual double generateNumber() const { - return rand(); - } - - virtual double getMaximumGeneratedNumber() const { - return RAND_MAX; - } - }; -} - -#endif diff --git a/src/xmpp/base/timezone.cpp b/src/xmpp/base/timezone.cpp index fb705254..bf647592 100644 --- a/src/xmpp/base/timezone.cpp +++ b/src/xmpp/base/timezone.cpp @@ -16,93 +16,14 @@ * */ -#include - -#if QT_VERSION < QT_VERSION_CHECK(5, 2, 0) -#include -#include -#ifdef Q_OS_UNIX -#include -#endif -#ifdef Q_OS_WIN -#include -#endif -#else -#include -#endif - #include "timezone.h" -#if QT_VERSION < QT_VERSION_CHECK(5,2,0) -static bool inited = false; -static int timezone_offset_; -static QString timezone_str_; - -static void init() -{ -#if defined(Q_OS_UNIX) - time_t x; - time(&x); - char str[256]; - char fmt[32]; - int size; - strcpy(fmt, "%z"); - size = strftime(str, 256, fmt, localtime(&x)); - if(size && strncmp(fmt, str, size)) { - timezone_offset_ = QByteArray::fromRawData(str + 1, 2).toInt() * 60 + QByteArray::fromRawData(str + 3, 2).toInt(); - if(str[0] == '-') - timezone_offset_ = -timezone_offset_; - } - strcpy(fmt, "%Z"); - strftime(str, 256, fmt, localtime(&x)); - if(strcmp(fmt, str)) - timezone_str_ = str; - -#elif defined(Q_OS_WIN) - TIME_ZONE_INFORMATION i; - memset(&i, 0, sizeof(i)); - bool inDST = (GetTimeZoneInformation(&i) == TIME_ZONE_ID_DAYLIGHT); - int bias = i.Bias; - if(inDST) - bias += i.DaylightBias; - timezone_offset_ = -bias; - timezone_str_ = ""; - for(int n = 0; n < 32; ++n) { - int w = inDST ? i.DaylightName[n] : i.StandardName[n]; - if(w == 0) - break; - timezone_str_ += QChar(w); - } - -#else - qWarning("Failed to properly init timezone data. Use UTC offset instead"); - inited = true; - timezone_offset_ = 0; - timezone_str_ = QLatin1String("N/A"); -#endif -} -#endif +#include +#include -int TimeZone::offsetFromUtc() -{ -#if QT_VERSION < QT_VERSION_CHECK(5,2,0) - if (!inited) { - init(); - } - return timezone_offset_; -#else - return QTimeZone::systemTimeZone().offsetFromUtc(QDateTime::currentDateTime()) / 60; -#endif -} +int TimeZone::offsetFromUtc() { return QTimeZone::systemTimeZone().offsetFromUtc(QDateTime::currentDateTime()) / 60; } -QString TimeZone::abbreviation() -{ -#if QT_VERSION < QT_VERSION_CHECK(5,2,0) - return timezone_str_; -#else - return QTimeZone::systemTimeZone().abbreviation(QDateTime::currentDateTime()); -#endif -} +QString TimeZone::abbreviation() { return QTimeZone::systemTimeZone().abbreviation(QDateTime::currentDateTime()); } int TimeZone::tzdToInt(const QString &tzd) { diff --git a/src/xmpp/base/timezone.h b/src/xmpp/base/timezone.h index d49cdc10..6a20d9a8 100644 --- a/src/xmpp/base/timezone.h +++ b/src/xmpp/base/timezone.h @@ -19,14 +19,15 @@ #ifndef IRIS_TIMEZONE_H #define IRIS_TIMEZONE_H +// TODO <5.2.0 code was stripped away. remaining has to be put to some common utils + #include -class TimeZone -{ +class TimeZone { public: - static int offsetFromUtc(); // in minutes + static int offsetFromUtc(); // in minutes static QString abbreviation(); - static int tzdToInt(const QString &tzd); + static int tzdToInt(const QString &tzd); }; #endif // IRIS_TIMEZONE_H diff --git a/src/xmpp/base/unittest/incrementingrandomnumbergenerator.h b/src/xmpp/base/unittest/incrementingrandomnumbergenerator.h deleted file mode 100644 index 6141481f..00000000 --- a/src/xmpp/base/unittest/incrementingrandomnumbergenerator.h +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright (C) 2008 Remko Troncon - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this library. If not, see . - * - */ - -#ifndef INCREMENTINGRANDOMNUMBERGENERATOR_H -#define INCREMENTINGRANDOMNUMBERGENERATOR_H - -#include - -#include "xmpp/base/randomnumbergenerator.h" - -namespace XMPP { - class IncrementingRandomNumberGenerator : public RandomNumberGenerator - { - public: - IncrementingRandomNumberGenerator(int maximumNumber = 10) : maximumNumber_(maximumNumber), currentNumber_(maximumNumber_) {} - - virtual double generateNumber() const { - currentNumber_ = (currentNumber_ + 1) % (maximumNumber_ + 1); - return currentNumber_; - } - - virtual double getMaximumGeneratedNumber() const { - return maximumNumber_; - } - - private: - int maximumNumber_; - mutable int currentNumber_; - }; -} - -#endif diff --git a/src/xmpp/base/unittest/randomnumbergeneratortest.cpp b/src/xmpp/base/unittest/randomnumbergeneratortest.cpp deleted file mode 100644 index d904e26e..00000000 --- a/src/xmpp/base/unittest/randomnumbergeneratortest.cpp +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright (C) 2008 Remko Troncon - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this library. If not, see . - * - */ - -#include -#include - -#include "qttestutil/qttestutil.h" -#include "xmpp/base/randomnumbergenerator.h" - -using namespace XMPP; - -class RandomNumberGeneratorTest : public QObject -{ - Q_OBJECT - - private: - class DummyRandomNumberGenerator : public RandomNumberGenerator - { - public: - DummyRandomNumberGenerator(double value, double maximum) : value_(value), maximum_(maximum) {} - - double generateNumber() const { return value_; } - double getMaximumGeneratedNumber() const { return maximum_; } - - private: - double value_; - double maximum_; - }; - - private slots: - void testGenerateNumberBetween() { - DummyRandomNumberGenerator testling(5,10); - QCOMPARE(75.0, testling.generateNumberBetween(50.0, 100.0)); - } - - void testGenerateNumberBetween_Minimum() { - DummyRandomNumberGenerator testling(0,10); - QCOMPARE(0.0, testling.generateNumberBetween(0.0, 100.0)); - } - - void testGenerateNumberBetween_Maximum() { - DummyRandomNumberGenerator testling(10,10); - QCOMPARE(100.0, testling.generateNumberBetween(0.0, 100.0)); - } -}; - -QTTESTUTIL_REGISTER_TEST(RandomNumberGeneratorTest); -#include "randomnumbergeneratortest.moc" diff --git a/src/xmpp/base/unittest/randrandomnumbergeneratortest.cpp b/src/xmpp/base/unittest/randrandomnumbergeneratortest.cpp deleted file mode 100644 index 9a784683..00000000 --- a/src/xmpp/base/unittest/randrandomnumbergeneratortest.cpp +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright (C) 2008 Remko Troncon - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this library. If not, see . - * - */ - -#include -#include - -#include "qttestutil/qttestutil.h" -#include "xmpp/base/randrandomnumbergenerator.h" - -using namespace XMPP; - -class RandRandomNumberGeneratorTest : public QObject -{ - Q_OBJECT - - private slots: - void testGenerateNumber() { - RandRandomNumberGenerator testling; - - double a = testling.generateNumberBetween(0.0,100.0); - double b = testling.generateNumberBetween(0.0,100.0); - - QVERIFY(a != b); - } - }; - -QTTESTUTIL_REGISTER_TEST(RandRandomNumberGeneratorTest); -#include "randrandomnumbergeneratortest.moc" diff --git a/src/xmpp/base/unittest/unittest.pri b/src/xmpp/base/unittest/unittest.pri deleted file mode 100644 index 0014eb89..00000000 --- a/src/xmpp/base/unittest/unittest.pri +++ /dev/null @@ -1,3 +0,0 @@ -SOURCES += \ - $$PWD/randrandomnumbergeneratortest.cpp \ - $$PWD/randomnumbergeneratortest.cpp diff --git a/src/xmpp/base/unittest/unittest.pro b/src/xmpp/base/unittest/unittest.pro deleted file mode 100644 index 2a0edb78..00000000 --- a/src/xmpp/base/unittest/unittest.pro +++ /dev/null @@ -1,4 +0,0 @@ -include(../../modules.pri) -include($$IRIS_XMPP_QA_UNITTEST_MODULE) -include($$IRIS_XMPP_BASE_MODULE) -include(unittest.pri) diff --git a/src/xmpp/blake2/blake2-impl.h b/src/xmpp/blake2/blake2-impl.h index c1df82e0..29d07fa6 100644 --- a/src/xmpp/blake2/blake2-impl.h +++ b/src/xmpp/blake2/blake2-impl.h @@ -19,142 +19,123 @@ #include #if !defined(__cplusplus) && (!defined(__STDC_VERSION__) || __STDC_VERSION__ < 199901L) - #if defined(_MSC_VER) - #define BLAKE2_INLINE __inline - #elif defined(__GNUC__) - #define BLAKE2_INLINE __inline__ - #else - #define BLAKE2_INLINE - #endif +#if defined(_MSC_VER) +#define BLAKE2_INLINE __inline +#elif defined(__GNUC__) +#define BLAKE2_INLINE __inline__ #else - #define BLAKE2_INLINE inline +#define BLAKE2_INLINE +#endif +#else +#define BLAKE2_INLINE inline #endif -static BLAKE2_INLINE uint32_t load32( const void *src ) +static BLAKE2_INLINE uint32_t load32(const void *src) { #if defined(NATIVE_LITTLE_ENDIAN) - uint32_t w; - memcpy(&w, src, sizeof w); - return w; + uint32_t w; + memcpy(&w, src, sizeof w); + return w; #else - const uint8_t *p = ( const uint8_t * )src; - return (( uint32_t )( p[0] ) << 0) | - (( uint32_t )( p[1] ) << 8) | - (( uint32_t )( p[2] ) << 16) | - (( uint32_t )( p[3] ) << 24) ; + const uint8_t *p = (const uint8_t *)src; + return ((uint32_t)(p[0]) << 0) | ((uint32_t)(p[1]) << 8) | ((uint32_t)(p[2]) << 16) | ((uint32_t)(p[3]) << 24); #endif } -static BLAKE2_INLINE uint64_t load64( const void *src ) +static BLAKE2_INLINE uint64_t load64(const void *src) { #if defined(NATIVE_LITTLE_ENDIAN) - uint64_t w; - memcpy(&w, src, sizeof w); - return w; + uint64_t w; + memcpy(&w, src, sizeof w); + return w; #else - const uint8_t *p = ( const uint8_t * )src; - return (( uint64_t )( p[0] ) << 0) | - (( uint64_t )( p[1] ) << 8) | - (( uint64_t )( p[2] ) << 16) | - (( uint64_t )( p[3] ) << 24) | - (( uint64_t )( p[4] ) << 32) | - (( uint64_t )( p[5] ) << 40) | - (( uint64_t )( p[6] ) << 48) | - (( uint64_t )( p[7] ) << 56) ; + const uint8_t *p = (const uint8_t *)src; + return ((uint64_t)(p[0]) << 0) | ((uint64_t)(p[1]) << 8) | ((uint64_t)(p[2]) << 16) | ((uint64_t)(p[3]) << 24) + | ((uint64_t)(p[4]) << 32) | ((uint64_t)(p[5]) << 40) | ((uint64_t)(p[6]) << 48) | ((uint64_t)(p[7]) << 56); #endif } -static BLAKE2_INLINE uint16_t load16( const void *src ) +static BLAKE2_INLINE uint16_t load16(const void *src) { #if defined(NATIVE_LITTLE_ENDIAN) - uint16_t w; - memcpy(&w, src, sizeof w); - return w; + uint16_t w; + memcpy(&w, src, sizeof w); + return w; #else - const uint8_t *p = ( const uint8_t * )src; - return ( uint16_t )((( uint32_t )( p[0] ) << 0) | - (( uint32_t )( p[1] ) << 8)); + const uint8_t *p = (const uint8_t *)src; + return (uint16_t)(((uint32_t)(p[0]) << 0) | ((uint32_t)(p[1]) << 8)); #endif } -static BLAKE2_INLINE void store16( void *dst, uint16_t w ) +static BLAKE2_INLINE void store16(void *dst, uint16_t w) { #if defined(NATIVE_LITTLE_ENDIAN) - memcpy(dst, &w, sizeof w); + memcpy(dst, &w, sizeof w); #else - uint8_t *p = ( uint8_t * )dst; - *p++ = ( uint8_t )w; w >>= 8; - *p++ = ( uint8_t )w; + uint8_t *p = (uint8_t *)dst; + *p++ = (uint8_t)w; + w >>= 8; + *p++ = (uint8_t)w; #endif } -static BLAKE2_INLINE void store32( void *dst, uint32_t w ) +static BLAKE2_INLINE void store32(void *dst, uint32_t w) { #if defined(NATIVE_LITTLE_ENDIAN) - memcpy(dst, &w, sizeof w); + memcpy(dst, &w, sizeof w); #else - uint8_t *p = ( uint8_t * )dst; - p[0] = (uint8_t)(w >> 0); - p[1] = (uint8_t)(w >> 8); - p[2] = (uint8_t)(w >> 16); - p[3] = (uint8_t)(w >> 24); + uint8_t *p = (uint8_t *)dst; + p[0] = (uint8_t)(w >> 0); + p[1] = (uint8_t)(w >> 8); + p[2] = (uint8_t)(w >> 16); + p[3] = (uint8_t)(w >> 24); #endif } -static BLAKE2_INLINE void store64( void *dst, uint64_t w ) +static BLAKE2_INLINE void store64(void *dst, uint64_t w) { #if defined(NATIVE_LITTLE_ENDIAN) - memcpy(dst, &w, sizeof w); + memcpy(dst, &w, sizeof w); #else - uint8_t *p = ( uint8_t * )dst; - p[0] = (uint8_t)(w >> 0); - p[1] = (uint8_t)(w >> 8); - p[2] = (uint8_t)(w >> 16); - p[3] = (uint8_t)(w >> 24); - p[4] = (uint8_t)(w >> 32); - p[5] = (uint8_t)(w >> 40); - p[6] = (uint8_t)(w >> 48); - p[7] = (uint8_t)(w >> 56); + uint8_t *p = (uint8_t *)dst; + p[0] = (uint8_t)(w >> 0); + p[1] = (uint8_t)(w >> 8); + p[2] = (uint8_t)(w >> 16); + p[3] = (uint8_t)(w >> 24); + p[4] = (uint8_t)(w >> 32); + p[5] = (uint8_t)(w >> 40); + p[6] = (uint8_t)(w >> 48); + p[7] = (uint8_t)(w >> 56); #endif } -static BLAKE2_INLINE uint64_t load48( const void *src ) +static BLAKE2_INLINE uint64_t load48(const void *src) { - const uint8_t *p = ( const uint8_t * )src; - return (( uint64_t )( p[0] ) << 0) | - (( uint64_t )( p[1] ) << 8) | - (( uint64_t )( p[2] ) << 16) | - (( uint64_t )( p[3] ) << 24) | - (( uint64_t )( p[4] ) << 32) | - (( uint64_t )( p[5] ) << 40) ; + const uint8_t *p = (const uint8_t *)src; + return ((uint64_t)(p[0]) << 0) | ((uint64_t)(p[1]) << 8) | ((uint64_t)(p[2]) << 16) | ((uint64_t)(p[3]) << 24) + | ((uint64_t)(p[4]) << 32) | ((uint64_t)(p[5]) << 40); } -static BLAKE2_INLINE void store48( void *dst, uint64_t w ) +static BLAKE2_INLINE void store48(void *dst, uint64_t w) { - uint8_t *p = ( uint8_t * )dst; - p[0] = (uint8_t)(w >> 0); - p[1] = (uint8_t)(w >> 8); - p[2] = (uint8_t)(w >> 16); - p[3] = (uint8_t)(w >> 24); - p[4] = (uint8_t)(w >> 32); - p[5] = (uint8_t)(w >> 40); + uint8_t *p = (uint8_t *)dst; + p[0] = (uint8_t)(w >> 0); + p[1] = (uint8_t)(w >> 8); + p[2] = (uint8_t)(w >> 16); + p[3] = (uint8_t)(w >> 24); + p[4] = (uint8_t)(w >> 32); + p[5] = (uint8_t)(w >> 40); } -static BLAKE2_INLINE uint32_t rotr32( const uint32_t w, const unsigned c ) -{ - return ( w >> c ) | ( w << ( 32 - c ) ); -} +static BLAKE2_INLINE uint32_t rotr32(const uint32_t w, const unsigned c) { return (w >> c) | (w << (32 - c)); } -static BLAKE2_INLINE uint64_t rotr64( const uint64_t w, const unsigned c ) -{ - return ( w >> c ) | ( w << ( 64 - c ) ); -} +static BLAKE2_INLINE uint64_t rotr64(const uint64_t w, const unsigned c) { return (w >> c) | (w << (64 - c)); } /* prevents compiler optimizing out memset() */ static BLAKE2_INLINE void secure_zero_memory(void *v, size_t n) { - static void *(*const volatile memset_v)(void *, int, size_t) = &memset; - memset_v(v, 0, n); + static void *(*const volatile memset_v)(void *, int, size_t) = &memset; + memset_v(v, 0, n); } -#endif +#endif // BLAKE2_IMPL_H diff --git a/src/xmpp/blake2/blake2.h b/src/xmpp/blake2/blake2.h index ad62f260..202bc4b7 100644 --- a/src/xmpp/blake2/blake2.h +++ b/src/xmpp/blake2/blake2.h @@ -28,26 +28,23 @@ extern "C" { #endif - enum blake2s_constant - { - BLAKE2S_BLOCKBYTES = 64, - BLAKE2S_OUTBYTES = 32, - BLAKE2S_KEYBYTES = 32, - BLAKE2S_SALTBYTES = 8, +enum blake2s_constant { + BLAKE2S_BLOCKBYTES = 64, + BLAKE2S_OUTBYTES = 32, + BLAKE2S_KEYBYTES = 32, + BLAKE2S_SALTBYTES = 8, BLAKE2S_PERSONALBYTES = 8 - }; - - enum blake2b_constant - { - BLAKE2B_BLOCKBYTES = 128, - BLAKE2B_OUTBYTES = 64, - BLAKE2B_KEYBYTES = 64, - BLAKE2B_SALTBYTES = 16, +}; + +enum blake2b_constant { + BLAKE2B_BLOCKBYTES = 128, + BLAKE2B_OUTBYTES = 64, + BLAKE2B_KEYBYTES = 64, + BLAKE2B_SALTBYTES = 16, BLAKE2B_PERSONALBYTES = 16 - }; +}; - typedef struct blake2s_state__ - { +typedef struct blake2s_state__ { uint32_t h[8]; uint32_t t[2]; uint32_t f[2]; @@ -55,10 +52,9 @@ extern "C" { size_t buflen; size_t outlen; uint8_t last_node; - } blake2s_state; +} blake2s_state; - typedef struct blake2b_state__ - { +typedef struct blake2b_state__ { uint64_t h[8]; uint64_t t[2]; uint64_t f[2]; @@ -66,130 +62,117 @@ extern "C" { size_t buflen; size_t outlen; uint8_t last_node; - } blake2b_state; +} blake2b_state; - typedef struct blake2sp_state__ - { +typedef struct blake2sp_state__ { blake2s_state S[8][1]; blake2s_state R[1]; uint8_t buf[8 * BLAKE2S_BLOCKBYTES]; size_t buflen; size_t outlen; - } blake2sp_state; +} blake2sp_state; - typedef struct blake2bp_state__ - { +typedef struct blake2bp_state__ { blake2b_state S[4][1]; blake2b_state R[1]; uint8_t buf[4 * BLAKE2B_BLOCKBYTES]; size_t buflen; size_t outlen; - } blake2bp_state; - +} blake2bp_state; - BLAKE2_PACKED(struct blake2s_param__ - { +BLAKE2_PACKED(struct blake2s_param__ { uint8_t digest_length; /* 1 */ uint8_t key_length; /* 2 */ uint8_t fanout; /* 3 */ uint8_t depth; /* 4 */ uint32_t leaf_length; /* 8 */ - uint32_t node_offset; /* 12 */ + uint32_t node_offset; /* 12 */ uint16_t xof_length; /* 14 */ uint8_t node_depth; /* 15 */ uint8_t inner_length; /* 16 */ /* uint8_t reserved[0]; */ - uint8_t salt[BLAKE2S_SALTBYTES]; /* 24 */ - uint8_t personal[BLAKE2S_PERSONALBYTES]; /* 32 */ - }); - - typedef struct blake2s_param__ blake2s_param; - - BLAKE2_PACKED(struct blake2b_param__ - { - uint8_t digest_length; /* 1 */ - uint8_t key_length; /* 2 */ - uint8_t fanout; /* 3 */ - uint8_t depth; /* 4 */ - uint32_t leaf_length; /* 8 */ - uint32_t node_offset; /* 12 */ - uint32_t xof_length; /* 16 */ - uint8_t node_depth; /* 17 */ - uint8_t inner_length; /* 18 */ - uint8_t reserved[14]; /* 32 */ - uint8_t salt[BLAKE2B_SALTBYTES]; /* 48 */ - uint8_t personal[BLAKE2B_PERSONALBYTES]; /* 64 */ - }); - - typedef struct blake2b_param__ blake2b_param; - - typedef struct blake2xs_state__ - { + uint8_t salt[BLAKE2S_SALTBYTES]; /* 24 */ + uint8_t personal[BLAKE2S_PERSONALBYTES]; /* 32 */ +}); + +typedef struct blake2s_param__ blake2s_param; + +BLAKE2_PACKED(struct blake2b_param__ { + uint8_t digest_length; /* 1 */ + uint8_t key_length; /* 2 */ + uint8_t fanout; /* 3 */ + uint8_t depth; /* 4 */ + uint32_t leaf_length; /* 8 */ + uint32_t node_offset; /* 12 */ + uint32_t xof_length; /* 16 */ + uint8_t node_depth; /* 17 */ + uint8_t inner_length; /* 18 */ + uint8_t reserved[14]; /* 32 */ + uint8_t salt[BLAKE2B_SALTBYTES]; /* 48 */ + uint8_t personal[BLAKE2B_PERSONALBYTES]; /* 64 */ +}); + +typedef struct blake2b_param__ blake2b_param; + +typedef struct blake2xs_state__ { blake2s_state S[1]; blake2s_param P[1]; - } blake2xs_state; +} blake2xs_state; - typedef struct blake2xb_state__ - { +typedef struct blake2xb_state__ { blake2b_state S[1]; blake2b_param P[1]; - } blake2xb_state; - - /* Padded structs result in a compile-time error */ - enum { - BLAKE2_DUMMY_1 = 1/(sizeof(blake2s_param) == BLAKE2S_OUTBYTES), - BLAKE2_DUMMY_2 = 1/(sizeof(blake2b_param) == BLAKE2B_OUTBYTES) - }; - - /* Streaming API */ - int blake2s_init( blake2s_state *S, size_t outlen ); - int blake2s_init_key( blake2s_state *S, size_t outlen, const void *key, size_t keylen ); - int blake2s_init_param( blake2s_state *S, const blake2s_param *P ); - int blake2s_update( blake2s_state *S, const void *in, size_t inlen ); - int blake2s_final( blake2s_state *S, void *out, size_t outlen ); - - int blake2b_init( blake2b_state *S, size_t outlen ); - int blake2b_init_key( blake2b_state *S, size_t outlen, const void *key, size_t keylen ); - int blake2b_init_param( blake2b_state *S, const blake2b_param *P ); - int blake2b_update( blake2b_state *S, const void *in, size_t inlen ); - int blake2b_final( blake2b_state *S, void *out, size_t outlen ); - - int blake2sp_init( blake2sp_state *S, size_t outlen ); - int blake2sp_init_key( blake2sp_state *S, size_t outlen, const void *key, size_t keylen ); - int blake2sp_update( blake2sp_state *S, const void *in, size_t inlen ); - int blake2sp_final( blake2sp_state *S, void *out, size_t outlen ); - - int blake2bp_init( blake2bp_state *S, size_t outlen ); - int blake2bp_init_key( blake2bp_state *S, size_t outlen, const void *key, size_t keylen ); - int blake2bp_update( blake2bp_state *S, const void *in, size_t inlen ); - int blake2bp_final( blake2bp_state *S, void *out, size_t outlen ); - - /* Variable output length API */ - int blake2xs_init( blake2xs_state *S, const size_t outlen ); - int blake2xs_init_key( blake2xs_state *S, const size_t outlen, const void *key, size_t keylen ); - int blake2xs_update( blake2xs_state *S, const void *in, size_t inlen ); - int blake2xs_final(blake2xs_state *S, void *out, size_t outlen); - - int blake2xb_init( blake2xb_state *S, const size_t outlen ); - int blake2xb_init_key( blake2xb_state *S, const size_t outlen, const void *key, size_t keylen ); - int blake2xb_update( blake2xb_state *S, const void *in, size_t inlen ); - int blake2xb_final(blake2xb_state *S, void *out, size_t outlen); - - /* Simple API */ - int blake2s( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen ); - int blake2b( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen ); - - int blake2sp( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen ); - int blake2bp( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen ); - - int blake2xs( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen ); - int blake2xb( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen ); - - /* This is simply an alias for blake2b */ - int blake2( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen ); +} blake2xb_state; + +/* Streaming API */ +int blake2s_init(blake2s_state *S, size_t outlen); +int blake2s_init_key(blake2s_state *S, size_t outlen, const void *key, size_t keylen); +int blake2s_init_param(blake2s_state *S, const blake2s_param *P); +int blake2s_update(blake2s_state *S, const void *in, size_t inlen); +int blake2s_final(blake2s_state *S, void *out, size_t outlen); + +int blake2b_init(blake2b_state *S, size_t outlen); +int blake2b_init_key(blake2b_state *S, size_t outlen, const void *key, size_t keylen); +int blake2b_init_param(blake2b_state *S, const blake2b_param *P); +int blake2b_update(blake2b_state *S, const void *in, size_t inlen); +int blake2b_final(blake2b_state *S, void *out, size_t outlen); + +int blake2sp_init(blake2sp_state *S, size_t outlen); +int blake2sp_init_key(blake2sp_state *S, size_t outlen, const void *key, size_t keylen); +int blake2sp_update(blake2sp_state *S, const void *in, size_t inlen); +int blake2sp_final(blake2sp_state *S, void *out, size_t outlen); + +int blake2bp_init(blake2bp_state *S, size_t outlen); +int blake2bp_init_key(blake2bp_state *S, size_t outlen, const void *key, size_t keylen); +int blake2bp_update(blake2bp_state *S, const void *in, size_t inlen); +int blake2bp_final(blake2bp_state *S, void *out, size_t outlen); + +/* Variable output length API */ +int blake2xs_init(blake2xs_state *S, const size_t outlen); +int blake2xs_init_key(blake2xs_state *S, const size_t outlen, const void *key, size_t keylen); +int blake2xs_update(blake2xs_state *S, const void *in, size_t inlen); +int blake2xs_final(blake2xs_state *S, void *out, size_t outlen); + +int blake2xb_init(blake2xb_state *S, const size_t outlen); +int blake2xb_init_key(blake2xb_state *S, const size_t outlen, const void *key, size_t keylen); +int blake2xb_update(blake2xb_state *S, const void *in, size_t inlen); +int blake2xb_final(blake2xb_state *S, void *out, size_t outlen); + +/* Simple API */ +int blake2s(void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen); +int blake2b(void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen); + +int blake2sp(void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen); +int blake2bp(void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen); + +int blake2xs(void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen); +int blake2xb(void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen); + +/* This is simply an alias for blake2b */ +int blake2(void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen); #if defined(__cplusplus) } #endif -#endif +#endif // BLAKE2_H diff --git a/src/xmpp/blake2/blake2.pri b/src/xmpp/blake2/blake2.pri deleted file mode 100644 index 8762944d..00000000 --- a/src/xmpp/blake2/blake2.pri +++ /dev/null @@ -1,11 +0,0 @@ -INCLUDEPATH *= $$PWD/../.. -DEPENDPATH *= $$PWD/../.. - -SOURCES += $$PWD/blake2qt.cpp \ - $$PWD/blake2s-ref.c \ - $$PWD/blake2b-ref.c - -HEADERS += $$PWD/blake2qt.h \ - $$PWD/blake2.h - -OTHER_FILES += $$PWD/README.md diff --git a/src/xmpp/blake2/blake2b-ref.c b/src/xmpp/blake2/blake2b-ref.c index cd38b1ba..a3a512e4 100644 --- a/src/xmpp/blake2/blake2b-ref.c +++ b/src/xmpp/blake2/blake2b-ref.c @@ -13,367 +13,354 @@ https://blake2.net. */ +#include "blake2-impl.h" +#include "blake2.h" + #include -#include #include +#include -#include "blake2.h" -#include "blake2-impl.h" - -static const uint64_t blake2b_IV[8] = -{ - 0x6a09e667f3bcc908ULL, 0xbb67ae8584caa73bULL, - 0x3c6ef372fe94f82bULL, 0xa54ff53a5f1d36f1ULL, - 0x510e527fade682d1ULL, 0x9b05688c2b3e6c1fULL, - 0x1f83d9abfb41bd6bULL, 0x5be0cd19137e2179ULL +static const uint64_t blake2b_IV[8] + = { 0x6a09e667f3bcc908ULL, 0xbb67ae8584caa73bULL, 0x3c6ef372fe94f82bULL, 0xa54ff53a5f1d36f1ULL, + 0x510e527fade682d1ULL, 0x9b05688c2b3e6c1fULL, 0x1f83d9abfb41bd6bULL, 0x5be0cd19137e2179ULL }; + +static const uint8_t blake2b_sigma[12][16] = { + { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 }, { 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3 }, + { 11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4 }, { 7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8 }, + { 9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13 }, { 2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9 }, + { 12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11 }, { 13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10 }, + { 6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5 }, { 10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13, 0 }, + { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 }, { 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3 } }; -static const uint8_t blake2b_sigma[12][16] = -{ - { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 } , - { 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3 } , - { 11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4 } , - { 7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8 } , - { 9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13 } , - { 2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9 } , - { 12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11 } , - { 13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10 } , - { 6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5 } , - { 10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13 , 0 } , - { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 } , - { 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3 } -}; - - -static void blake2b_set_lastnode( blake2b_state *S ) -{ - S->f[1] = (uint64_t)-1; -} +static void blake2b_set_lastnode(blake2b_state *S) { S->f[1] = (uint64_t)-1; } /* Some helper functions, not necessarily useful */ -static int blake2b_is_lastblock( const blake2b_state *S ) -{ - return S->f[0] != 0; -} +static int blake2b_is_lastblock(const blake2b_state *S) { return S->f[0] != 0; } -static void blake2b_set_lastblock( blake2b_state *S ) +static void blake2b_set_lastblock(blake2b_state *S) { - if( S->last_node ) blake2b_set_lastnode( S ); + if (S->last_node) + blake2b_set_lastnode(S); - S->f[0] = (uint64_t)-1; + S->f[0] = (uint64_t)-1; } -static void blake2b_increment_counter( blake2b_state *S, const uint64_t inc ) +static void blake2b_increment_counter(blake2b_state *S, const uint64_t inc) { - S->t[0] += inc; - S->t[1] += ( S->t[0] < inc ); + S->t[0] += inc; + S->t[1] += (S->t[0] < inc); } -static void blake2b_init0( blake2b_state *S ) +static void blake2b_init0(blake2b_state *S) { - size_t i; - memset( S, 0, sizeof( blake2b_state ) ); + size_t i; + memset(S, 0, sizeof(blake2b_state)); - for( i = 0; i < 8; ++i ) S->h[i] = blake2b_IV[i]; + for (i = 0; i < 8; ++i) + S->h[i] = blake2b_IV[i]; } /* init xors IV with input parameter block */ -int blake2b_init_param( blake2b_state *S, const blake2b_param *P ) +int blake2b_init_param(blake2b_state *S, const blake2b_param *P) { - const uint8_t *p = ( const uint8_t * )( P ); - size_t i; + const uint8_t *p = (const uint8_t *)(P); + size_t i; - blake2b_init0( S ); + blake2b_init0(S); - /* IV XOR ParamBlock */ - for( i = 0; i < 8; ++i ) - S->h[i] ^= load64( p + sizeof( S->h[i] ) * i ); + /* IV XOR ParamBlock */ + for (i = 0; i < 8; ++i) + S->h[i] ^= load64(p + sizeof(S->h[i]) * i); - S->outlen = P->digest_length; - return 0; + S->outlen = P->digest_length; + return 0; } - - -int blake2b_init( blake2b_state *S, size_t outlen ) +int blake2b_init(blake2b_state *S, size_t outlen) { - blake2b_param P[1]; - - if ( ( !outlen ) || ( outlen > BLAKE2B_OUTBYTES ) ) return -1; - - P->digest_length = (uint8_t)outlen; - P->key_length = 0; - P->fanout = 1; - P->depth = 1; - store32( &P->leaf_length, 0 ); - store32( &P->node_offset, 0 ); - store32( &P->xof_length, 0 ); - P->node_depth = 0; - P->inner_length = 0; - memset( P->reserved, 0, sizeof( P->reserved ) ); - memset( P->salt, 0, sizeof( P->salt ) ); - memset( P->personal, 0, sizeof( P->personal ) ); - return blake2b_init_param( S, P ); + blake2b_param P[1]; + + if ((!outlen) || (outlen > BLAKE2B_OUTBYTES)) + return -1; + + P->digest_length = (uint8_t)outlen; + P->key_length = 0; + P->fanout = 1; + P->depth = 1; + store32(&P->leaf_length, 0); + store32(&P->node_offset, 0); + store32(&P->xof_length, 0); + P->node_depth = 0; + P->inner_length = 0; + memset(P->reserved, 0, sizeof(P->reserved)); + memset(P->salt, 0, sizeof(P->salt)); + memset(P->personal, 0, sizeof(P->personal)); + return blake2b_init_param(S, P); } - -int blake2b_init_key( blake2b_state *S, size_t outlen, const void *key, size_t keylen ) +int blake2b_init_key(blake2b_state *S, size_t outlen, const void *key, size_t keylen) { - blake2b_param P[1]; - - if ( ( !outlen ) || ( outlen > BLAKE2B_OUTBYTES ) ) return -1; - - if ( !key || !keylen || keylen > BLAKE2B_KEYBYTES ) return -1; - - P->digest_length = (uint8_t)outlen; - P->key_length = (uint8_t)keylen; - P->fanout = 1; - P->depth = 1; - store32( &P->leaf_length, 0 ); - store32( &P->node_offset, 0 ); - store32( &P->xof_length, 0 ); - P->node_depth = 0; - P->inner_length = 0; - memset( P->reserved, 0, sizeof( P->reserved ) ); - memset( P->salt, 0, sizeof( P->salt ) ); - memset( P->personal, 0, sizeof( P->personal ) ); - - if( blake2b_init_param( S, P ) < 0 ) return -1; - - { - uint8_t block[BLAKE2B_BLOCKBYTES]; - memset( block, 0, BLAKE2B_BLOCKBYTES ); - memcpy( block, key, keylen ); - blake2b_update( S, block, BLAKE2B_BLOCKBYTES ); - secure_zero_memory( block, BLAKE2B_BLOCKBYTES ); /* Burn the key from stack */ - } - return 0; + blake2b_param P[1]; + + if ((!outlen) || (outlen > BLAKE2B_OUTBYTES)) + return -1; + + if (!key || !keylen || keylen > BLAKE2B_KEYBYTES) + return -1; + + P->digest_length = (uint8_t)outlen; + P->key_length = (uint8_t)keylen; + P->fanout = 1; + P->depth = 1; + store32(&P->leaf_length, 0); + store32(&P->node_offset, 0); + store32(&P->xof_length, 0); + P->node_depth = 0; + P->inner_length = 0; + memset(P->reserved, 0, sizeof(P->reserved)); + memset(P->salt, 0, sizeof(P->salt)); + memset(P->personal, 0, sizeof(P->personal)); + + if (blake2b_init_param(S, P) < 0) + return -1; + + { + uint8_t block[BLAKE2B_BLOCKBYTES]; + memset(block, 0, BLAKE2B_BLOCKBYTES); + memcpy(block, key, keylen); + blake2b_update(S, block, BLAKE2B_BLOCKBYTES); + secure_zero_memory(block, BLAKE2B_BLOCKBYTES); /* Burn the key from stack */ + } + return 0; } -#define G(r,i,a,b,c,d) \ - do { \ - a = a + b + m[blake2b_sigma[r][2*i+0]]; \ - d = rotr64(d ^ a, 32); \ - c = c + d; \ - b = rotr64(b ^ c, 24); \ - a = a + b + m[blake2b_sigma[r][2*i+1]]; \ - d = rotr64(d ^ a, 16); \ - c = c + d; \ - b = rotr64(b ^ c, 63); \ - } while(0) - -#define ROUND(r) \ - do { \ - G(r,0,v[ 0],v[ 4],v[ 8],v[12]); \ - G(r,1,v[ 1],v[ 5],v[ 9],v[13]); \ - G(r,2,v[ 2],v[ 6],v[10],v[14]); \ - G(r,3,v[ 3],v[ 7],v[11],v[15]); \ - G(r,4,v[ 0],v[ 5],v[10],v[15]); \ - G(r,5,v[ 1],v[ 6],v[11],v[12]); \ - G(r,6,v[ 2],v[ 7],v[ 8],v[13]); \ - G(r,7,v[ 3],v[ 4],v[ 9],v[14]); \ - } while(0) - -static void blake2b_compress( blake2b_state *S, const uint8_t block[BLAKE2B_BLOCKBYTES] ) +#define G(r, i, a, b, c, d) \ + do { \ + a = a + b + m[blake2b_sigma[r][2 * i + 0]]; \ + d = rotr64(d ^ a, 32); \ + c = c + d; \ + b = rotr64(b ^ c, 24); \ + a = a + b + m[blake2b_sigma[r][2 * i + 1]]; \ + d = rotr64(d ^ a, 16); \ + c = c + d; \ + b = rotr64(b ^ c, 63); \ + } while (0) + +#define ROUND(r) \ + do { \ + G(r, 0, v[0], v[4], v[8], v[12]); \ + G(r, 1, v[1], v[5], v[9], v[13]); \ + G(r, 2, v[2], v[6], v[10], v[14]); \ + G(r, 3, v[3], v[7], v[11], v[15]); \ + G(r, 4, v[0], v[5], v[10], v[15]); \ + G(r, 5, v[1], v[6], v[11], v[12]); \ + G(r, 6, v[2], v[7], v[8], v[13]); \ + G(r, 7, v[3], v[4], v[9], v[14]); \ + } while (0) + +static void blake2b_compress(blake2b_state *S, const uint8_t block[BLAKE2B_BLOCKBYTES]) { - uint64_t m[16]; - uint64_t v[16]; - size_t i; - - for( i = 0; i < 16; ++i ) { - m[i] = load64( block + i * sizeof( m[i] ) ); - } - - for( i = 0; i < 8; ++i ) { - v[i] = S->h[i]; - } - - v[ 8] = blake2b_IV[0]; - v[ 9] = blake2b_IV[1]; - v[10] = blake2b_IV[2]; - v[11] = blake2b_IV[3]; - v[12] = blake2b_IV[4] ^ S->t[0]; - v[13] = blake2b_IV[5] ^ S->t[1]; - v[14] = blake2b_IV[6] ^ S->f[0]; - v[15] = blake2b_IV[7] ^ S->f[1]; - - ROUND( 0 ); - ROUND( 1 ); - ROUND( 2 ); - ROUND( 3 ); - ROUND( 4 ); - ROUND( 5 ); - ROUND( 6 ); - ROUND( 7 ); - ROUND( 8 ); - ROUND( 9 ); - ROUND( 10 ); - ROUND( 11 ); - - for( i = 0; i < 8; ++i ) { - S->h[i] = S->h[i] ^ v[i] ^ v[i + 8]; - } + uint64_t m[16]; + uint64_t v[16]; + size_t i; + + for (i = 0; i < 16; ++i) { + m[i] = load64(block + i * sizeof(m[i])); + } + + for (i = 0; i < 8; ++i) { + v[i] = S->h[i]; + } + + v[8] = blake2b_IV[0]; + v[9] = blake2b_IV[1]; + v[10] = blake2b_IV[2]; + v[11] = blake2b_IV[3]; + v[12] = blake2b_IV[4] ^ S->t[0]; + v[13] = blake2b_IV[5] ^ S->t[1]; + v[14] = blake2b_IV[6] ^ S->f[0]; + v[15] = blake2b_IV[7] ^ S->f[1]; + + ROUND(0); + ROUND(1); + ROUND(2); + ROUND(3); + ROUND(4); + ROUND(5); + ROUND(6); + ROUND(7); + ROUND(8); + ROUND(9); + ROUND(10); + ROUND(11); + + for (i = 0; i < 8; ++i) { + S->h[i] = S->h[i] ^ v[i] ^ v[i + 8]; + } } #undef G #undef ROUND -int blake2b_update( blake2b_state *S, const void *pin, size_t inlen ) +int blake2b_update(blake2b_state *S, const void *pin, size_t inlen) { - const unsigned char * in = (const unsigned char *)pin; - if( inlen > 0 ) - { - size_t left = S->buflen; - size_t fill = BLAKE2B_BLOCKBYTES - left; - if( inlen > fill ) - { - S->buflen = 0; - memcpy( S->buf + left, in, fill ); /* Fill buffer */ - blake2b_increment_counter( S, BLAKE2B_BLOCKBYTES ); - blake2b_compress( S, S->buf ); /* Compress */ - in += fill; inlen -= fill; - while(inlen > BLAKE2B_BLOCKBYTES) { - blake2b_increment_counter(S, BLAKE2B_BLOCKBYTES); - blake2b_compress( S, in ); - in += BLAKE2B_BLOCKBYTES; - inlen -= BLAKE2B_BLOCKBYTES; - } + const unsigned char *in = (const unsigned char *)pin; + if (inlen > 0) { + size_t left = S->buflen; + size_t fill = BLAKE2B_BLOCKBYTES - left; + if (inlen > fill) { + S->buflen = 0; + memcpy(S->buf + left, in, fill); /* Fill buffer */ + blake2b_increment_counter(S, BLAKE2B_BLOCKBYTES); + blake2b_compress(S, S->buf); /* Compress */ + in += fill; + inlen -= fill; + while (inlen > BLAKE2B_BLOCKBYTES) { + blake2b_increment_counter(S, BLAKE2B_BLOCKBYTES); + blake2b_compress(S, in); + in += BLAKE2B_BLOCKBYTES; + inlen -= BLAKE2B_BLOCKBYTES; + } + } + memcpy(S->buf + S->buflen, in, inlen); + S->buflen += inlen; } - memcpy( S->buf + S->buflen, in, inlen ); - S->buflen += inlen; - } - return 0; + return 0; } -int blake2b_final( blake2b_state *S, void *out, size_t outlen ) +int blake2b_final(blake2b_state *S, void *out, size_t outlen) { - uint8_t buffer[BLAKE2B_OUTBYTES] = {0}; - size_t i; + uint8_t buffer[BLAKE2B_OUTBYTES] = { 0 }; + size_t i; - if( out == NULL || outlen < S->outlen ) - return -1; + if (out == NULL || outlen < S->outlen) + return -1; - if( blake2b_is_lastblock( S ) ) - return -1; + if (blake2b_is_lastblock(S)) + return -1; - blake2b_increment_counter( S, S->buflen ); - blake2b_set_lastblock( S ); - memset( S->buf + S->buflen, 0, BLAKE2B_BLOCKBYTES - S->buflen ); /* Padding */ - blake2b_compress( S, S->buf ); + blake2b_increment_counter(S, S->buflen); + blake2b_set_lastblock(S); + memset(S->buf + S->buflen, 0, BLAKE2B_BLOCKBYTES - S->buflen); /* Padding */ + blake2b_compress(S, S->buf); - for( i = 0; i < 8; ++i ) /* Output full hash to temp buffer */ - store64( buffer + sizeof( S->h[i] ) * i, S->h[i] ); + for (i = 0; i < 8; ++i) /* Output full hash to temp buffer */ + store64(buffer + sizeof(S->h[i]) * i, S->h[i]); - memcpy( out, buffer, S->outlen ); - secure_zero_memory(buffer, sizeof(buffer)); - return 0; + memcpy(out, buffer, S->outlen); + secure_zero_memory(buffer, sizeof(buffer)); + return 0; } /* inlen, at least, should be uint64_t. Others can be size_t. */ -int blake2b( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen ) +int blake2b(void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen) { - blake2b_state S[1]; + blake2b_state S[1]; - /* Verify parameters */ - if ( NULL == in && inlen > 0 ) return -1; + /* Verify parameters */ + if (NULL == in && inlen > 0) + return -1; - if ( NULL == out ) return -1; + if (NULL == out) + return -1; - if( NULL == key && keylen > 0 ) return -1; + if (NULL == key && keylen > 0) + return -1; - if( !outlen || outlen > BLAKE2B_OUTBYTES ) return -1; + if (!outlen || outlen > BLAKE2B_OUTBYTES) + return -1; - if( keylen > BLAKE2B_KEYBYTES ) return -1; + if (keylen > BLAKE2B_KEYBYTES) + return -1; - if( keylen > 0 ) - { - if( blake2b_init_key( S, outlen, key, keylen ) < 0 ) return -1; - } - else - { - if( blake2b_init( S, outlen ) < 0 ) return -1; - } + if (keylen > 0) { + if (blake2b_init_key(S, outlen, key, keylen) < 0) + return -1; + } else { + if (blake2b_init(S, outlen) < 0) + return -1; + } - blake2b_update( S, ( const uint8_t * )in, inlen ); - blake2b_final( S, out, outlen ); - return 0; + blake2b_update(S, (const uint8_t *)in, inlen); + blake2b_final(S, out, outlen); + return 0; } -int blake2( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen ) { - return blake2b(out, outlen, in, inlen, key, keylen); +int blake2(void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen) +{ + return blake2b(out, outlen, in, inlen, key, keylen); } #if defined(SUPERCOP) -int crypto_hash( unsigned char *out, unsigned char *in, unsigned long long inlen ) +int crypto_hash(unsigned char *out, unsigned char *in, unsigned long long inlen) { - return blake2b( out, BLAKE2B_OUTBYTES, in, inlen, NULL, 0 ); + return blake2b(out, BLAKE2B_OUTBYTES, in, inlen, NULL, 0); } #endif #if defined(BLAKE2B_SELFTEST) -#include #include "blake2-kat.h" -int main( void ) +#include +int main(void) { - uint8_t key[BLAKE2B_KEYBYTES]; - uint8_t buf[BLAKE2_KAT_LENGTH]; - size_t i, step; + uint8_t key[BLAKE2B_KEYBYTES]; + uint8_t buf[BLAKE2_KAT_LENGTH]; + size_t i, step; - for( i = 0; i < BLAKE2B_KEYBYTES; ++i ) - key[i] = ( uint8_t )i; + for (i = 0; i < BLAKE2B_KEYBYTES; ++i) + key[i] = (uint8_t)i; - for( i = 0; i < BLAKE2_KAT_LENGTH; ++i ) - buf[i] = ( uint8_t )i; + for (i = 0; i < BLAKE2_KAT_LENGTH; ++i) + buf[i] = (uint8_t)i; - /* Test simple API */ - for( i = 0; i < BLAKE2_KAT_LENGTH; ++i ) - { - uint8_t hash[BLAKE2B_OUTBYTES]; - blake2b( hash, BLAKE2B_OUTBYTES, buf, i, key, BLAKE2B_KEYBYTES ); + /* Test simple API */ + for (i = 0; i < BLAKE2_KAT_LENGTH; ++i) { + uint8_t hash[BLAKE2B_OUTBYTES]; + blake2b(hash, BLAKE2B_OUTBYTES, buf, i, key, BLAKE2B_KEYBYTES); - if( 0 != memcmp( hash, blake2b_keyed_kat[i], BLAKE2B_OUTBYTES ) ) - { - goto fail; + if (0 != memcmp(hash, blake2b_keyed_kat[i], BLAKE2B_OUTBYTES)) { + goto fail; + } } - } - /* Test streaming API */ - for(step = 1; step < BLAKE2B_BLOCKBYTES; ++step) { - for (i = 0; i < BLAKE2_KAT_LENGTH; ++i) { - uint8_t hash[BLAKE2B_OUTBYTES]; - blake2b_state S; - uint8_t * p = buf; - size_t mlen = i; - int err = 0; - - if( (err = blake2b_init_key(&S, BLAKE2B_OUTBYTES, key, BLAKE2B_KEYBYTES)) < 0 ) { - goto fail; - } - - while (mlen >= step) { - if ( (err = blake2b_update(&S, p, step)) < 0 ) { - goto fail; + /* Test streaming API */ + for (step = 1; step < BLAKE2B_BLOCKBYTES; ++step) { + for (i = 0; i < BLAKE2_KAT_LENGTH; ++i) { + uint8_t hash[BLAKE2B_OUTBYTES]; + blake2b_state S; + uint8_t *p = buf; + size_t mlen = i; + int err = 0; + + if ((err = blake2b_init_key(&S, BLAKE2B_OUTBYTES, key, BLAKE2B_KEYBYTES)) < 0) { + goto fail; + } + + while (mlen >= step) { + if ((err = blake2b_update(&S, p, step)) < 0) { + goto fail; + } + mlen -= step; + p += step; + } + if ((err = blake2b_update(&S, p, mlen)) < 0) { + goto fail; + } + if ((err = blake2b_final(&S, hash, BLAKE2B_OUTBYTES)) < 0) { + goto fail; + } + + if (0 != memcmp(hash, blake2b_keyed_kat[i], BLAKE2B_OUTBYTES)) { + goto fail; + } } - mlen -= step; - p += step; - } - if ( (err = blake2b_update(&S, p, mlen)) < 0) { - goto fail; - } - if ( (err = blake2b_final(&S, hash, BLAKE2B_OUTBYTES)) < 0) { - goto fail; - } - - if (0 != memcmp(hash, blake2b_keyed_kat[i], BLAKE2B_OUTBYTES)) { - goto fail; - } } - } - puts( "ok" ); - return 0; + puts("ok"); + return 0; fail: - puts("error"); - return -1; + puts("error"); + return -1; } #endif diff --git a/src/xmpp/blake2/blake2qt.cpp b/src/xmpp/blake2/blake2qt.cpp index b90c7f6d..4173e3f0 100644 --- a/src/xmpp/blake2/blake2qt.cpp +++ b/src/xmpp/blake2/blake2qt.cpp @@ -1,105 +1,90 @@ #include "blake2qt.h" + #include "blake2.h" #include namespace XMPP { +/* Padded structs result in a compile-time error */ +static_assert(sizeof(blake2s_param) == BLAKE2S_OUTBYTES, "sizeof(blake2s_param) != BLAKE2S_OUTBYTES"); +static_assert(sizeof(blake2b_param) == BLAKE2B_OUTBYTES, "sizeof(blake2b_param) != BLAKE2B_OUTBYTES"); + +class Blake2Hash::Private { +public: + blake2b_state state; +}; -QByteArray computeBlake2Hash(const QByteArray &ba, Blake2DigestSize digestSize) +Blake2Hash::Blake2Hash(DigestSize digestSize) : d(new Private) { - QByteArray ret; - int retCode; - if (digestSize == Blake2Digest256) { - ret.reserve(BLAKE2S_OUTBYTES); - retCode = blake2s(ret.data(), BLAKE2S_OUTBYTES, ba.data(), ba.size(), nullptr, 0); - } else { - ret.reserve(BLAKE2B_OUTBYTES); - retCode = blake2b(ret.data(), BLAKE2B_OUTBYTES, ba.data(), ba.size(), nullptr, 0); - } + size_t digestSizeBytes = digestSize == Digest256 ? 32 : 64; + int retCode = blake2b_init(&d->state, digestSizeBytes); + if (retCode != 0) + d.reset(); +} - if (retCode != 0) { - ret.clear(); - } +Blake2Hash::Blake2Hash(Blake2Hash &&other) : d(other.d.release()) { } - return ret; -} +Blake2Hash::~Blake2Hash() { } -static QByteArray computeBlake2Hash256(QIODevice *dev) +bool Blake2Hash::addData(const QByteArray &data) { - int retCode; - blake2s_state state; + return blake2b_update(&d->state, data.data(), size_t(data.size())) == 0; +} - if (!dev->isOpen()) { +bool Blake2Hash::addData(QIODevice *dev) +{ + bool isOpen = dev->isOpen(); + if (!isOpen) { dev->open(QIODevice::ReadOnly); } if (!dev->isOpen()) { - return QByteArray(); + return false; } - retCode = blake2s_init(&state, BLAKE2S_OUTBYTES); - if (retCode != 0) { - return QByteArray(); - } + bool ret = true; QByteArray buf; - while ((buf = dev->read(64 * 1024 * 1024)).size() > 0) { - retCode = blake2s_update(&state, buf.data(), buf.size()); - if (retCode != 0) { - return QByteArray(); + // reading by 1Mb should work well with disk caches + while ((buf = dev->read(1024 * 1024)).size() > 0) + if (!addData(buf)) { + ret = false; + break; } - } - - QByteArray ret; - ret.reserve(BLAKE2S_OUTBYTES); - retCode = blake2s_final(&state, ret.data(), ret.size()); - if (retCode != 0) { - return QByteArray(); - } + if (!isOpen) + dev->close(); return ret; } -static QByteArray computeBlake2Hash512(QIODevice *dev) +QByteArray Blake2Hash::final() { - int retCode; - blake2b_state state; + QByteArray ret; + ret.resize(int(d->state.outlen)); + if (blake2b_final(&d->state, ret.data(), size_t(ret.size())) == 0) + return ret; + return QByteArray(); +} - if (!dev->isOpen()) { - dev->open(QIODevice::ReadOnly); - } - if (!dev->isOpen()) { - return QByteArray(); - } +QByteArray Blake2Hash::compute(const QByteArray &ba, DigestSize digestSize) +{ + // otherwise try to libb2 or bundled reference implementation depending on which is available - retCode = blake2b_init(&state, BLAKE2B_OUTBYTES); - if (retCode != 0) { - return QByteArray(); - } - QByteArray buf; - while ((buf = dev->read(64 * 1024 * 1024)).size() > 0) { - retCode = blake2b_update(&state, buf.data(), buf.size()); - if (retCode != 0) { - return QByteArray(); - } - } + size_t digestSizeBytes = digestSize == Digest256 ? 32 : 64; + QByteArray ret(int(digestSizeBytes), Qt::Uninitialized); - QByteArray ret; - ret.reserve(BLAKE2B_OUTBYTES); - retCode = blake2b_final(&state, ret.data(), ret.size()); - if (retCode != 0) { - return QByteArray(); + if (blake2b(ret.data(), digestSizeBytes, ba.data(), size_t(ba.size()), nullptr, 0) != 0) { + ret.clear(); } return ret; } -QByteArray computeBlake2Hash(QIODevice *dev, Blake2DigestSize digestSize) +QByteArray Blake2Hash::compute(QIODevice *dev, DigestSize digestSize) { - QByteArray ret; - if (digestSize == Blake2Digest256) { - return computeBlake2Hash256(dev); - } else { - return computeBlake2Hash512(dev); - } + Blake2Hash hash(digestSize); + if (!(hash.isValid() && hash.addData(dev))) + return QByteArray(); + + return hash.final(); } -} //namespace XMPP +} // namespace XMPP diff --git a/src/xmpp/blake2/blake2qt.h b/src/xmpp/blake2/blake2qt.h index 8240afbd..e8965560 100644 --- a/src/xmpp/blake2/blake2qt.h +++ b/src/xmpp/blake2/blake2qt.h @@ -3,17 +3,33 @@ #include +#include + class QIODevice; namespace XMPP { -enum Blake2DigestSize { - Blake2Digest256, - Blake2Digest512 +class Blake2Hash { +public: + enum DigestSize { Digest256, Digest512 }; + + Blake2Hash(DigestSize digestSize); + Blake2Hash(Blake2Hash &&other); + ~Blake2Hash(); + + bool addData(const QByteArray &data); + bool addData(QIODevice *dev); + QByteArray final(); + bool isValid() const { return d != nullptr; } + + static QByteArray compute(const QByteArray &ba, DigestSize digestSize); + static QByteArray compute(QIODevice *dev, DigestSize digestSize); + +private: + class Private; + std::unique_ptr d; }; -QByteArray computeBlake2Hash(const QByteArray &ba, Blake2DigestSize digestSize); -QByteArray computeBlake2Hash(QIODevice *dev, Blake2DigestSize digestSize); +} // namespace XMPP -} -#endif +#endif // BLAKE2QT_H diff --git a/src/xmpp/blake2/blake2s-ref.c b/src/xmpp/blake2/blake2s-ref.c index c8b035f6..33f3d875 100644 --- a/src/xmpp/blake2/blake2s-ref.c +++ b/src/xmpp/blake2/blake2s-ref.c @@ -13,355 +13,346 @@ https://blake2.net. */ +#include "blake2-impl.h" +#include "blake2.h" + #include -#include #include +#include -#include "blake2.h" -#include "blake2-impl.h" +static const uint32_t blake2s_IV[8] = { 0x6A09E667UL, 0xBB67AE85UL, 0x3C6EF372UL, 0xA54FF53AUL, + 0x510E527FUL, 0x9B05688CUL, 0x1F83D9ABUL, 0x5BE0CD19UL }; -static const uint32_t blake2s_IV[8] = -{ - 0x6A09E667UL, 0xBB67AE85UL, 0x3C6EF372UL, 0xA54FF53AUL, - 0x510E527FUL, 0x9B05688CUL, 0x1F83D9ABUL, 0x5BE0CD19UL +static const uint8_t blake2s_sigma[10][16] = { + { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 }, { 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3 }, + { 11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4 }, { 7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8 }, + { 9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13 }, { 2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9 }, + { 12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11 }, { 13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10 }, + { 6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5 }, { 10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13, 0 }, }; -static const uint8_t blake2s_sigma[10][16] = -{ - { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 } , - { 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3 } , - { 11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4 } , - { 7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8 } , - { 9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13 } , - { 2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9 } , - { 12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11 } , - { 13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10 } , - { 6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5 } , - { 10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13 , 0 } , -}; - -static void blake2s_set_lastnode( blake2s_state *S ) -{ - S->f[1] = (uint32_t)-1; -} +static void blake2s_set_lastnode(blake2s_state *S) { S->f[1] = (uint32_t)-1; } /* Some helper functions, not necessarily useful */ -static int blake2s_is_lastblock( const blake2s_state *S ) -{ - return S->f[0] != 0; -} +static int blake2s_is_lastblock(const blake2s_state *S) { return S->f[0] != 0; } -static void blake2s_set_lastblock( blake2s_state *S ) +static void blake2s_set_lastblock(blake2s_state *S) { - if( S->last_node ) blake2s_set_lastnode( S ); + if (S->last_node) + blake2s_set_lastnode(S); - S->f[0] = (uint32_t)-1; + S->f[0] = (uint32_t)-1; } -static void blake2s_increment_counter( blake2s_state *S, const uint32_t inc ) +static void blake2s_increment_counter(blake2s_state *S, const uint32_t inc) { - S->t[0] += inc; - S->t[1] += ( S->t[0] < inc ); + S->t[0] += inc; + S->t[1] += (S->t[0] < inc); } -static void blake2s_init0( blake2s_state *S ) +static void blake2s_init0(blake2s_state *S) { - size_t i; - memset( S, 0, sizeof( blake2s_state ) ); + size_t i; + memset(S, 0, sizeof(blake2s_state)); - for( i = 0; i < 8; ++i ) S->h[i] = blake2s_IV[i]; + for (i = 0; i < 8; ++i) + S->h[i] = blake2s_IV[i]; } /* init2 xors IV with input parameter block */ -int blake2s_init_param( blake2s_state *S, const blake2s_param *P ) +int blake2s_init_param(blake2s_state *S, const blake2s_param *P) { - const unsigned char *p = ( const unsigned char * )( P ); - size_t i; + const unsigned char *p = (const unsigned char *)(P); + size_t i; - blake2s_init0( S ); + blake2s_init0(S); - /* IV XOR ParamBlock */ - for( i = 0; i < 8; ++i ) - S->h[i] ^= load32( &p[i * 4] ); + /* IV XOR ParamBlock */ + for (i = 0; i < 8; ++i) + S->h[i] ^= load32(&p[i * 4]); - S->outlen = P->digest_length; - return 0; + S->outlen = P->digest_length; + return 0; } - /* Sequential blake2s initialization */ -int blake2s_init( blake2s_state *S, size_t outlen ) +int blake2s_init(blake2s_state *S, size_t outlen) { - blake2s_param P[1]; - - /* Move interval verification here? */ - if ( ( !outlen ) || ( outlen > BLAKE2S_OUTBYTES ) ) return -1; - - P->digest_length = (uint8_t)outlen; - P->key_length = 0; - P->fanout = 1; - P->depth = 1; - store32( &P->leaf_length, 0 ); - store32( &P->node_offset, 0 ); - store16( &P->xof_length, 0 ); - P->node_depth = 0; - P->inner_length = 0; - /* memset(P->reserved, 0, sizeof(P->reserved) ); */ - memset( P->salt, 0, sizeof( P->salt ) ); - memset( P->personal, 0, sizeof( P->personal ) ); - return blake2s_init_param( S, P ); + blake2s_param P[1]; + + /* Move interval verification here? */ + if ((!outlen) || (outlen > BLAKE2S_OUTBYTES)) + return -1; + + P->digest_length = (uint8_t)outlen; + P->key_length = 0; + P->fanout = 1; + P->depth = 1; + store32(&P->leaf_length, 0); + store32(&P->node_offset, 0); + store16(&P->xof_length, 0); + P->node_depth = 0; + P->inner_length = 0; + /* memset(P->reserved, 0, sizeof(P->reserved) ); */ + memset(P->salt, 0, sizeof(P->salt)); + memset(P->personal, 0, sizeof(P->personal)); + return blake2s_init_param(S, P); } -int blake2s_init_key( blake2s_state *S, size_t outlen, const void *key, size_t keylen ) +int blake2s_init_key(blake2s_state *S, size_t outlen, const void *key, size_t keylen) { - blake2s_param P[1]; - - if ( ( !outlen ) || ( outlen > BLAKE2S_OUTBYTES ) ) return -1; - - if ( !key || !keylen || keylen > BLAKE2S_KEYBYTES ) return -1; - - P->digest_length = (uint8_t)outlen; - P->key_length = (uint8_t)keylen; - P->fanout = 1; - P->depth = 1; - store32( &P->leaf_length, 0 ); - store32( &P->node_offset, 0 ); - store16( &P->xof_length, 0 ); - P->node_depth = 0; - P->inner_length = 0; - /* memset(P->reserved, 0, sizeof(P->reserved) ); */ - memset( P->salt, 0, sizeof( P->salt ) ); - memset( P->personal, 0, sizeof( P->personal ) ); - - if( blake2s_init_param( S, P ) < 0 ) return -1; - - { - uint8_t block[BLAKE2S_BLOCKBYTES]; - memset( block, 0, BLAKE2S_BLOCKBYTES ); - memcpy( block, key, keylen ); - blake2s_update( S, block, BLAKE2S_BLOCKBYTES ); - secure_zero_memory( block, BLAKE2S_BLOCKBYTES ); /* Burn the key from stack */ - } - return 0; + blake2s_param P[1]; + + if ((!outlen) || (outlen > BLAKE2S_OUTBYTES)) + return -1; + + if (!key || !keylen || keylen > BLAKE2S_KEYBYTES) + return -1; + + P->digest_length = (uint8_t)outlen; + P->key_length = (uint8_t)keylen; + P->fanout = 1; + P->depth = 1; + store32(&P->leaf_length, 0); + store32(&P->node_offset, 0); + store16(&P->xof_length, 0); + P->node_depth = 0; + P->inner_length = 0; + /* memset(P->reserved, 0, sizeof(P->reserved) ); */ + memset(P->salt, 0, sizeof(P->salt)); + memset(P->personal, 0, sizeof(P->personal)); + + if (blake2s_init_param(S, P) < 0) + return -1; + + { + uint8_t block[BLAKE2S_BLOCKBYTES]; + memset(block, 0, BLAKE2S_BLOCKBYTES); + memcpy(block, key, keylen); + blake2s_update(S, block, BLAKE2S_BLOCKBYTES); + secure_zero_memory(block, BLAKE2S_BLOCKBYTES); /* Burn the key from stack */ + } + return 0; } -#define G(r,i,a,b,c,d) \ - do { \ - a = a + b + m[blake2s_sigma[r][2*i+0]]; \ - d = rotr32(d ^ a, 16); \ - c = c + d; \ - b = rotr32(b ^ c, 12); \ - a = a + b + m[blake2s_sigma[r][2*i+1]]; \ - d = rotr32(d ^ a, 8); \ - c = c + d; \ - b = rotr32(b ^ c, 7); \ - } while(0) - -#define ROUND(r) \ - do { \ - G(r,0,v[ 0],v[ 4],v[ 8],v[12]); \ - G(r,1,v[ 1],v[ 5],v[ 9],v[13]); \ - G(r,2,v[ 2],v[ 6],v[10],v[14]); \ - G(r,3,v[ 3],v[ 7],v[11],v[15]); \ - G(r,4,v[ 0],v[ 5],v[10],v[15]); \ - G(r,5,v[ 1],v[ 6],v[11],v[12]); \ - G(r,6,v[ 2],v[ 7],v[ 8],v[13]); \ - G(r,7,v[ 3],v[ 4],v[ 9],v[14]); \ - } while(0) - -static void blake2s_compress( blake2s_state *S, const uint8_t in[BLAKE2S_BLOCKBYTES] ) +#define G(r, i, a, b, c, d) \ + do { \ + a = a + b + m[blake2s_sigma[r][2 * i + 0]]; \ + d = rotr32(d ^ a, 16); \ + c = c + d; \ + b = rotr32(b ^ c, 12); \ + a = a + b + m[blake2s_sigma[r][2 * i + 1]]; \ + d = rotr32(d ^ a, 8); \ + c = c + d; \ + b = rotr32(b ^ c, 7); \ + } while (0) + +#define ROUND(r) \ + do { \ + G(r, 0, v[0], v[4], v[8], v[12]); \ + G(r, 1, v[1], v[5], v[9], v[13]); \ + G(r, 2, v[2], v[6], v[10], v[14]); \ + G(r, 3, v[3], v[7], v[11], v[15]); \ + G(r, 4, v[0], v[5], v[10], v[15]); \ + G(r, 5, v[1], v[6], v[11], v[12]); \ + G(r, 6, v[2], v[7], v[8], v[13]); \ + G(r, 7, v[3], v[4], v[9], v[14]); \ + } while (0) + +static void blake2s_compress(blake2s_state *S, const uint8_t in[BLAKE2S_BLOCKBYTES]) { - uint32_t m[16]; - uint32_t v[16]; - size_t i; - - for( i = 0; i < 16; ++i ) { - m[i] = load32( in + i * sizeof( m[i] ) ); - } - - for( i = 0; i < 8; ++i ) { - v[i] = S->h[i]; - } - - v[ 8] = blake2s_IV[0]; - v[ 9] = blake2s_IV[1]; - v[10] = blake2s_IV[2]; - v[11] = blake2s_IV[3]; - v[12] = S->t[0] ^ blake2s_IV[4]; - v[13] = S->t[1] ^ blake2s_IV[5]; - v[14] = S->f[0] ^ blake2s_IV[6]; - v[15] = S->f[1] ^ blake2s_IV[7]; - - ROUND( 0 ); - ROUND( 1 ); - ROUND( 2 ); - ROUND( 3 ); - ROUND( 4 ); - ROUND( 5 ); - ROUND( 6 ); - ROUND( 7 ); - ROUND( 8 ); - ROUND( 9 ); - - for( i = 0; i < 8; ++i ) { - S->h[i] = S->h[i] ^ v[i] ^ v[i + 8]; - } + uint32_t m[16]; + uint32_t v[16]; + size_t i; + + for (i = 0; i < 16; ++i) { + m[i] = load32(in + i * sizeof(m[i])); + } + + for (i = 0; i < 8; ++i) { + v[i] = S->h[i]; + } + + v[8] = blake2s_IV[0]; + v[9] = blake2s_IV[1]; + v[10] = blake2s_IV[2]; + v[11] = blake2s_IV[3]; + v[12] = S->t[0] ^ blake2s_IV[4]; + v[13] = S->t[1] ^ blake2s_IV[5]; + v[14] = S->f[0] ^ blake2s_IV[6]; + v[15] = S->f[1] ^ blake2s_IV[7]; + + ROUND(0); + ROUND(1); + ROUND(2); + ROUND(3); + ROUND(4); + ROUND(5); + ROUND(6); + ROUND(7); + ROUND(8); + ROUND(9); + + for (i = 0; i < 8; ++i) { + S->h[i] = S->h[i] ^ v[i] ^ v[i + 8]; + } } #undef G #undef ROUND -int blake2s_update( blake2s_state *S, const void *pin, size_t inlen ) +int blake2s_update(blake2s_state *S, const void *pin, size_t inlen) { - const unsigned char * in = (const unsigned char *)pin; - if( inlen > 0 ) - { - size_t left = S->buflen; - size_t fill = BLAKE2S_BLOCKBYTES - left; - if( inlen > fill ) - { - S->buflen = 0; - memcpy( S->buf + left, in, fill ); /* Fill buffer */ - blake2s_increment_counter( S, BLAKE2S_BLOCKBYTES ); - blake2s_compress( S, S->buf ); /* Compress */ - in += fill; inlen -= fill; - while(inlen > BLAKE2S_BLOCKBYTES) { - blake2s_increment_counter(S, BLAKE2S_BLOCKBYTES); - blake2s_compress( S, in ); - in += BLAKE2S_BLOCKBYTES; - inlen -= BLAKE2S_BLOCKBYTES; - } + const unsigned char *in = (const unsigned char *)pin; + if (inlen > 0) { + size_t left = S->buflen; + size_t fill = BLAKE2S_BLOCKBYTES - left; + if (inlen > fill) { + S->buflen = 0; + memcpy(S->buf + left, in, fill); /* Fill buffer */ + blake2s_increment_counter(S, BLAKE2S_BLOCKBYTES); + blake2s_compress(S, S->buf); /* Compress */ + in += fill; + inlen -= fill; + while (inlen > BLAKE2S_BLOCKBYTES) { + blake2s_increment_counter(S, BLAKE2S_BLOCKBYTES); + blake2s_compress(S, in); + in += BLAKE2S_BLOCKBYTES; + inlen -= BLAKE2S_BLOCKBYTES; + } + } + memcpy(S->buf + S->buflen, in, inlen); + S->buflen += inlen; } - memcpy( S->buf + S->buflen, in, inlen ); - S->buflen += inlen; - } - return 0; + return 0; } -int blake2s_final( blake2s_state *S, void *out, size_t outlen ) +int blake2s_final(blake2s_state *S, void *out, size_t outlen) { - uint8_t buffer[BLAKE2S_OUTBYTES] = {0}; - size_t i; + uint8_t buffer[BLAKE2S_OUTBYTES] = { 0 }; + size_t i; - if( out == NULL || outlen < S->outlen ) - return -1; + if (out == NULL || outlen < S->outlen) + return -1; - if( blake2s_is_lastblock( S ) ) - return -1; + if (blake2s_is_lastblock(S)) + return -1; - blake2s_increment_counter( S, ( uint32_t )S->buflen ); - blake2s_set_lastblock( S ); - memset( S->buf + S->buflen, 0, BLAKE2S_BLOCKBYTES - S->buflen ); /* Padding */ - blake2s_compress( S, S->buf ); + blake2s_increment_counter(S, (uint32_t)S->buflen); + blake2s_set_lastblock(S); + memset(S->buf + S->buflen, 0, BLAKE2S_BLOCKBYTES - S->buflen); /* Padding */ + blake2s_compress(S, S->buf); - for( i = 0; i < 8; ++i ) /* Output full hash to temp buffer */ - store32( buffer + sizeof( S->h[i] ) * i, S->h[i] ); + for (i = 0; i < 8; ++i) /* Output full hash to temp buffer */ + store32(buffer + sizeof(S->h[i]) * i, S->h[i]); - memcpy( out, buffer, outlen ); - secure_zero_memory(buffer, sizeof(buffer)); - return 0; + memcpy(out, buffer, outlen); + secure_zero_memory(buffer, sizeof(buffer)); + return 0; } -int blake2s( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen ) +int blake2s(void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen) { - blake2s_state S[1]; + blake2s_state S[1]; - /* Verify parameters */ - if ( NULL == in && inlen > 0 ) return -1; + /* Verify parameters */ + if (NULL == in && inlen > 0) + return -1; - if ( NULL == out ) return -1; + if (NULL == out) + return -1; - if ( NULL == key && keylen > 0) return -1; + if (NULL == key && keylen > 0) + return -1; - if( !outlen || outlen > BLAKE2S_OUTBYTES ) return -1; + if (!outlen || outlen > BLAKE2S_OUTBYTES) + return -1; - if( keylen > BLAKE2S_KEYBYTES ) return -1; + if (keylen > BLAKE2S_KEYBYTES) + return -1; - if( keylen > 0 ) - { - if( blake2s_init_key( S, outlen, key, keylen ) < 0 ) return -1; - } - else - { - if( blake2s_init( S, outlen ) < 0 ) return -1; - } + if (keylen > 0) { + if (blake2s_init_key(S, outlen, key, keylen) < 0) + return -1; + } else { + if (blake2s_init(S, outlen) < 0) + return -1; + } - blake2s_update( S, ( const uint8_t * )in, inlen ); - blake2s_final( S, out, outlen ); - return 0; + blake2s_update(S, (const uint8_t *)in, inlen); + blake2s_final(S, out, outlen); + return 0; } #if defined(SUPERCOP) -int crypto_hash( unsigned char *out, unsigned char *in, unsigned long long inlen ) +int crypto_hash(unsigned char *out, unsigned char *in, unsigned long long inlen) { - return blake2s( out, BLAKE2S_OUTBYTES, in, inlen, NULL, 0 ); + return blake2s(out, BLAKE2S_OUTBYTES, in, inlen, NULL, 0); } #endif #if defined(BLAKE2S_SELFTEST) -#include #include "blake2-kat.h" -int main( void ) +#include +int main(void) { - uint8_t key[BLAKE2S_KEYBYTES]; - uint8_t buf[BLAKE2_KAT_LENGTH]; - size_t i, step; + uint8_t key[BLAKE2S_KEYBYTES]; + uint8_t buf[BLAKE2_KAT_LENGTH]; + size_t i, step; - for( i = 0; i < BLAKE2S_KEYBYTES; ++i ) - key[i] = ( uint8_t )i; + for (i = 0; i < BLAKE2S_KEYBYTES; ++i) + key[i] = (uint8_t)i; - for( i = 0; i < BLAKE2_KAT_LENGTH; ++i ) - buf[i] = ( uint8_t )i; + for (i = 0; i < BLAKE2_KAT_LENGTH; ++i) + buf[i] = (uint8_t)i; - /* Test simple API */ - for( i = 0; i < BLAKE2_KAT_LENGTH; ++i ) - { - uint8_t hash[BLAKE2S_OUTBYTES]; - blake2s( hash, BLAKE2S_OUTBYTES, buf, i, key, BLAKE2S_KEYBYTES ); + /* Test simple API */ + for (i = 0; i < BLAKE2_KAT_LENGTH; ++i) { + uint8_t hash[BLAKE2S_OUTBYTES]; + blake2s(hash, BLAKE2S_OUTBYTES, buf, i, key, BLAKE2S_KEYBYTES); - if( 0 != memcmp( hash, blake2s_keyed_kat[i], BLAKE2S_OUTBYTES ) ) - { - goto fail; + if (0 != memcmp(hash, blake2s_keyed_kat[i], BLAKE2S_OUTBYTES)) { + goto fail; + } } - } - /* Test streaming API */ - for(step = 1; step < BLAKE2S_BLOCKBYTES; ++step) { - for (i = 0; i < BLAKE2_KAT_LENGTH; ++i) { - uint8_t hash[BLAKE2S_OUTBYTES]; - blake2s_state S; - uint8_t * p = buf; - size_t mlen = i; - int err = 0; - - if( (err = blake2s_init_key(&S, BLAKE2S_OUTBYTES, key, BLAKE2S_KEYBYTES)) < 0 ) { - goto fail; - } - - while (mlen >= step) { - if ( (err = blake2s_update(&S, p, step)) < 0 ) { - goto fail; + /* Test streaming API */ + for (step = 1; step < BLAKE2S_BLOCKBYTES; ++step) { + for (i = 0; i < BLAKE2_KAT_LENGTH; ++i) { + uint8_t hash[BLAKE2S_OUTBYTES]; + blake2s_state S; + uint8_t *p = buf; + size_t mlen = i; + int err = 0; + + if ((err = blake2s_init_key(&S, BLAKE2S_OUTBYTES, key, BLAKE2S_KEYBYTES)) < 0) { + goto fail; + } + + while (mlen >= step) { + if ((err = blake2s_update(&S, p, step)) < 0) { + goto fail; + } + mlen -= step; + p += step; + } + if ((err = blake2s_update(&S, p, mlen)) < 0) { + goto fail; + } + if ((err = blake2s_final(&S, hash, BLAKE2S_OUTBYTES)) < 0) { + goto fail; + } + + if (0 != memcmp(hash, blake2s_keyed_kat[i], BLAKE2S_OUTBYTES)) { + goto fail; + } } - mlen -= step; - p += step; - } - if ( (err = blake2s_update(&S, p, mlen)) < 0) { - goto fail; - } - if ( (err = blake2s_final(&S, hash, BLAKE2S_OUTBYTES)) < 0) { - goto fail; - } - - if (0 != memcmp(hash, blake2s_keyed_kat[i], BLAKE2S_OUTBYTES)) { - goto fail; - } } - } - puts( "ok" ); - return 0; + puts("ok"); + return 0; fail: - puts("error"); - return -1; + puts("error"); + return -1; } #endif diff --git a/src/xmpp/common.pri b/src/xmpp/common.pri deleted file mode 100644 index 6cd8bb4f..00000000 --- a/src/xmpp/common.pri +++ /dev/null @@ -1,3 +0,0 @@ -OBJECTS_DIR = .obj -MOC_DIR = .moc -UI_DIR = .ui diff --git a/src/xmpp/jid/jid.cpp b/src/xmpp/jid/jid.cpp index 93c08e16..5295e58b 100644 --- a/src/xmpp/jid/jid.cpp +++ b/src/xmpp/jid/jid.cpp @@ -19,182 +19,156 @@ #include "xmpp/jid/jid.h" -#include -#include - #ifndef NO_IRISNET -#include "irisnetglobal_p.h" +#include "irisnet/corelib/irisnetglobal_p.h" #endif -using namespace XMPP; +#include "qstringprep.h" +#include +using namespace XMPP; //---------------------------------------------------------------------------- // StringPrepCache //---------------------------------------------------------------------------- - QScopedPointer StringPrepCache::_instance; +std::unique_ptr StringPrepCache::_instance; - bool StringPrepCache::nameprep(const QString &in, int maxbytes, QString& out) - { - if (in.trimmed().isEmpty()) { - out = QString(); - return false; // empty names or just spaces are disallowed (rfc5892+rfc6122) - } - - StringPrepCache *that = instance(); +bool StringPrepCache::nameprep(const QString &in, int maxbytes, QString &out) +{ + if (in.trimmed().isEmpty()) { + out = QString(); + return false; // empty names or just spaces are disallowed (rfc5892+rfc6122) + } - auto it = that->nameprep_table.constFind(in);\ - if (it != that->nameprep_table.constEnd()) { - if (it.value().isNull()) { - return false; - } - out = it.value(); - return true; - } + StringPrepCache *that = instance(); - QByteArray cs = in.toUtf8(); - cs.resize(maxbytes); - if(stringprep(cs.data(), maxbytes, (Stringprep_profile_flags)0, stringprep_nameprep) != 0) - { - that->nameprep_table.insert(in, QString::null); + auto it = that->nameprep_table.constFind(in); + if (it != that->nameprep_table.constEnd()) { + if (it.value().isNull()) { return false; } - - QString norm = QString::fromUtf8(cs); - that->nameprep_table.insert(in, norm); - out = norm; + out = it.value(); return true; } - bool StringPrepCache::nodeprep(const QString &in, int maxbytes, QString& out) - { - if(in.isEmpty()) { - out = QString(); - return true; - } + out = in; + if (stringprep(out, (Stringprep_profile_flags)0, stringprep_nameprep) != 0 || out.size() > maxbytes) { + that->nameprep_table.insert(in, QString()); + return false; + } - StringPrepCache *that = instance(); + that->nameprep_table.insert(in, out); + return true; +} - auto it = that->nodeprep_table.constFind(in);\ - if (it != that->nodeprep_table.constEnd()) { - if (it.value().isNull()) { - return false; - } - out = it.value(); - return true; - } +bool StringPrepCache::nodeprep(const QString &in, int maxbytes, QString &out) +{ + if (in.isEmpty()) { + out = QString(); + return true; + } + + StringPrepCache *that = instance(); - QByteArray cs = in.toUtf8(); - cs.resize(maxbytes); - if(stringprep(cs.data(), maxbytes, (Stringprep_profile_flags)0, stringprep_xmpp_nodeprep) != 0) { - that->nodeprep_table.insert(in, QString::null); + auto it = that->nodeprep_table.constFind(in); + if (it != that->nodeprep_table.constEnd()) { + if (it.value().isNull()) { return false; } - - QString norm = QString::fromUtf8(cs); - that->nodeprep_table.insert(in, norm); - out = norm; + out = it.value(); return true; } - bool StringPrepCache::resourceprep(const QString &in, int maxbytes, QString& out) - { - if(in.isEmpty()) { - out = QString(); - return true; - } + out = in; + if (stringprep(out, (Stringprep_profile_flags)0, stringprep_xmpp_nodeprep) != 0 || out.size() > maxbytes) { + that->nodeprep_table.insert(in, QString()); + return false; + } - StringPrepCache *that = instance(); + that->nodeprep_table.insert(in, out); + return true; +} - auto it = that->resourceprep_table.constFind(in);\ - if (it != that->resourceprep_table.constEnd()) { - if (it.value().isNull()) { - return false; - } - out = it.value(); - return true; - } +bool StringPrepCache::resourceprep(const QString &in, int maxbytes, QString &out) +{ + if (in.isEmpty()) { + out = QString(); + return true; + } + + StringPrepCache *that = instance(); - QByteArray cs = in.toUtf8(); - cs.resize(maxbytes); - if(stringprep(cs.data(), maxbytes, (Stringprep_profile_flags)0, stringprep_xmpp_resourceprep) != 0) { - that->resourceprep_table.insert(in, QString::null); + auto it = that->resourceprep_table.constFind(in); + if (it != that->resourceprep_table.constEnd()) { + if (it.value().isNull()) { return false; } - - QString norm = QString::fromUtf8(cs); - that->resourceprep_table.insert(in, norm); - out = norm; + out = it.value(); return true; } - bool StringPrepCache::saslprep(const QString &in, int maxbytes, QString& out) - { - if(in.isEmpty()) { - out = QString(); - return true; - } + out = in; + if (stringprep(out, (Stringprep_profile_flags)0, stringprep_xmpp_resourceprep) != 0 || out.size() > maxbytes) { + that->resourceprep_table.insert(in, QString()); + return false; + } + + that->resourceprep_table.insert(in, out); + return true; +} - StringPrepCache *that = instance(); +bool StringPrepCache::saslprep(const QString &in, int maxbytes, QString &out) +{ + if (in.isEmpty()) { + out = QString(); + return true; + } - auto it = that->saslprep_table.constFind(in);\ - if (it != that->saslprep_table.constEnd()) { - if (it.value().isNull()) { - return false; - } - out = it.value(); - return true; - } + StringPrepCache *that = instance(); - QByteArray cs = in.toUtf8(); - cs.resize(maxbytes); - if(stringprep(cs.data(), maxbytes, (Stringprep_profile_flags)0, stringprep_saslprep) != 0) { - that->saslprep_table.insert(in, QString::null); + auto it = that->saslprep_table.constFind(in); + if (it != that->saslprep_table.constEnd()) { + if (it.value().isNull()) { return false; } - - QString norm = QString::fromUtf8(cs); - that->saslprep_table.insert(in, norm); - out = norm; + out = it.value(); return true; } - void StringPrepCache::cleanup() - { - _instance.reset(0); + out = in; + if (stringprep(out, (Stringprep_profile_flags)0, stringprep_saslprep) != 0 || out.size() > maxbytes) { + that->saslprep_table.insert(in, QString()); + return false; } - StringPrepCache *StringPrepCache::instance() - { - if(!_instance) - { - _instance.reset(new StringPrepCache); + that->saslprep_table.insert(in, out); + return true; +} + +void StringPrepCache::cleanup() { _instance.reset(nullptr); } + +StringPrepCache *StringPrepCache::instance() +{ + if (!_instance) { + _instance.reset(new StringPrepCache); #ifndef NO_IRISNET - irisNetAddPostRoutine(cleanup); // REVIEW probably not necessary since heap will be deallocated with destructors + irisNetAddPostRoutine(cleanup); // REVIEW probably not necessary since heap will be deallocated with destructors #endif - } - return _instance.data(); } + return _instance.get(); +} - StringPrepCache::StringPrepCache() - { - } +StringPrepCache::StringPrepCache() { } //---------------------------------------------------------------------------- // Jid //---------------------------------------------------------------------------- // -static inline bool validDomain(const QString &s, QString& norm) -{ - return StringPrepCache::nameprep(s, 1024, norm); -} +static inline bool validDomain(const QString &s, QString &norm) { return StringPrepCache::nameprep(s, 1024, norm); } -static inline bool validNode(const QString &s, QString& norm) -{ - return StringPrepCache::nodeprep(s, 1024, norm); -} +static inline bool validNode(const QString &s, QString &norm) { return StringPrepCache::nodeprep(s, 1024, norm); } -static inline bool validResource(const QString &s, QString& norm) +static inline bool validResource(const QString &s, QString &norm) { return StringPrepCache::resourceprep(s, 1024, norm); } @@ -202,35 +176,24 @@ static inline bool validResource(const QString &s, QString& norm) Jid::Jid() { valid = false; - null = true; + null = true; } -Jid::~Jid() -{ -} +Jid::~Jid() { } -Jid::Jid(const QString &s) -{ - set(s); -} +Jid::Jid(const QString &s) { set(s); } -Jid::Jid(const QString &node, const QString& domain, const QString& resource) -{ - set(domain, node, resource); -} +Jid::Jid(const QString &node, const QString &domain, const QString &resource) { set(domain, node, resource); } -Jid::Jid(const char *s) -{ - set(QString(s)); -} +Jid::Jid(const char *s) { set(QString(s)); } -Jid & Jid::operator=(const QString &s) +Jid &Jid::operator=(const QString &s) { set(s); return *this; } -Jid & Jid::operator=(const char *s) +Jid &Jid::operator=(const char *s) { set(QString(s)); return *this; @@ -238,27 +201,27 @@ Jid & Jid::operator=(const char *s) void Jid::reset() { - f = QString(); - b = QString(); - d = QString(); - n = QString(); - r = QString(); + f = QString(); + b = QString(); + d = QString(); + n = QString(); + r = QString(); valid = false; - null = true; + null = true; } void Jid::update() { // build 'bare' and 'full' jids - if(n.isEmpty()) + if (n.isEmpty()) b = d; else b = n + '@' + d; - if(r.isEmpty()) + if (r.isEmpty()) f = b; else f = b + '/' + r; - if(f.isEmpty()) + if (f.isEmpty()) valid = false; null = f.isEmpty() && r.isEmpty(); } @@ -267,66 +230,61 @@ void Jid::set(const QString &s) { QString rest, domain, node, resource; QString norm_domain, norm_node, norm_resource; - int x = s.indexOf('/'); - if(x != -1) { - rest = s.mid(0, x); - resource = s.mid(x+1); - } - else { - rest = s; + int x = s.indexOf('/'); + if (x != -1) { + rest = s.mid(0, x); + resource = s.mid(x + 1); + } else { + rest = s; resource = QString(); } - if(!validResource(resource, norm_resource)) { + if (!validResource(resource, norm_resource)) { reset(); return; } x = rest.indexOf('@'); - if(x != -1) { - node = rest.mid(0, x); - domain = rest.mid(x+1); - } - else { - node = QString(); + if (x != -1) { + node = rest.mid(0, x); + domain = rest.mid(x + 1); + } else { + node = QString(); domain = rest; } - if(!validDomain(domain, norm_domain) || !validNode(node, norm_node)) { + if (!validDomain(domain, norm_domain) || !validNode(node, norm_node)) { reset(); return; } valid = true; - null = false; - d = norm_domain; - n = norm_node; - r = norm_resource; + null = false; + d = norm_domain; + n = norm_node; + r = norm_resource; update(); } void Jid::set(const QString &domain, const QString &node, const QString &resource) { QString norm_domain, norm_node, norm_resource; - if(!validDomain(domain, norm_domain) || - !validNode(node, norm_node) || - !validResource(resource, norm_resource)) - { + if (!validDomain(domain, norm_domain) || !validNode(node, norm_node) || !validResource(resource, norm_resource)) { reset(); return; } valid = true; - null = false; - d = norm_domain; - n = norm_node; - r = norm_resource; + null = false; + d = norm_domain; + n = norm_node; + r = norm_resource; update(); } void Jid::setDomain(const QString &s) { - if(!valid) + if (!valid) return; QString norm; - if(!validDomain(s, norm)) { + if (!validDomain(s, norm)) { reset(); return; } @@ -336,10 +294,10 @@ void Jid::setDomain(const QString &s) void Jid::setNode(const QString &s) { - if(!valid) + if (!valid) return; QString norm; - if(!validNode(s, norm)) { + if (!validNode(s, norm)) { reset(); return; } @@ -349,10 +307,10 @@ void Jid::setNode(const QString &s) void Jid::setResource(const QString &s) { - if(!valid) + if (!valid) return; QString norm; - if(!validResource(s, norm)) { + if (!validResource(s, norm)) { reset(); return; } @@ -381,27 +339,18 @@ Jid Jid::withResource(const QString &s) const return j; } -bool Jid::isValid() const -{ - return valid; -} +bool Jid::isValid() const { return valid; } -bool Jid::isEmpty() const -{ - return f.isEmpty(); -} +bool Jid::isEmpty() const { return f.isEmpty(); } bool Jid::compare(const Jid &a, bool compareRes) const { - if(null && a.null) + if (null && a.null) return true; // only compare valid jids - if(!valid || !a.valid) - return false; - - if(compareRes ? (f != a.f) : (b != a.b)) + if (!valid || !a.valid) return false; - return true; + return !(compareRes ? (f != a.f) : (b != a.b)); } diff --git a/src/xmpp/jid/jid.h b/src/xmpp/jid/jid.h index 92ee5d84..08966774 100644 --- a/src/xmpp/jid/jid.h +++ b/src/xmpp/jid/jid.h @@ -20,84 +20,89 @@ #ifndef XMPP_JID_H #define XMPP_JID_H -#include -#include #include #include - -namespace XMPP +#include +#include + +namespace XMPP { +class StringPrepCache { +public: + static bool nameprep(const QString &in, int maxbytes, QString &out); + static bool nodeprep(const QString &in, int maxbytes, QString &out); + static bool resourceprep(const QString &in, int maxbytes, QString &out); + static bool saslprep(const QString &in, int maxbytes, QString &out); + + static void cleanup(); + +private: + QHash nameprep_table; + QHash nodeprep_table; + QHash resourceprep_table; + QHash saslprep_table; + + static std::unique_ptr _instance; + static StringPrepCache *instance(); + + StringPrepCache(); +}; + +class Jid { +public: + Jid(); + ~Jid(); + + Jid(const QString &s); + Jid(const QString &node, const QString &domain, const QString &resource = ""); + Jid(const char *s); + Jid &operator=(const QString &s); + Jid &operator=(const char *s); + + bool isNull() const { return null; } + const QString &domain() const { return d; } + const QString &node() const { return n; } + const QString &resource() const { return r; } + const QString &bare() const { return b; } + const QString &full() const { return f; } + + Jid withNode(const QString &s) const; + Jid withDomain(const QString &s) const; + Jid withResource(const QString &s) const; + + bool isValid() const; + bool isEmpty() const; + bool compare(const Jid &a, bool compareRes = true) const; + inline bool operator==(const Jid &other) const { return compare(other, true); } + inline bool operator!=(const Jid &other) const { return !(*this == other); } + +private: + void set(const QString &s); + void set(const QString &domain, const QString &node, const QString &resource = ""); + + void setDomain(const QString &s); + void setNode(const QString &s); + void setResource(const QString &s); + +private: + void reset(); + void update(); + + QString f, b, d, n, r; + bool valid, null; +}; + +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) +Q_DECL_PURE_FUNCTION inline uint qHash(const XMPP::Jid &key, uint seed = 0) Q_DECL_NOTHROW { - class StringPrepCache - { - public: - static bool nameprep(const QString &in, int maxbytes, QString& out); - static bool nodeprep(const QString &in, int maxbytes, QString& out); - static bool resourceprep(const QString &in, int maxbytes, QString& out); - static bool saslprep(const QString &in, int maxbytes, QString& out); - - static void cleanup(); - private: - - QHash nameprep_table; - QHash nodeprep_table; - QHash resourceprep_table; - QHash saslprep_table; - - static QScopedPointer _instance; - static StringPrepCache *instance(); - - StringPrepCache(); - }; - - class Jid - { - public: - Jid(); - ~Jid(); - - Jid(const QString &s); - Jid(const QString &node, const QString& domain, const QString& resource = ""); - Jid(const char *s); - Jid & operator=(const QString &s); - Jid & operator=(const char *s); - - bool isNull() const { return null; } - const QString & domain() const { return d; } - const QString & node() const { return n; } - const QString & resource() const { return r; } - const QString & bare() const { return b; } - const QString & full() const { return f; } - - Jid withNode(const QString &s) const; - Jid withDomain(const QString &s) const; - Jid withResource(const QString &s) const; - - bool isValid() const; - bool isEmpty() const; - bool compare(const Jid &a, bool compareRes=true) const; - inline bool operator==(const Jid &other) const { return compare(other, true); } - inline bool operator!=(const Jid &other) const { return !(*this == other); } - - private: - void set(const QString &s); - void set(const QString &domain, const QString &node, const QString &resource=""); - - void setDomain(const QString &s); - void setNode(const QString &s); - void setResource(const QString &s); - - private: - void reset(); - void update(); - - QString f, b, d, n, r; - bool valid, null; - }; - - Q_DECL_PURE_FUNCTION inline uint qHash(const XMPP::Jid &key, uint seed = 0) Q_DECL_NOTHROW - { - return qHash(key.full(), seed); - } + return qHash(key.full(), seed); +} +#else +Q_DECL_PURE_FUNCTION inline size_t qHash(const XMPP::Jid &key, size_t seed = 0) Q_DECL_NOTHROW +{ + return qHash(key.full(), seed); } - #endif + +} // namespace XMPP + +#endif // XMPP_JID_H diff --git a/src/xmpp/jid/jid.pri b/src/xmpp/jid/jid.pri deleted file mode 100644 index b67e6ff3..00000000 --- a/src/xmpp/jid/jid.pri +++ /dev/null @@ -1,8 +0,0 @@ -INCLUDEPATH *= $$PWD/../.. -DEPENDPATH *= $$PWD/../.. - -HEADERS += \ - $$PWD/jid.h - -SOURCES += \ - $$PWD/jid.cpp diff --git a/src/xmpp/jid/unittest/jidtest.cpp b/src/xmpp/jid/unittest/jidtest.cpp index fa4dc602..1acbe9e3 100644 --- a/src/xmpp/jid/unittest/jidtest.cpp +++ b/src/xmpp/jid/unittest/jidtest.cpp @@ -18,26 +18,26 @@ // FIXME: Complete this -#include -#include - -#include "qttestutil/qttestutil.h" #include "xmpp/jid/jid.h" +#include "qttestutil/qttestutil.h" + +#include +#include using namespace XMPP; -class JidTest : public QObject -{ - Q_OBJECT +class JidTest : public QObject { + Q_OBJECT - private slots: - void testConstructorWithString() { - Jid testling("foo@bar/baz"); +private slots: + void testConstructorWithString() + { + Jid testling("foo@bar/baz"); - QCOMPARE(testling.node(), QString("foo")); - QCOMPARE(testling.domain(), QString("bar")); - QCOMPARE(testling.resource(), QString("baz")); - } + QCOMPARE(testling.node(), QString("foo")); + QCOMPARE(testling.domain(), QString("bar")); + QCOMPARE(testling.resource(), QString("baz")); + } }; QTTESTUTIL_REGISTER_TEST(JidTest); diff --git a/src/xmpp/jid/unittest/unittest.pri b/src/xmpp/jid/unittest/unittest.pri deleted file mode 100644 index e0994628..00000000 --- a/src/xmpp/jid/unittest/unittest.pri +++ /dev/null @@ -1,2 +0,0 @@ -SOURCES += \ - $$PWD/jidtest.cpp diff --git a/src/xmpp/jid/unittest/unittest.pro b/src/xmpp/jid/unittest/unittest.pro deleted file mode 100644 index fd9fa52b..00000000 --- a/src/xmpp/jid/unittest/unittest.pro +++ /dev/null @@ -1,4 +0,0 @@ -include(../../modules.pri) -include($$IRIS_XMPP_QA_UNITTEST_MODULE) -include($$IRIS_XMPP_JID_MODULE) -include(unittest.pri) diff --git a/src/xmpp/modules.pri b/src/xmpp/modules.pri deleted file mode 100644 index 9a31f009..00000000 --- a/src/xmpp/modules.pri +++ /dev/null @@ -1,6 +0,0 @@ -IRIS_XMPP_QA_UNITTEST_MODULE = $$PWD/qa/unittest.pri -IRIS_XMPP_JID_MODULE = $$PWD/jid/jid.pri -IRIS_XMPP_SASL_MODULE = $$PWD/sasl/sasl.pri -IRIS_XMPP_BASE_MODULE = $$PWD/base/base.pri -IRIS_XMPP_ZLIB_MODULE = $$PWD/zlib/zlib.pri -IRIS_XMPP_BLAKE2_MODULE = $$PWD/blake2/blake2.pri diff --git a/src/xmpp/qa/README b/src/xmpp/qa/README index 05425a06..d2f3b124 100644 --- a/src/xmpp/qa/README +++ b/src/xmpp/qa/README @@ -3,11 +3,11 @@ How to add unit tests to a module - Copy the qa/unittest.template module to the module dir, and rename it to 'unittest'. Be careful not to copy '.svn'. - Create a file test.cpp for every class, and implement the tests - for each method.. See 'myclasstest.cpp' for a template. + for each method.. See 'myclasstest.cpp' for a template. - Add every test.cpp file to 'unittest.pri' -- In 'unittest.pro', replace '$$MYMODULE' with the name of the module that you +- In 'unittest.pro', replace '$$MYMODULE' with the name of the module that you are unit testing. Also include all the modules that are required to compile - a standalone checker for the module under test. + a standalone checker for the module under test. - Add an 'include' line to the list of unit tests in qa/unittests.pri. This is used to compile the full unit test suite of all modules. - Make sure 'qa/unittests.pro' contains the module under test, and make diff --git a/src/xmpp/qa/qttestutil/example/example.pro b/src/xmpp/qa/qttestutil/example/example.pro deleted file mode 100644 index 086b4790..00000000 --- a/src/xmpp/qa/qttestutil/example/example.pro +++ /dev/null @@ -1,23 +0,0 @@ -# -# Example unit test module with 2 unit test suites. -# - -include(../qttestutil.pri) - -QT += testlib -QT -= gui -CONFIG -= app_bundle - -TARGET = checker - -SOURCES += \ - myfirstclasstest.cpp \ - mysecondclasstest.cpp \ - ../simplechecker.cpp - -# Add an extra 'make check' target. -QMAKE_EXTRA_TARGETS = check -check.commands = \$(MAKE) && ./$(QMAKE_TARGET) - -# Cleanup the checker on 'make clean' -QMAKE_CLEAN += $(QMAKE_TARGET) diff --git a/src/xmpp/qa/qttestutil/example/myfirstclasstest.cpp b/src/xmpp/qa/qttestutil/example/myfirstclasstest.cpp index 122742a7..89c12a0a 100644 --- a/src/xmpp/qa/qttestutil/example/myfirstclasstest.cpp +++ b/src/xmpp/qa/qttestutil/example/myfirstclasstest.cpp @@ -1,22 +1,20 @@ +#include "qttestutil/qttestutil.h" + #include #include -#include "qttestutil/qttestutil.h" - -class MyFirstClassTest : public QObject -{ - Q_OBJECT +class MyFirstClassTest : public QObject { + Q_OBJECT - private slots: - void initTestCase() { - } +private slots: + void initTestCase() { } - void cleanupTestCase() { - } + void cleanupTestCase() { } - void testMyMethod() { - QCOMPARE(1, 1); // Dummy test - } + void testMyMethod() + { + QCOMPARE(1, 1); // Dummy test + } }; QTTESTUTIL_REGISTER_TEST(MyFirstClassTest); diff --git a/src/xmpp/qa/qttestutil/example/mysecondclasstest.cpp b/src/xmpp/qa/qttestutil/example/mysecondclasstest.cpp index 80be96d5..4d73775f 100644 --- a/src/xmpp/qa/qttestutil/example/mysecondclasstest.cpp +++ b/src/xmpp/qa/qttestutil/example/mysecondclasstest.cpp @@ -1,22 +1,20 @@ +#include "qttestutil/qttestutil.h" + #include #include -#include "qttestutil/qttestutil.h" - -class MySecondClassTest : public QObject -{ - Q_OBJECT +class MySecondClassTest : public QObject { + Q_OBJECT - private slots: - void initTestCase() { - } +private slots: + void initTestCase() { } - void cleanupTestCase() { - } + void cleanupTestCase() { } - void testMyMethod() { - QCOMPARE(1, 0); // Dummy test - } + void testMyMethod() + { + QCOMPARE(1, 0); // Dummy test + } }; QTTESTUTIL_REGISTER_TEST(MySecondClassTest); diff --git a/src/xmpp/qa/qttestutil/qttestutil.h b/src/xmpp/qa/qttestutil/qttestutil.h index 69729aff..91f19baf 100644 --- a/src/xmpp/qa/qttestutil/qttestutil.h +++ b/src/xmpp/qa/qttestutil/qttestutil.h @@ -35,7 +35,6 @@ * * QTTESTUTIL_REGISTER_TEST(MyTest) */ -#define QTTESTUTIL_REGISTER_TEST(TestClass) \ - static QtTestUtil::TestRegistration TestClass##Registration +#define QTTESTUTIL_REGISTER_TEST(TestClass) static QtTestUtil::TestRegistration TestClass##Registration -#endif +#endif // QTTESTUTIL_H diff --git a/src/xmpp/qa/qttestutil/qttestutil.pri b/src/xmpp/qa/qttestutil/qttestutil.pri deleted file mode 100644 index e11607b3..00000000 --- a/src/xmpp/qa/qttestutil/qttestutil.pri +++ /dev/null @@ -1,5 +0,0 @@ -INCLUDEPATH *= $$PWD/.. -DEPENDPATH *= $$PWD/.. - -SOURCES += \ - $$PWD/testregistry.cpp diff --git a/src/xmpp/qa/qttestutil/simplechecker.cpp b/src/xmpp/qa/qttestutil/simplechecker.cpp index 1ac56714..17710777 100644 --- a/src/xmpp/qa/qttestutil/simplechecker.cpp +++ b/src/xmpp/qa/qttestutil/simplechecker.cpp @@ -16,14 +16,14 @@ * */ -#include - #include "qttestutil/testregistry.h" +#include + /** * Runs all tests registered with the QtTestUtil registry. */ -int main(int argc, char* argv[]) +int main(int argc, char *argv[]) { QCoreApplication application(argc, argv); return QtTestUtil::TestRegistry::getInstance()->runTests(argc, argv); diff --git a/src/xmpp/qa/qttestutil/testregistration.h b/src/xmpp/qa/qttestutil/testregistration.h index 7754c735..741d6464 100644 --- a/src/xmpp/qa/qttestutil/testregistration.h +++ b/src/xmpp/qa/qttestutil/testregistration.h @@ -22,30 +22,25 @@ #include "qttestutil/testregistry.h" namespace QtTestUtil { - - /** - * A wrapper class around a test to manage registration and static - * creation of an instance of the test class. - * This class is used by QTTESTUTIL_REGISTER_TEST(), and you should not - * use this class directly. - */ - template - class TestRegistration +/** + * A wrapper class around a test to manage registration and static + * creation of an instance of the test class. + * This class is used by QTTESTUTIL_REGISTER_TEST(), and you should not + * use this class directly. + */ +template class TestRegistration { +public: + TestRegistration() { - public: - TestRegistration() { - test_ = new TestClass(); - TestRegistry::getInstance()->registerTest(test_); - } - - ~TestRegistration() { - delete test_; - } + test_ = new TestClass(); + TestRegistry::getInstance()->registerTest(test_); + } - private: - TestClass* test_; - }; + ~TestRegistration() { delete test_; } -} +private: + TestClass *test_; +}; +} // namespace QtTestUtil -#endif +#endif // QTTESTUTIL_TESTREGISTRATION_H diff --git a/src/xmpp/qa/qttestutil/testregistry.cpp b/src/xmpp/qa/qttestutil/testregistry.cpp index 9afaf083..ca5d91e8 100644 --- a/src/xmpp/qa/qttestutil/testregistry.cpp +++ b/src/xmpp/qa/qttestutil/testregistry.cpp @@ -21,25 +21,20 @@ #include namespace QtTestUtil { - -TestRegistry* TestRegistry::getInstance() +TestRegistry *TestRegistry::getInstance() { static TestRegistry registry; return ®istry; } -void TestRegistry::registerTest(QObject* test) -{ - tests_ += test; -} +void TestRegistry::registerTest(QObject *test) { tests_ += test; } -int TestRegistry::runTests(int argc, char* argv[]) +int TestRegistry::runTests(int argc, char *argv[]) { int result = 0; - foreach(QObject* test, tests_) { + foreach (QObject *test, tests_) { result |= QTest::qExec(test, argc, argv); } return result; } - -} +} // namespace QtTestUtil diff --git a/src/xmpp/qa/qttestutil/testregistry.h b/src/xmpp/qa/qttestutil/testregistry.h index 332b8013..ff56252a 100644 --- a/src/xmpp/qa/qttestutil/testregistry.h +++ b/src/xmpp/qa/qttestutil/testregistry.h @@ -24,39 +24,37 @@ class QObject; namespace QtTestUtil { +/** + * A registry of QtTest test classes. + * All test classes registered with QTTESTUTIL_REGISTER_TEST add + * themselves to this registry. All registered tests can then be run at + * once using runTests(). + */ +class TestRegistry { +public: + /** + * Retrieve the single instance of the registry. + */ + static TestRegistry *getInstance(); /** - * A registry of QtTest test classes. - * All test classes registered with QTTESTUTIL_REGISTER_TEST add - * themselves to this registry. All registered tests can then be run at - * once using runTests(). + * Register a QtTest test. + * This method is called by QTTESTUTIL_REGISTER_TEST, and you should + * not use this method directly. */ - class TestRegistry - { - public: - /** - * Retrieve the single instance of the registry. - */ - static TestRegistry* getInstance(); - - /** - * Register a QtTest test. - * This method is called by QTTESTUTIL_REGISTER_TEST, and you should - * not use this method directly. - */ - void registerTest(QObject*); - - /** - * Run all registered tests using QTest::qExec() - */ - int runTests(int argc, char* argv[]); - - private: - TestRegistry() {} - - private: - QList tests_; - }; -} - -#endif + void registerTest(QObject *); + + /** + * Run all registered tests using QTest::qExec() + */ + int runTests(int argc, char *argv[]); + +private: + TestRegistry() { } + +private: + QList tests_; +}; +} // namespace QtTestUtil + +#endif // QTTESTUTIL_TESTREGISTRY_H diff --git a/src/xmpp/qa/unittest.pri b/src/xmpp/qa/unittest.pri deleted file mode 100644 index 116b7096..00000000 --- a/src/xmpp/qa/unittest.pri +++ /dev/null @@ -1,29 +0,0 @@ -# -# Declares all common settings/includes for a unittest module. -# -# Include this file from your module's unittest project file to create a -# standalone checker for the module. -# - - -CONFIG += crypto - -include($$PWD/qttestutil/qttestutil.pri) -include($$PWD/../common.pri) - -QT += testlib -QT -= gui -CONFIG -= app_bundle - -INCLUDEPATH *= $$PWD -DEPENDPATH *= $$PWD - -TARGET = checker - -SOURCES += \ - $$PWD/qttestutil/simplechecker.cpp - -QMAKE_EXTRA_TARGETS = check -check.commands = \$(MAKE) && ./checker - -QMAKE_CLEAN += $(QMAKE_TARGET) diff --git a/src/xmpp/qa/unittest.template/myclasstest.cpp b/src/xmpp/qa/unittest.template/myclasstest.cpp index d665feba..ff7008ae 100644 --- a/src/xmpp/qa/unittest.template/myclasstest.cpp +++ b/src/xmpp/qa/unittest.template/myclasstest.cpp @@ -16,26 +16,24 @@ * */ +#include "qttestutil/qttestutil.h" + #include #include -#include "qttestutil/qttestutil.h" - -class MyClassTest : public QObject -{ - Q_OBJECT +class MyClassTest : public QObject { + Q_OBJECT - private slots: - void initTestCase() { - } +private slots: + void initTestCase() { } - void cleanupTestCase() { - } + void cleanupTestCase() { } - void testMyMethod() { - //QCOMPARE(foo, bar); - //QVERIFY(baz); - } + void testMyMethod() + { + // QCOMPARE(foo, bar); + // QVERIFY(baz); + } }; QTTESTUTIL_REGISTER_TEST(MyClassTest); diff --git a/src/xmpp/qa/unittest.template/unittest.pri b/src/xmpp/qa/unittest.template/unittest.pri deleted file mode 100644 index fd93c47b..00000000 --- a/src/xmpp/qa/unittest.template/unittest.pri +++ /dev/null @@ -1,2 +0,0 @@ -SOURCES += \ - $$PWD/myclasstest.cpp diff --git a/src/xmpp/qa/unittest.template/unittest.pro b/src/xmpp/qa/unittest.template/unittest.pro deleted file mode 100644 index e2574cbf..00000000 --- a/src/xmpp/qa/unittest.template/unittest.pro +++ /dev/null @@ -1,4 +0,0 @@ -include(../../modules.pri) -include($$IRIS_XMPP_QA_UNITTEST_MODULE) -include($$MYMODULE) -include(unittest.pri) diff --git a/src/xmpp/qa/unittests.pri b/src/xmpp/qa/unittests.pri deleted file mode 100644 index 2509e1e5..00000000 --- a/src/xmpp/qa/unittests.pri +++ /dev/null @@ -1,4 +0,0 @@ -# All available unit tests - -include($$PWD/../base/unittest/unittest.pri) -include($$PWD/../sasl/unittest/unittest.pri) diff --git a/src/xmpp/qa/unittests/unittests.pro b/src/xmpp/qa/unittests/unittests.pro deleted file mode 100644 index 00176049..00000000 --- a/src/xmpp/qa/unittests/unittests.pro +++ /dev/null @@ -1,6 +0,0 @@ -include(../../../../iris.pri) -include(../unittests.pri) -include(../unittest.pri) - -# FIXME -include(../../../../../third-party/qca/qca.pri) diff --git a/src/xmpp/sasl/digestmd5proplist.cpp b/src/xmpp/sasl/digestmd5proplist.cpp index f5e0bfe0..e4bfc5ff 100644 --- a/src/xmpp/sasl/digestmd5proplist.cpp +++ b/src/xmpp/sasl/digestmd5proplist.cpp @@ -19,12 +19,10 @@ #include "xmpp/sasl/digestmd5proplist.h" namespace XMPP { +DIGESTMD5PropList::DIGESTMD5PropList() : QList() { } -DIGESTMD5PropList::DIGESTMD5PropList() : QList() +void DIGESTMD5PropList::set(const QByteArray &var, const QByteArray &val) { -} - -void DIGESTMD5PropList::set(const QByteArray &var, const QByteArray &val) { DIGESTMD5Prop p; p.var = var; p.val = val; @@ -33,9 +31,9 @@ void DIGESTMD5PropList::set(const QByteArray &var, const QByteArray &val) { QByteArray DIGESTMD5PropList::get(const QByteArray &var) const { - for(ConstIterator it = begin(); it != end(); ++it) { - if((*it).var == var) - return (*it).val; + for (const auto &it : std::as_const(*this)) { + if (it.var == var) + return it.val; } return QByteArray(); } @@ -43,14 +41,15 @@ QByteArray DIGESTMD5PropList::get(const QByteArray &var) const QByteArray DIGESTMD5PropList::toString() const { QByteArray str; - bool first = true; - for(ConstIterator it = begin(); it != end(); ++it) { - if(!first) + bool first = true; + for (const auto &it : std::as_const(*this)) { + if (!first) str += ','; - if ((*it).var == "realm" || (*it).var == "nonce" || (*it).var == "username" || (*it).var == "cnonce" || (*it).var == "digest-uri" || (*it).var == "authzid") - str += (*it).var + "=\"" + (*it).val + '\"'; + if (it.var == "realm" || it.var == "nonce" || it.var == "username" || it.var == "cnonce" + || it.var == "digest-uri" || it.var == "authzid") + str += it.var + "=\"" + it.val + '\"'; else - str += (*it).var + "=" + (*it).val; + str += it.var + "=" + it.val; first = false; } return str; @@ -59,30 +58,29 @@ QByteArray DIGESTMD5PropList::toString() const bool DIGESTMD5PropList::fromString(const QByteArray &str) { DIGESTMD5PropList list; - int at = 0; - while(1) { + int at = 0; + while (1) { while (at < str.length() && (str[at] == ',' || str[at] == ' ' || str[at] == '\t')) - ++at; + ++at; int n = str.indexOf('=', at); - if(n == -1) + if (n == -1) break; QByteArray var, val; - var = str.mid(at, n-at); - at = n + 1; - if(str[at] == '\"') { + var = str.mid(at, n - at); + at = n + 1; + if (str[at] == '\"') { ++at; n = str.indexOf('\"', at); - if(n == -1) + if (n == -1) break; - val = str.mid(at, n-at); - at = n + 1; - } - else { + val = str.mid(at, n - at); + at = n + 1; + } else { n = at; while (n < str.length() && str[n] != ',' && str[n] != ' ' && str[n] != '\t') ++n; - val = str.mid(at, n-at); - at = n; + val = str.mid(at, n - at); + at = n; } DIGESTMD5Prop prop; prop.var = var; @@ -93,27 +91,26 @@ bool DIGESTMD5PropList::fromString(const QByteArray &str) ++a; if (a == val.length()) break; - n = a+1; + n = a + 1; while (n < val.length() && val[n] != ',' && val[n] != ' ' && val[n] != '\t') ++n; - prop.val = val.mid(a, n-a); + prop.val = val.mid(a, n - a); list.append(prop); - a = n+1; + a = n + 1; } - } - else { + } else { prop.val = val; list.append(prop); } - if(at >= str.size() - 1 || (str[at] != ',' && str[at] != ' ' && str[at] != '\t')) + if (at >= str.size() - 1 || (str[at] != ',' && str[at] != ' ' && str[at] != '\t')) break; } // integrity check - if(list.varCount("nonce") != 1) + if (list.varCount("nonce") != 1) return false; - if(list.varCount("algorithm") != 1) + if (list.varCount("algorithm") != 1) return false; *this = list; return true; @@ -122,11 +119,11 @@ bool DIGESTMD5PropList::fromString(const QByteArray &str) int DIGESTMD5PropList::varCount(const QByteArray &var) const { int n = 0; - for(ConstIterator it = begin(); it != end(); ++it) { - if((*it).var == var) + for (const auto &it : std::as_const(*this)) { + if (it.var == var) ++n; } return n; } -} +} // namespace XMPP diff --git a/src/xmpp/sasl/digestmd5proplist.h b/src/xmpp/sasl/digestmd5proplist.h index f6c9f4db..a7f1ae99 100644 --- a/src/xmpp/sasl/digestmd5proplist.h +++ b/src/xmpp/sasl/digestmd5proplist.h @@ -19,28 +19,26 @@ #ifndef DIGESTMD5PROPLIST_H #define DIGESTMD5PROPLIST_H -#include #include +#include namespace XMPP { - struct DIGESTMD5Prop - { - QByteArray var, val; - }; +struct DIGESTMD5Prop { + QByteArray var, val; +}; - class DIGESTMD5PropList : public QList - { - public: - DIGESTMD5PropList(); +class DIGESTMD5PropList : public QList { +public: + DIGESTMD5PropList(); - void set(const QByteArray &var, const QByteArray &val); - QByteArray get(const QByteArray &var) const; - QByteArray toString() const; - bool fromString(const QByteArray &str); + void set(const QByteArray &var, const QByteArray &val); + QByteArray get(const QByteArray &var) const; + QByteArray toString() const; + bool fromString(const QByteArray &str); - private: - int varCount(const QByteArray &var) const; - }; -} +private: + int varCount(const QByteArray &var) const; +}; +} // namespace XMPP -#endif +#endif // DIGESTMD5PROPLIST_H diff --git a/src/xmpp/sasl/digestmd5response.cpp b/src/xmpp/sasl/digestmd5response.cpp index 02f625a2..44b39f6f 100644 --- a/src/xmpp/sasl/digestmd5response.cpp +++ b/src/xmpp/sasl/digestmd5response.cpp @@ -18,32 +18,33 @@ #include "xmpp/sasl/digestmd5response.h" +#include "xmpp/sasl/digestmd5proplist.h" + #include +#include #include #include -#include "xmpp/sasl/digestmd5proplist.h" -#include "xmpp/base/randomnumbergenerator.h" - namespace XMPP { - -DIGESTMD5Response::DIGESTMD5Response(const QByteArray& challenge, const QString& service, const QString& host, const QString& arealm, const QString& user, const QString& authz, const QByteArray& password, const RandomNumberGenerator& rand) : isValid_(true) +DIGESTMD5Response::DIGESTMD5Response(const QByteArray &challenge, const QString &service, const QString &host, + const QString &arealm, const QString &user, const QString &authz, + const QByteArray &password) : isValid_(true) { QString realm = arealm; // get props DIGESTMD5PropList in; - if(!in.fromString(challenge)) { + if (!in.fromString(challenge)) { isValid_ = false; return; } - //qDebug() << (QString("simplesasl.cpp: IN: %1").arg(QString(in.toString()))); + // qDebug() << (QString("simplesasl.cpp: IN: %1").arg(QString(in.toString()))); // make a cnonce QByteArray a; a.resize(32); - for(int n = 0; n < (int)a.size(); ++n) { - a[n] = (char) rand.generateNumberBetween(0, 255); + for (int n = 0; n < (int)a.size(); ++n) { + a[n] = (char)QRandomGenerator::global()->bounded(0, 256); } QByteArray cnonce = a.toBase64(); @@ -52,28 +53,28 @@ DIGESTMD5Response::DIGESTMD5Response(const QByteArray& challenge, const QString& realm = QString::fromUtf8(in.get("realm")); } QByteArray nonce = in.get("nonce"); - QByteArray nc = "00000001"; - QByteArray uri = service.toUtf8() + '/' + host.toUtf8(); - QByteArray qop = "auth"; + QByteArray nc = "00000001"; + QByteArray uri = service.toUtf8() + '/' + host.toUtf8(); + QByteArray qop = "auth"; // build 'response' - QByteArray X = user.toUtf8() + ':' + realm.toUtf8() + ':' + password; - QByteArray Y = QCA::Hash("md5").hash(X).toByteArray(); + QByteArray X = user.toUtf8() + ':' + realm.toUtf8() + ':' + password; + QByteArray Y = QCA::Hash("md5").hash(X).toByteArray(); QByteArray tmp = ':' + nonce + ':' + cnonce; if (!authz.isEmpty()) tmp += ':' + authz.toUtf8(); - //qDebug() << (QString(tmp)); + // qDebug() << (QString(tmp)); QByteArray A1(Y + tmp); - QByteArray A2 = QByteArray("AUTHENTICATE:") + uri; + QByteArray A2 = QByteArray("AUTHENTICATE:") + uri; QByteArray HA1 = QCA::Hash("md5").hashToString(A1).toLatin1(); QByteArray HA2 = QCA::Hash("md5").hashToString(A2).toLatin1(); - QByteArray KD = HA1 + ':' + nonce + ':' + nc + ':' + cnonce + ':' + qop + ':' + HA2; - QByteArray Z = QCA::Hash("md5").hashToString(KD).toLatin1(); + QByteArray KD = HA1 + ':' + nonce + ':' + nc + ':' + cnonce + ':' + qop + ':' + HA2; + QByteArray Z = QCA::Hash("md5").hashToString(KD).toLatin1(); - //qDebug() << QString("simplesasl.cpp: A1 = %1").arg(QString(A1)); - //qDebug() << QString("simplesasl.cpp: A2 = %1").arg(QString(A2)); - //qDebug() << QString("simplesasl.cpp: KD = %1").arg(QString(KD)); + // qDebug() << QString("simplesasl.cpp: A1 = %1").arg(QString(A1)); + // qDebug() << QString("simplesasl.cpp: A2 = %1").arg(QString(A2)); + // qDebug() << QString("simplesasl.cpp: KD = %1").arg(QString(KD)); // build output DIGESTMD5PropList out; @@ -83,15 +84,14 @@ DIGESTMD5Response::DIGESTMD5Response(const QByteArray& challenge, const QString& out.set("nonce", nonce); out.set("cnonce", cnonce); out.set("nc", nc); - //out.set("serv-type", service.toUtf8()); - //out.set("host", host.toUtf8()); + // out.set("serv-type", service.toUtf8()); + // out.set("host", host.toUtf8()); out.set("digest-uri", uri); out.set("qop", qop); out.set("response", Z); out.set("charset", "utf-8"); if (!authz.isEmpty()) out.set("authzid", authz.toUtf8()); - value_ = out.toString(); -} - + value_ = out.toString(); } +} // namespace XMPP diff --git a/src/xmpp/sasl/digestmd5response.h b/src/xmpp/sasl/digestmd5response.h index 403b3948..0f5d05d0 100644 --- a/src/xmpp/sasl/digestmd5response.h +++ b/src/xmpp/sasl/digestmd5response.h @@ -23,33 +23,20 @@ #include namespace XMPP { - class RandomNumberGenerator; - - class DIGESTMD5Response - { - public: - DIGESTMD5Response( - const QByteArray& challenge, - const QString& service, - const QString& host, - const QString& realm, - const QString& user, - const QString& authz, - const QByteArray& password, - const RandomNumberGenerator& rand); - - const QByteArray& getValue() const { - return value_; - } - - bool isValid() const { - return isValid_; - } - - private: - bool isValid_; - QByteArray value_; - }; -} - -#endif + +class DIGESTMD5Response { +public: + DIGESTMD5Response(const QByteArray &challenge, const QString &service, const QString &host, const QString &realm, + const QString &user, const QString &authz, const QByteArray &password); + + const QByteArray &getValue() const { return value_; } + + bool isValid() const { return isValid_; } + +private: + bool isValid_; + QByteArray value_; +}; +} // namespace XMPP + +#endif // DIGESTMD5RESPONSE_H diff --git a/src/xmpp/sasl/plainmessage.cpp b/src/xmpp/sasl/plainmessage.cpp index b067859a..46ac34cd 100644 --- a/src/xmpp/sasl/plainmessage.cpp +++ b/src/xmpp/sasl/plainmessage.cpp @@ -19,10 +19,9 @@ #include "xmpp/sasl/plainmessage.h" namespace XMPP { - -PLAINMessage::PLAINMessage(const QString& authzid, const QString& authcid, const QByteArray& password) +PLAINMessage::PLAINMessage(const QString &authzid, const QString &authcid, const QByteArray &password) { value_ = authzid.toUtf8() + '\0' + authcid.toUtf8() + '\0' + password; } -} +} // namespace XMPP diff --git a/src/xmpp/sasl/plainmessage.h b/src/xmpp/sasl/plainmessage.h index 127613b9..949a8749 100644 --- a/src/xmpp/sasl/plainmessage.h +++ b/src/xmpp/sasl/plainmessage.h @@ -23,18 +23,15 @@ #include namespace XMPP { - class PLAINMessage - { - public: - PLAINMessage(const QString& authzid, const QString& authcid, const QByteArray& password); +class PLAINMessage { +public: + PLAINMessage(const QString &authzid, const QString &authcid, const QByteArray &password); - const QByteArray& getValue() { - return value_; - } + const QByteArray &getValue() { return value_; } - private: - QByteArray value_; - }; -} +private: + QByteArray value_; +}; +} // namespace XMPP -#endif +#endif // PLAINMESSAGE_H diff --git a/src/xmpp/sasl/sasl.pri b/src/xmpp/sasl/sasl.pri deleted file mode 100644 index 1248370c..00000000 --- a/src/xmpp/sasl/sasl.pri +++ /dev/null @@ -1,18 +0,0 @@ -INCLUDEPATH *= $$PWD/../.. -DEPENDPATH *= $$PWD/../.. - -HEADERS += \ - $$PWD/plainmessage.h \ - $$PWD/digestmd5proplist.h \ - $$PWD/digestmd5response.h \ - $$PWD/scramsha1message.h \ - $$PWD/scramsha1response.h \ - $$PWD/scramsha1signature.cpp - -SOURCES += \ - $$PWD/plainmessage.cpp \ - $$PWD/digestmd5proplist.cpp \ - $$PWD/digestmd5response.cpp \ - $$PWD/scramsha1message.cpp \ - $$PWD/scramsha1response.cpp \ - $$PWD/scramsha1signature.cpp diff --git a/src/xmpp/sasl/scramsha1message.cpp b/src/xmpp/sasl/scramsha1message.cpp index 12559b73..023d0986 100644 --- a/src/xmpp/sasl/scramsha1message.cpp +++ b/src/xmpp/sasl/scramsha1message.cpp @@ -18,17 +18,17 @@ #include "xmpp/sasl/scramsha1message.h" +#include "xmpp/jid/jid.h" + +#include +#include #include #include #include -#include - -#include "xmpp/base/randomnumbergenerator.h" -#include "xmpp/jid/jid.h" namespace XMPP { - -bool Normalize(const QString &username_in, QString &username_out ) { +bool Normalize(const QString &username_in, QString &username_out) +{ // SASLprep if (StringPrepCache::saslprep(username_in, 1024, username_out)) { // '=' -> '=3D' and ',' -> '=2C' @@ -40,11 +40,12 @@ bool Normalize(const QString &username_in, QString &username_out ) { } } -SCRAMSHA1Message::SCRAMSHA1Message(const QString& authzid, const QString& authcid, const QByteArray& cnonce, const RandomNumberGenerator& rand) : isValid_(true) +SCRAMSHA1Message::SCRAMSHA1Message(const QString &authzid, const QString &authcid, const QByteArray &cnonce) : + isValid_(true) { - QString result; + QString result; QByteArray clientnonce; - QString username; + QString username; if (!Normalize(authcid, username)) { isValid_ = false; @@ -55,11 +56,12 @@ SCRAMSHA1Message::SCRAMSHA1Message(const QString& authzid, const QString& authci // make a cnonce QByteArray a; a.resize(32); - for(int n = 0; n < (int)a.size(); ++n) { - a[n] = (char) rand.generateNumberBetween(0, 255); + for (int n = 0; n < (int)a.size(); ++n) { + a[n] = (char)QRandomGenerator::global()->bounded(0, 256); } clientnonce = a.toBase64(); - } else clientnonce = cnonce; + } else + clientnonce = cnonce; QTextStream(&result) << "n,"; if (authzid.size() > 0) { @@ -68,5 +70,4 @@ SCRAMSHA1Message::SCRAMSHA1Message(const QString& authzid, const QString& authci QTextStream(&result) << ",n=" << username << ",r=" << clientnonce; value_ = result.toUtf8(); } - -} +} // namespace XMPP diff --git a/src/xmpp/sasl/scramsha1message.h b/src/xmpp/sasl/scramsha1message.h index cad4f790..d3260dcf 100644 --- a/src/xmpp/sasl/scramsha1message.h +++ b/src/xmpp/sasl/scramsha1message.h @@ -22,26 +22,19 @@ #include #include -#include "xmpp/base/randomnumbergenerator.h" - namespace XMPP { - class SCRAMSHA1Message - { - public: - SCRAMSHA1Message(const QString& authzid, const QString& authcid, const QByteArray& cnonce, const RandomNumberGenerator& rand); +class SCRAMSHA1Message { +public: + SCRAMSHA1Message(const QString &authzid, const QString &authcid, const QByteArray &cnonce); - const QByteArray& getValue() { - return value_; - } + const QByteArray &getValue() { return value_; } - bool isValid() const { - return isValid_; - } + bool isValid() const { return isValid_; } - private: - QByteArray value_; - bool isValid_; - }; -} +private: + QByteArray value_; + bool isValid_; +}; +} // namespace XMPP -#endif +#endif // SCRAMSHA1MESSAGE_H diff --git a/src/xmpp/sasl/scramsha1response.cpp b/src/xmpp/sasl/scramsha1response.cpp index 210ae63b..22f92a31 100644 --- a/src/xmpp/sasl/scramsha1response.cpp +++ b/src/xmpp/sasl/scramsha1response.cpp @@ -18,117 +18,123 @@ #include "xmpp/sasl/scramsha1response.h" +#include "xmpp/jid/jid.h" + #include +#include #include #include #include #include -#include - -#include "xmpp/base/randomnumbergenerator.h" -#include "xmpp/jid/jid.h" namespace XMPP { - QCA::SecureArray HMAC_SHA_1(const QCA::SecureArray &key, const QCA::SecureArray &str) { - QCA::SecureArray result = QCA::MessageAuthenticationCode("hmac(sha1)", key).process(str); - return result; - } +QCA::SecureArray HMAC_SHA_1(const QCA::SecureArray &key, const QCA::SecureArray &str) +{ + QCA::SecureArray result = QCA::MessageAuthenticationCode("hmac(sha1)", key).process(str); + return result; +} - SCRAMSHA1Response::SCRAMSHA1Response(const QByteArray& server_first_message, const QByteArray& password_in, const QByteArray& client_first_message, const QString &salted_password_base64, const RandomNumberGenerator& rand) +SCRAMSHA1Response::SCRAMSHA1Response(const QByteArray &server_first_message, const QByteArray &password_in, + const QByteArray &client_first_message, const QString &salted_password_base64) { Q_UNUSED(rand); QString pass_in = QString::fromUtf8(password_in); QString pass_out; - QRegExp pattern("r=(.*),s=(.+),i=(\\d+)"); - int pos = pattern.indexIn(QString(server_first_message)); - isValid_ = true; - if (pos > -1) { - QString clientservernonce = pattern.cap(1); - QString salt = pattern.cap(2); - QString icount = pattern.cap(3); - - unsigned int dkLen; + QRegularExpression pattern("r=(.*),s=(.+),i=(\\d+)"); + auto match = pattern.match(QString(server_first_message)); + isValid_ = match.hasMatch(); + if (!isValid_) { + qWarning("SASL/SCRAM-SHA-1: Failed to match pattern for server-final-message."); + return; + } + QCA::PBKDF2 hi("sha1"); + if (!hi.context()) { + qWarning("SASL/SCRAM-SHA-1: sha1 is not supported by qca."); + isValid_ = false; + return; + } - QCA::Hash shaHash("sha1"); - shaHash.update("", 0); - dkLen = shaHash.final().size(); + QString clientservernonce = match.captured(1); + QString salt = match.captured(2); + QString icount = match.captured(3); - QCA::PBKDF2 hi("sha1"); + unsigned int dkLen; - QByteArray password; + QCA::Hash shaHash("sha1"); + shaHash.update("", 0); + dkLen = shaHash.final().size(); - // SaltedPassword := Hi(Normalize(password), salt, i) - if (salted_password_base64.size() > 0) - salted_password_ = QCA::SymmetricKey(QCA::SecureArray(QCA::Base64().stringToArray(salted_password_base64.toUtf8()))); - if (salted_password_.size() == 0) { - if (!StringPrepCache::saslprep(pass_in, 1023, pass_out)) { - isValid_ = false; - return; - } + QByteArray password; - password = pass_out.toUtf8(); - salted_password_ = hi.makeKey(QCA::SecureArray(password), QCA::InitializationVector(QCA::Base64().stringToArray(salt)), dkLen, icount.toULong()); + // SaltedPassword := Hi(Normalize(password), salt, i) + if (salted_password_base64.size() > 0) + salted_password_ + = QCA::SymmetricKey(QCA::SecureArray(QCA::Base64().stringToArray(salted_password_base64.toUtf8()))); + if (salted_password_.size() == 0) { + if (!StringPrepCache::saslprep(pass_in, 1023, pass_out)) { + isValid_ = false; + return; } - // ClientKey := HMAC(SaltedPassword, "Client Key") - QCA::SecureArray client_key(HMAC_SHA_1(salted_password_.toByteArray(), QByteArray("Client Key"))); - - // StoredKey := H(ClientKey) - QCA::SecureArray stored_key = QCA::Hash("sha1").process(client_key); + password = pass_out.toUtf8(); + salted_password_ + = hi.makeKey(QCA::SecureArray(password), QCA::InitializationVector(QCA::Base64().stringToArray(salt)), + dkLen, icount.toULong()); + } - // assemble client-final-message-without-proof + // ClientKey := HMAC(SaltedPassword, "Client Key") + QCA::SecureArray client_key(HMAC_SHA_1(salted_password_.toByteArray(), QByteArray("Client Key"))); - QString gs2_header; - { - QRegExp pattern("(.+)n=.+"); - pattern.indexIn(QString(client_first_message)); - gs2_header = pattern.cap(1); - } + // StoredKey := H(ClientKey) + QCA::SecureArray stored_key = QCA::Hash("sha1").process(client_key); - QString client_final_message; - QTextStream final_message_stream(&client_final_message); - final_message_stream << "c=" << QCA::Base64().arrayToString((gs2_header.toUtf8())); - final_message_stream << ",r=" << clientservernonce; + // assemble client-final-message-without-proof - // AuthMessage := client-first-message-bare + "," + server-first-message + "," + client-final-message-without-proof - QRegExp extract_cfmb_pattern("(n=.+)"); - if (extract_cfmb_pattern.indexIn(QString(client_first_message)) < 0) { - isValid_ = false; - return; - } + QString gs2_header; + { + QRegularExpression pattern("(.+)n=.+"); + gs2_header = pattern.match(QString(client_first_message)).captured(1); + } - QString client_first_message_bare = extract_cfmb_pattern.cap(1); + QString client_final_message; + QTextStream final_message_stream(&client_final_message); + final_message_stream << "c=" << QCA::Base64().arrayToString((gs2_header.toUtf8())); + final_message_stream << ",r=" << clientservernonce; + + // AuthMessage := client-first-message-bare + "," + server-first-message + "," + + // client-final-message-without-proof + QRegularExpression extract_cfmb_pattern("(n=.+)"); + match = extract_cfmb_pattern.match(QString(client_first_message)); + isValid_ = match.hasMatch(); + if (!isValid_) { + return; + } - QCA::SecureArray auth_message = QCA::SecureArray(client_first_message_bare.toUtf8()); - auth_message += QCA::SecureArray(",") + QCA::SecureArray(server_first_message); - auth_message += QCA::SecureArray(",") + QCA::SecureArray(client_final_message.toUtf8()); + QString client_first_message_bare = match.captured(1); - // ClientSignature := HMAC(StoredKey, AuthMessage) - QCA::SecureArray client_signature = HMAC_SHA_1(stored_key, auth_message); + QCA::SecureArray auth_message = QCA::SecureArray(client_first_message_bare.toUtf8()); + auth_message += QCA::SecureArray(",") + QCA::SecureArray(server_first_message); + auth_message += QCA::SecureArray(",") + QCA::SecureArray(client_final_message.toUtf8()); - // ClientProof := ClientKey XOR ClientSignature - QCA::SecureArray client_proof(client_key.size()); - for(int i = 0; i < client_proof.size(); ++i) { - client_proof[i] = client_key[i] ^ client_signature[i]; - } + // ClientSignature := HMAC(StoredKey, AuthMessage) + QCA::SecureArray client_signature = HMAC_SHA_1(stored_key, auth_message); - // ServerKey := HMAC(SaltedPassword, "Server Key") - QCA::SecureArray server_key = HMAC_SHA_1(salted_password_, QByteArray("Server Key")); + // ClientProof := ClientKey XOR ClientSignature + QCA::SecureArray client_proof(client_key.size()); + for (int i = 0; i < client_proof.size(); ++i) { + client_proof[i] = client_key[i] ^ client_signature[i]; + } - // ServerSignature := HMAC(ServerKey, AuthMessage) - server_signature_ = HMAC_SHA_1(server_key, auth_message); + // ServerKey := HMAC(SaltedPassword, "Server Key") + QCA::SecureArray server_key = HMAC_SHA_1(salted_password_, QByteArray("Server Key")); - final_message_stream << ",p=" << QCA::Base64().arrayToString(client_proof); - value_ = client_final_message.toUtf8(); - } else { - qWarning("SASL/SCRAM-SHA-1: Failed to match pattern for server-final-message."); - isValid_ = false; - } -} + // ServerSignature := HMAC(ServerKey, AuthMessage) + server_signature_ = HMAC_SHA_1(server_key, auth_message); -const QString SCRAMSHA1Response::getSaltedPassword() { - return QCA::Base64().arrayToString(salted_password_); + final_message_stream << ",p=" << QCA::Base64().arrayToString(client_proof); + value_ = client_final_message.toUtf8(); } -} +const QString SCRAMSHA1Response::getSaltedPassword() { return QCA::Base64().arrayToString(salted_password_); } +} // namespace XMPP diff --git a/src/xmpp/sasl/scramsha1response.h b/src/xmpp/sasl/scramsha1response.h index 93a141cc..c791c14a 100644 --- a/src/xmpp/sasl/scramsha1response.h +++ b/src/xmpp/sasl/scramsha1response.h @@ -24,38 +24,26 @@ #include namespace XMPP { - class RandomNumberGenerator; - - class SCRAMSHA1Response - { - public: - SCRAMSHA1Response( - const QByteArray& server_first_message, - const QByteArray& password, - const QByteArray& client_first_message, - const QString &salted_password_base64, - const RandomNumberGenerator& rand); - - const QByteArray& getValue() const { - return value_; - } - - const QCA::SecureArray getServerSignature() const { - return server_signature_; - } - - const QString getSaltedPassword(); - - bool isValid() const { - return isValid_; - } - - private: - bool isValid_; - QByteArray value_; - QCA::SecureArray server_signature_; - QCA::SymmetricKey salted_password_; - }; -} - -#endif + +class SCRAMSHA1Response { +public: + SCRAMSHA1Response(const QByteArray &server_first_message, const QByteArray &password, + const QByteArray &client_first_message, const QString &salted_password_base64); + + const QByteArray &getValue() const { return value_; } + + const QCA::SecureArray getServerSignature() const { return server_signature_; } + + const QString getSaltedPassword(); + + bool isValid() const { return isValid_; } + +private: + bool isValid_; + QByteArray value_; + QCA::SecureArray server_signature_; + QCA::SymmetricKey salted_password_; +}; +} // namespace XMPP + +#endif // SCRAMSHA1RESPONSE_H diff --git a/src/xmpp/sasl/scramsha1signature.cpp b/src/xmpp/sasl/scramsha1signature.cpp index 70240c23..1c9c71c5 100644 --- a/src/xmpp/sasl/scramsha1signature.cpp +++ b/src/xmpp/sasl/scramsha1signature.cpp @@ -19,29 +19,26 @@ #include "xmpp/sasl/scramsha1signature.h" #include +#include #include #include #include #include -#include - -#include "xmpp/base/randomnumbergenerator.h" namespace XMPP { - - SCRAMSHA1Signature::SCRAMSHA1Signature(const QByteArray &server_final_message, const QCA::SecureArray &server_signature_should) +SCRAMSHA1Signature::SCRAMSHA1Signature(const QByteArray &server_final_message, + const QCA::SecureArray &server_signature_should) { - QRegExp pattern("v=([^,]*)"); - int pos = pattern.indexIn(QString(server_final_message)); - isValid_ = true; - if (pos > -1) { - QString server_signature = pattern.cap(1); + QRegularExpression pattern("v=([^,]*)"); + auto match = pattern.match(QString(server_final_message)); + isValid_ = match.hasMatch(); + if (isValid_) { + QString server_signature = match.captured(1); QCA::SecureArray server_sig(QCA::Base64().stringToArray(server_signature)); - if (server_sig != server_signature_should) isValid_ = false; + if (server_sig != server_signature_should) + isValid_ = false; } else { qWarning("SASL/SCRAM-SHA-1: Failed to match pattern for server-final-message."); - isValid_ = false; } } - -} +} // namespace XMPP diff --git a/src/xmpp/sasl/scramsha1signature.h b/src/xmpp/sasl/scramsha1signature.h index 81969ca1..27990e34 100644 --- a/src/xmpp/sasl/scramsha1signature.h +++ b/src/xmpp/sasl/scramsha1signature.h @@ -24,18 +24,15 @@ #include namespace XMPP { - class SCRAMSHA1Signature - { - public: - SCRAMSHA1Signature(const QByteArray &server_final_message, const QCA::SecureArray &server_signature_should); +class SCRAMSHA1Signature { +public: + SCRAMSHA1Signature(const QByteArray &server_final_message, const QCA::SecureArray &server_signature_should); - bool isValid() const { - return isValid_; - } + bool isValid() const { return isValid_; } - private: - bool isValid_; - }; -} +private: + bool isValid_; +}; +} // namespace XMPP -#endif +#endif // SCRAMSHA1SIGNATURE_H diff --git a/src/xmpp/sasl/unittest/digestmd5responsetest.cpp b/src/xmpp/sasl/unittest/digestmd5responsetest.cpp index 825b21d9..af731af6 100644 --- a/src/xmpp/sasl/unittest/digestmd5responsetest.cpp +++ b/src/xmpp/sasl/unittest/digestmd5responsetest.cpp @@ -16,64 +16,58 @@ * */ +#include "xmpp/sasl/digestmd5response.h" +#include "qttestutil/qttestutil.h" + #include -#include #include +#include -#include "qttestutil/qttestutil.h" -#include "xmpp/sasl/digestmd5response.h" -#include "xmpp/base/unittest/incrementingrandomnumbergenerator.h" +// TODO fix test adfter random generator removal using namespace XMPP; -class DIGESTMD5ResponseTest : public QObject -{ - Q_OBJECT +class DIGESTMD5ResponseTest : public QObject { + Q_OBJECT - private slots: - void testConstructor_WithAuthzid() { - DIGESTMD5Response response( - "realm=\"example.com\"," - "nonce=\"O6skKPuaCZEny3hteI19qXMBXSadoWs840MchORo\"," - "qop=\"auth\",charset=\"utf-8\",algorithm=\"md5-sess\"", - "xmpp", "jabber.example.com", "example.com", "myuser", "myuser_authz", - "mypass", - IncrementingRandomNumberGenerator(255)); - QByteArray expectedValue( - "username=\"myuser\",realm=\"example.com\"," - "nonce=\"O6skKPuaCZEny3hteI19qXMBXSadoWs840MchORo\"," - "cnonce=\"AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8=\"," - "nc=00000001," - "digest-uri=\"xmpp/jabber.example.com\"," - "qop=auth,response=8fe15bc2aa31956b62d9de831b21a5d4," - "charset=utf-8,authzid=\"myuser_authz\""); +private slots: + void testConstructor_WithAuthzid() + { + DIGESTMD5Response response("realm=\"example.com\"," + "nonce=\"O6skKPuaCZEny3hteI19qXMBXSadoWs840MchORo\"," + "qop=\"auth\",charset=\"utf-8\",algorithm=\"md5-sess\"", + "xmpp", "jabber.example.com", "example.com", "myuser", "myuser_authz", "mypass"); + QByteArray expectedValue("username=\"myuser\",realm=\"example.com\"," + "nonce=\"O6skKPuaCZEny3hteI19qXMBXSadoWs840MchORo\"," + "cnonce=\"AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8=\"," + "nc=00000001," + "digest-uri=\"xmpp/jabber.example.com\"," + "qop=auth,response=8fe15bc2aa31956b62d9de831b21a5d4," + "charset=utf-8,authzid=\"myuser_authz\""); - QVERIFY(response.isValid()); - QCOMPARE(response.getValue(), expectedValue); - } + QVERIFY(response.isValid()); + QCOMPARE(response.getValue(), expectedValue); + } - void testConstructor_WithoutAuthzid() { - DIGESTMD5Response response( - "realm=\"example.com\"," - "nonce=\"O6skKPuaCZEny3hteI19qXMBXSadoWs840MchORo\"," - "qop=\"auth\",charset=\"utf-8\",algorithm=\"md5-sess\"", - "xmpp", "jabber.example.com", "example.com", "myuser", "", - "mypass", - IncrementingRandomNumberGenerator(255)); - QByteArray expectedValue( - "username=\"myuser\",realm=\"example.com\"," - "nonce=\"O6skKPuaCZEny3hteI19qXMBXSadoWs840MchORo\"," - "cnonce=\"AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8=\"," - "nc=00000001," - "digest-uri=\"xmpp/jabber.example.com\"," - "qop=auth,response=564b1c1cc16d97b019f18b14c979964b,charset=utf-8"); + void testConstructor_WithoutAuthzid() + { + DIGESTMD5Response response("realm=\"example.com\"," + "nonce=\"O6skKPuaCZEny3hteI19qXMBXSadoWs840MchORo\"," + "qop=\"auth\",charset=\"utf-8\",algorithm=\"md5-sess\"", + "xmpp", "jabber.example.com", "example.com", "myuser", "", "mypass"); + QByteArray expectedValue("username=\"myuser\",realm=\"example.com\"," + "nonce=\"O6skKPuaCZEny3hteI19qXMBXSadoWs840MchORo\"," + "cnonce=\"AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8=\"," + "nc=00000001," + "digest-uri=\"xmpp/jabber.example.com\"," + "qop=auth,response=564b1c1cc16d97b019f18b14c979964b,charset=utf-8"); - QVERIFY(response.isValid()); - QCOMPARE(response.getValue(), expectedValue); - } + QVERIFY(response.isValid()); + QCOMPARE(response.getValue(), expectedValue); + } - private: - QCA::Initializer initializer; +private: + QCA::Initializer initializer; }; QTTESTUTIL_REGISTER_TEST(DIGESTMD5ResponseTest); diff --git a/src/xmpp/sasl/unittest/plainmessagetest.cpp b/src/xmpp/sasl/unittest/plainmessagetest.cpp index ec2a66c1..4ee25e3b 100644 --- a/src/xmpp/sasl/unittest/plainmessagetest.cpp +++ b/src/xmpp/sasl/unittest/plainmessagetest.cpp @@ -16,33 +16,36 @@ * */ -#include -#include - #include "xmpp/sasl/plainmessage.h" #include "qttestutil/qttestutil.h" +#include +#include + using namespace XMPP; -class PlainMessageTest : public QObject -{ - Q_OBJECT - - private slots: - void testConstructor_WithoutAuthzID() { - PLAINMessage message("", QString("user"), "pass"); - QCOMPARE(message.getValue(), QByteArray("\0user\0pass", 10)); - } - - void testConstructor_WithAuthzID() { - PLAINMessage message(QString("authz"), QString("user"), "pass"); - QCOMPARE(message.getValue(), QByteArray("authz\0user\0pass", 15)); - } - - void testConstructor_WithNonASCIICharacters() { - PLAINMessage message(QString("authz") + QChar(0x03A8) /* psi */, QString("user") + QChar(0x03A8) /* psi */, "pass"); - QCOMPARE(message.getValue(), QByteArray("authz\xCE\xA8\0user\xCE\xA8\0pass", 19)); - } +class PlainMessageTest : public QObject { + Q_OBJECT + +private slots: + void testConstructor_WithoutAuthzID() + { + PLAINMessage message("", QString("user"), "pass"); + QCOMPARE(message.getValue(), QByteArray("\0user\0pass", 10)); + } + + void testConstructor_WithAuthzID() + { + PLAINMessage message(QString("authz"), QString("user"), "pass"); + QCOMPARE(message.getValue(), QByteArray("authz\0user\0pass", 15)); + } + + void testConstructor_WithNonASCIICharacters() + { + PLAINMessage message(QString("authz") + QChar(0x03A8) /* psi */, QString("user") + QChar(0x03A8) /* psi */, + "pass"); + QCOMPARE(message.getValue(), QByteArray("authz\xCE\xA8\0user\xCE\xA8\0pass", 19)); + } }; QTTESTUTIL_REGISTER_TEST(PlainMessageTest); diff --git a/src/xmpp/sasl/unittest/scramsha1messagetest.cpp b/src/xmpp/sasl/unittest/scramsha1messagetest.cpp index 24d3a36a..26582c73 100644 --- a/src/xmpp/sasl/unittest/scramsha1messagetest.cpp +++ b/src/xmpp/sasl/unittest/scramsha1messagetest.cpp @@ -16,38 +16,38 @@ * */ +#include "xmpp/sasl/scramsha1message.h" +#include "qttestutil/qttestutil.h" + #include -#include #include +#include -#include "qttestutil/qttestutil.h" -#include "xmpp/base/unittest/incrementingrandomnumbergenerator.h" -#include "xmpp/sasl/scramsha1message.h" +// TODO fix test adfter random generator removal using namespace XMPP; -class SCRAMSHA1MessageTest : public QObject -{ - Q_OBJECT +class SCRAMSHA1MessageTest : public QObject { + Q_OBJECT - private slots: - void testConstructor_WithAuthzid() { - } +private slots: + void testConstructor_WithAuthzid() { } - void testConstructor_WithoutAuthzid() { - SCRAMSHA1Message msg1("", "testuser", QByteArray(0, ' '), IncrementingRandomNumberGenerator(255)); - QByteArray msg1_good("n,,n=testuser,r=AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8="); - QVERIFY(msg1.isValid()); - QCOMPARE(msg1.getValue(), msg1_good); + void testConstructor_WithoutAuthzid() + { + SCRAMSHA1Message msg1("", "testuser", QByteArray(0, ' ')); + QByteArray msg1_good("n,,n=testuser,r=AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8="); + QVERIFY(msg1.isValid()); + QCOMPARE(msg1.getValue(), msg1_good); - SCRAMSHA1Message msg2("", "username=test,man", QByteArray(0, ' '), IncrementingRandomNumberGenerator(255)); - QByteArray msg2_good("n,,n=username=3Dtest=2Cman,r=AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8="); - QVERIFY(msg2.isValid()); - QCOMPARE(msg2.getValue(), msg2_good); - } + SCRAMSHA1Message msg2("", "username=test,man", QByteArray(0, ' ')); + QByteArray msg2_good("n,,n=username=3Dtest=2Cman,r=AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8="); + QVERIFY(msg2.isValid()); + QCOMPARE(msg2.getValue(), msg2_good); + } - private: - QCA::Initializer initializer; +private: + QCA::Initializer initializer; }; QTTESTUTIL_REGISTER_TEST(SCRAMSHA1MessageTest); diff --git a/src/xmpp/sasl/unittest/scramsha1responsetest.cpp b/src/xmpp/sasl/unittest/scramsha1responsetest.cpp index 132302aa..7835fb52 100644 --- a/src/xmpp/sasl/unittest/scramsha1responsetest.cpp +++ b/src/xmpp/sasl/unittest/scramsha1responsetest.cpp @@ -16,43 +16,42 @@ * */ -#include -#include -#include - -#include "qttestutil/qttestutil.h" #include "xmpp/sasl/scramsha1response.h" +#include "qttestutil/qttestutil.h" #include "xmpp/sasl/scramsha1signature.h" -#include "xmpp/base/unittest/incrementingrandomnumbergenerator.h" -using namespace XMPP; - -class SCRAMSHA1ResponseTest : public QObject -{ - Q_OBJECT +#include +#include +#include - private slots: - void testConstructor_WithAuthzid() { +// TODO fix test adfter random generator removal - } +using namespace XMPP; - void testConstructor_WithoutAuthzid() { - if (QCA::isSupported("hmac(sha1)")) { - SCRAMSHA1Response resp1("r=fyko+d2lbbFgONRv9qkxdawL3rfcNHYJY1ZVvWVs7j,s=QSXCR+Q6sek8bf92,i=4096", - "pencil", "n,,n=user,r=fyko+d2lbbFgONRv9qkxdawL", "", IncrementingRandomNumberGenerator(255)); - const QCA::SecureArray sig = resp1.getServerSignature(); - QByteArray resp_sig("v=rmF9pqV8S7suAoZWja4dJRkFsKQ="); - SCRAMSHA1Signature sig1(resp_sig, sig); - QByteArray resp1_ok("c=biws,r=fyko+d2lbbFgONRv9qkxdawL3rfcNHYJY1ZVvWVs7j,p=v0X8v3Bz2T0CJGbJQyF0X+HI4Ts="); - QCOMPARE(resp1.getValue(), resp1_ok); - QVERIFY(sig1.isValid()); - } else { - QFAIL("hmac(sha1) not supported in QCA."); - } +class SCRAMSHA1ResponseTest : public QObject { + Q_OBJECT + +private slots: + void testConstructor_WithAuthzid() { } + + void testConstructor_WithoutAuthzid() + { + if (QCA::isSupported("hmac(sha1)")) { + SCRAMSHA1Response resp1("r=fyko+d2lbbFgONRv9qkxdawL3rfcNHYJY1ZVvWVs7j,s=QSXCR+Q6sek8bf92,i=4096", "pencil", + "n,,n=user,r=fyko+d2lbbFgONRv9qkxdawL", ""); + const QCA::SecureArray sig = resp1.getServerSignature(); + QByteArray resp_sig("v=rmF9pqV8S7suAoZWja4dJRkFsKQ="); + SCRAMSHA1Signature sig1(resp_sig, sig); + QByteArray resp1_ok("c=biws,r=fyko+d2lbbFgONRv9qkxdawL3rfcNHYJY1ZVvWVs7j,p=v0X8v3Bz2T0CJGbJQyF0X+HI4Ts="); + QCOMPARE(resp1.getValue(), resp1_ok); + QVERIFY(sig1.isValid()); + } else { + QFAIL("hmac(sha1) not supported in QCA."); } + } - private: - QCA::Initializer initializer; +private: + QCA::Initializer initializer; }; QTTESTUTIL_REGISTER_TEST(SCRAMSHA1ResponseTest); diff --git a/src/xmpp/sasl/unittest/unittest.pri b/src/xmpp/sasl/unittest/unittest.pri deleted file mode 100644 index a4e78d02..00000000 --- a/src/xmpp/sasl/unittest/unittest.pri +++ /dev/null @@ -1,5 +0,0 @@ -SOURCES += \ - $$PWD/plainmessagetest.cpp \ - $$PWD/digestmd5responsetest.cpp \ - $$PWD/scramsha1messagetest.cpp \ - $$PWD/scramsha1responsetest.cpp diff --git a/src/xmpp/sasl/unittest/unittest.pro b/src/xmpp/sasl/unittest/unittest.pro deleted file mode 100644 index 055d8665..00000000 --- a/src/xmpp/sasl/unittest/unittest.pro +++ /dev/null @@ -1,12 +0,0 @@ -include(../../modules.pri) -include($$IRIS_XMPP_QA_UNITTEST_MODULE) -include($$IRIS_XMPP_SASL_MODULE) -include($$IRIS_XMPP_BASE_MODULE) -include($$IRIS_XMPP_BASE64_MODULE) -include($$IRIS_XMPP_JID_MODULE) -include(unittest.pri) - -# QCA stuff. -# TODO: Remove the dependency on QCA from this module -include(../../../../../third-party/qca/qca.pri) -include(../../../../../third-party/qca/qca-ossl.pri) diff --git a/src/xmpp/xmpp-core/compressionhandler.cpp b/src/xmpp/xmpp-core/compressionhandler.cpp index 89fd9f1d..56dd078d 100644 --- a/src/xmpp/xmpp-core/compressionhandler.cpp +++ b/src/xmpp/xmpp-core/compressionhandler.cpp @@ -1,12 +1,11 @@ -#include -#include - #include "compressionhandler.h" #include "xmpp/zlib/zlibcompressor.h" #include "xmpp/zlib/zlibdecompressor.h" -CompressionHandler::CompressionHandler() - : errorCode_(0) +#include +#include + +CompressionHandler::CompressionHandler() : errorCode_(0) { outgoing_buffer_.open(QIODevice::ReadWrite); compressor_ = new ZLibCompressor(&outgoing_buffer_); @@ -21,20 +20,20 @@ CompressionHandler::~CompressionHandler() delete decompressor_; } -void CompressionHandler::writeIncoming(const QByteArray& a) +void CompressionHandler::writeIncoming(const QByteArray &a) { - //qDebug("CompressionHandler::writeIncoming"); - //qDebug() << QString("Incoming %1 bytes").arg(a.size()); - errorCode_ = decompressor_->write(a); + // qDebug("CompressionHandler::writeIncoming"); + // qDebug() << QString("Incoming %1 bytes").arg(a.size()); + errorCode_ = int(decompressor_->write(a)); if (!errorCode_) QTimer::singleShot(0, this, SIGNAL(readyRead())); else QTimer::singleShot(0, this, SIGNAL(error())); } -void CompressionHandler::write(const QByteArray& a) +void CompressionHandler::write(const QByteArray &a) { - //qDebug() << QString("CompressionHandler::write(%1)").arg(a.size()); + // qDebug() << QString("CompressionHandler::write(%1)").arg(a.size()); errorCode_ = compressor_->write(a); if (!errorCode_) QTimer::singleShot(0, this, SIGNAL(readyReadOutgoing())); @@ -44,17 +43,17 @@ void CompressionHandler::write(const QByteArray& a) QByteArray CompressionHandler::read() { - //qDebug("CompressionHandler::read"); + // qDebug("CompressionHandler::read"); QByteArray b = incoming_buffer_.buffer(); incoming_buffer_.buffer().clear(); incoming_buffer_.reset(); return b; } -QByteArray CompressionHandler::readOutgoing(int* i) +QByteArray CompressionHandler::readOutgoing(int *i) { - //qDebug("CompressionHandler::readOutgoing"); - //qDebug() << QString("Outgoing %1 bytes").arg(outgoing_buffer_.size()); + // qDebug("CompressionHandler::readOutgoing"); + // qDebug() << QString("Outgoing %1 bytes").arg(outgoing_buffer_.size()); QByteArray b = outgoing_buffer_.buffer(); outgoing_buffer_.buffer().clear(); outgoing_buffer_.reset(); @@ -62,7 +61,4 @@ QByteArray CompressionHandler::readOutgoing(int* i) return b; } -int CompressionHandler::errorCode() -{ - return errorCode_; -} +int CompressionHandler::errorCode() { return errorCode_; } diff --git a/src/xmpp/xmpp-core/compressionhandler.h b/src/xmpp/xmpp-core/compressionhandler.h index d336af47..3f3479fd 100644 --- a/src/xmpp/xmpp-core/compressionhandler.h +++ b/src/xmpp/xmpp-core/compressionhandler.h @@ -1,24 +1,23 @@ #ifndef COMPRESSIONHANDLER_H #define COMPRESSIONHANDLER_H -#include #include +#include class ZLibCompressor; class ZLibDecompressor; -class CompressionHandler : public QObject -{ +class CompressionHandler : public QObject { Q_OBJECT public: CompressionHandler(); ~CompressionHandler(); - void writeIncoming(const QByteArray& a); - void write(const QByteArray& a); + void writeIncoming(const QByteArray &a); + void write(const QByteArray &a); QByteArray read(); - QByteArray readOutgoing(int*); - int errorCode(); + QByteArray readOutgoing(int *); + int errorCode(); signals: void readyRead(); @@ -26,10 +25,10 @@ class CompressionHandler : public QObject void error(); private: - ZLibCompressor* compressor_; - ZLibDecompressor* decompressor_; - QBuffer outgoing_buffer_, incoming_buffer_; - int errorCode_; + ZLibCompressor *compressor_; + ZLibDecompressor *decompressor_; + QBuffer outgoing_buffer_, incoming_buffer_; + int errorCode_; }; -#endif +#endif // COMPRESSIONHANDLER_H diff --git a/src/xmpp/xmpp-core/connector.cpp b/src/xmpp/xmpp-core/connector.cpp index 6bbce914..f73e8bd5 100644 --- a/src/xmpp/xmpp-core/connector.cpp +++ b/src/xmpp/xmpp-core/connector.cpp @@ -28,149 +28,104 @@ greatly simplify this class. - Sep 3rd, 2003. */ -#include "xmpp.h" - -#include -#include -#include -#include -#include - #include "bsocket.h" #include "httpconnect.h" #include "httppoll.h" #include "socks.h" +#include "xmpp.h" -//#define XMPP_DEBUG +#include +#include +#include +#include +#include +#include +// #define XMPP_DEBUG #ifdef XMPP_DEBUG -# define XDEBUG (qDebug() << this << "#" << __FUNCTION__ << ":") +#define XDEBUG (qDebug() << this << "#" << __FUNCTION__ << ":") #endif using namespace XMPP; -static const int XMPP_DEFAULT_PORT = 5222; -static const int XMPP_LEGACY_PORT = 5223; -static const char* XMPP_CLIENT_SRV = "xmpp-client"; -static const char* XMPP_CLIENT_TRANSPORT = "tcp"; - +static const int XMPP_DEFAULT_PORT = 5222; +static const int XMPP_LEGACY_PORT = 5223; +static const char *XMPP_CLIENT_SRV = "xmpp-client"; +static const char *XMPP_CLIENT_TRANSPORT = "tcp"; //---------------------------------------------------------------------------- // Connector //---------------------------------------------------------------------------- -Connector::Connector(QObject *parent) -:QObject(parent) +Connector::Connector(QObject *parent) : QObject(parent) { setUseSSL(false); setPeerAddressNone(); } -Connector::~Connector() -{ -} +Connector::~Connector() { } -bool Connector::useSSL() const -{ - return ssl; -} +bool Connector::useSSL() const { return ssl; } -bool Connector::havePeerAddress() const -{ - return haveaddr; -} +bool Connector::havePeerAddress() const { return haveaddr; } -QHostAddress Connector::peerAddress() const -{ - return addr; -} +QHostAddress Connector::peerAddress() const { return addr; } -quint16 Connector::peerPort() const -{ - return port; -} +quint16 Connector::peerPort() const { return port; } -void Connector::setUseSSL(bool b) -{ - ssl = b; -} +void Connector::setUseSSL(bool b) { ssl = b; } void Connector::setPeerAddressNone() { haveaddr = false; - addr = QHostAddress(); - port = 0; + addr = QHostAddress(); + port = 0; } void Connector::setPeerAddress(const QHostAddress &_addr, quint16 _port) { haveaddr = true; - addr = _addr; - port = _port; -} - -QString Connector::host() const -{ - return QString(); + addr = _addr; + port = _port; } +QString Connector::host() const { return QString(); } //---------------------------------------------------------------------------- // AdvancedConnector::Proxy //---------------------------------------------------------------------------- -int AdvancedConnector::Proxy::type() const -{ - return t; -} +int AdvancedConnector::Proxy::type() const { return t; } -QString AdvancedConnector::Proxy::host() const -{ - return v_host; -} +QString AdvancedConnector::Proxy::host() const { return v_host; } -quint16 AdvancedConnector::Proxy::port() const -{ - return v_port; -} +quint16 AdvancedConnector::Proxy::port() const { return v_port; } -QUrl AdvancedConnector::Proxy::url() const -{ - return v_url; -} +QUrl AdvancedConnector::Proxy::url() const { return v_url; } -QString AdvancedConnector::Proxy::user() const -{ - return v_user; -} +QString AdvancedConnector::Proxy::user() const { return v_user; } -QString AdvancedConnector::Proxy::pass() const -{ - return v_pass; -} +QString AdvancedConnector::Proxy::pass() const { return v_pass; } -int AdvancedConnector::Proxy::pollInterval() const -{ - return v_poll; -} +int AdvancedConnector::Proxy::pollInterval() const { return v_poll; } void AdvancedConnector::Proxy::setHttpConnect(const QString &host, quint16 port) { - t = HttpConnect; + t = HttpConnect; v_host = host; v_port = port; } void AdvancedConnector::Proxy::setHttpPoll(const QString &host, quint16 port, const QUrl &url) { - t = HttpPoll; + t = HttpPoll; v_host = host; v_port = port; - v_url = url; + v_url = url; } void AdvancedConnector::Proxy::setSocks(const QString &host, quint16 port) { - t = Socks; + t = Socks; v_host = host; v_port = port; } @@ -181,41 +136,41 @@ void AdvancedConnector::Proxy::setUserPass(const QString &user, const QString &p v_pass = pass; } -void AdvancedConnector::Proxy::setPollInterval(int secs) +void AdvancedConnector::Proxy::setPollInterval(int secs) { v_poll = secs; } + +AdvancedConnector::Proxy::operator QNetworkProxy() { - v_poll = secs; + return QNetworkProxy(t == Socks ? QNetworkProxy::Socks5Proxy : QNetworkProxy::HttpProxy, v_host, v_port, v_user, + v_pass); } - //---------------------------------------------------------------------------- // AdvancedConnector //---------------------------------------------------------------------------- typedef enum { Idle, Connecting, Connected } Mode; typedef enum { Force, Probe, Never } LegacySSL; -class AdvancedConnector::Private -{ +class AdvancedConnector::Private { public: ByteStream *bs; //!< Socket to use /* configuration values / "options" */ - QString opt_host; //!< explicit host from config - quint16 opt_port; //!< explicit port from config - LegacySSL opt_ssl; //!< Whether to use legacy SSL support - Proxy proxy; //!< Proxy configuration + QString opt_host; //!< explicit host from config + quint16 opt_port; //!< explicit port from config + LegacySSL opt_ssl; //!< Whether to use legacy SSL support + Proxy proxy; //!< Proxy configuration /* State tracking values */ - Mode mode; //!< Idle, Connecting, Connected - QString host; //!< Host we currently try to connect to, set from connectToServer() - int port; //!< Port we currently try to connect to, set from connectToServer() and bs_error() - int errorCode; //!< Current error, if any + Mode mode; //!< Idle, Connecting, Connected + QString host; //!< Host we currently try to connect to, set from connectToServer() + int port; //!< Port we currently try to connect to, set from connectToServer() and bs_error() + int errorCode; //!< Current error, if any }; -AdvancedConnector::AdvancedConnector(QObject *parent) -:Connector(parent) +AdvancedConnector::AdvancedConnector(QObject *parent) : Connector(parent) { - d = new Private; - d->bs = 0; + d = new Private; + d->bs = nullptr; d->opt_ssl = Never; cleanup(); d->errorCode = 0; @@ -233,7 +188,7 @@ void AdvancedConnector::cleanup() // destroy the bytestream, if there is one delete d->bs; - d->bs = 0; + d->bs = nullptr; setUseSSL(false); setPeerAddressNone(); @@ -241,7 +196,7 @@ void AdvancedConnector::cleanup() void AdvancedConnector::setProxy(const Proxy &proxy) { - if(d->mode != Idle) + if (d->mode != Idle) return; d->proxy = proxy; } @@ -252,11 +207,11 @@ void AdvancedConnector::setOptHostPort(const QString &_host, quint16 _port) XDEBUG << "h:" << _host << "p:" << _port; #endif - if(d->mode != Idle) + if (d->mode != Idle) return; // empty host means disable explicit host support - if(_host.isEmpty()) { + if (_host.isEmpty()) { d->opt_host.clear(); return; } @@ -270,7 +225,7 @@ void AdvancedConnector::setOptProbe(bool b) XDEBUG << "b:" << b; #endif - if(d->mode != Idle) + if (d->mode != Idle) return; d->opt_ssl = (b ? Probe : Never); } @@ -281,7 +236,7 @@ void AdvancedConnector::setOptSSL(bool b) XDEBUG << "b:" << b; #endif - if(d->mode != Idle) + if (d->mode != Idle) return; d->opt_ssl = (b ? Force : Never); } @@ -292,13 +247,13 @@ void AdvancedConnector::connectToServer(const QString &server) XDEBUG << "s:" << server; #endif - if(d->mode != Idle) + if (d->mode != Idle) return; - if(server.isEmpty()) + if (server.isEmpty()) return; d->errorCode = 0; - d->mode = Connecting; + d->mode = Connecting; // Encode the servername d->host = QUrl::toAce(server); @@ -315,61 +270,58 @@ void AdvancedConnector::connectToServer(const QString &server) d->opt_ssl = Never; // probe is possible only with direct connect } - if(d->proxy.type() == Proxy::HttpPoll) { + if (d->proxy.type() == Proxy::HttpPoll) { HttpPoll *s = new HttpPoll; - d->bs = s; + d->bs = s; connect(s, SIGNAL(connected()), SLOT(bs_connected())); connect(s, SIGNAL(syncStarted()), SLOT(http_syncStarted())); connect(s, SIGNAL(syncFinished()), SLOT(http_syncFinished())); connect(s, SIGNAL(error(int)), SLOT(bs_error(int))); - if(!d->proxy.user().isEmpty()) + if (!d->proxy.user().isEmpty()) s->setAuth(d->proxy.user(), d->proxy.pass()); s->setPollInterval(d->proxy.pollInterval()); - if(d->proxy.host().isEmpty()) + if (d->proxy.host().isEmpty()) s->connectToUrl(d->proxy.url()); else s->connectToHost(d->proxy.host(), d->proxy.port(), d->proxy.url()); - } - else if (d->proxy.type() == Proxy::HttpConnect) { + } else if (d->proxy.type() == Proxy::HttpConnect) { HttpConnect *s = new HttpConnect; - d->bs = s; + d->bs = s; connect(s, SIGNAL(connected()), SLOT(bs_connected())); connect(s, SIGNAL(error(int)), SLOT(bs_error(int))); - if(!d->opt_host.isEmpty()) { + if (!d->opt_host.isEmpty()) { d->host = d->opt_host; d->port = d->opt_port; } - if(!d->proxy.user().isEmpty()) + if (!d->proxy.user().isEmpty()) s->setAuth(d->proxy.user(), d->proxy.pass()); s->connectToHost(d->proxy.host(), d->proxy.port(), d->host, d->port); - } - else if (d->proxy.type() == Proxy::Socks) { + } else if (d->proxy.type() == Proxy::Socks) { SocksClient *s = new SocksClient; - d->bs = s; + d->bs = s; connect(s, SIGNAL(connected()), SLOT(bs_connected())); connect(s, SIGNAL(error(int)), SLOT(bs_error(int))); - if(!d->opt_host.isEmpty()) { + if (!d->opt_host.isEmpty()) { d->host = d->opt_host; d->port = d->opt_port; } - if(!d->proxy.user().isEmpty()) + if (!d->proxy.user().isEmpty()) s->setAuth(d->proxy.user(), d->proxy.pass()); s->connectToHost(d->proxy.host(), d->proxy.port(), d->host, d->port); - } - else { + } else { BSocket *s = new BSocket; - d->bs = s; + d->bs = s; #ifdef XMPP_DEBUG XDEBUG << "Adding socket:" << s; #endif @@ -377,65 +329,61 @@ void AdvancedConnector::connectToServer(const QString &server) connect(s, SIGNAL(connected()), SLOT(bs_connected())); connect(s, SIGNAL(error(int)), SLOT(bs_error(int))); - if(!d->opt_host.isEmpty()) { /* if custom host:port */ + if (!d->opt_host.isEmpty()) { /* if custom host:port */ d->host = d->opt_host; d->port = d->opt_port; - s->connectToHost(d->host, d->port); + s->connectToHost(d->host, quint16(d->port)); return; } else if (d->opt_ssl != Never) { /* if ssl forced or should be probed */ d->port = XMPP_LEGACY_PORT; } - s->connectToHost(XMPP_CLIENT_SRV, XMPP_CLIENT_TRANSPORT, d->host, d->port); + s->connectToHost(XMPP_CLIENT_SRV, XMPP_CLIENT_TRANSPORT, d->host, quint16(d->port)); } } void AdvancedConnector::changePollInterval(int secs) { - if(d->bs && (d->bs->inherits("XMPP::HttpPoll") || d->bs->inherits("HttpPoll"))) { - HttpPoll *s = static_cast(d->bs); + if (d->bs && (d->bs->inherits("XMPP::HttpPoll") || d->bs->inherits("HttpPoll"))) { + HttpPoll *s = static_cast(d->bs); s->setPollInterval(secs); } } ByteStream *AdvancedConnector::stream() const { - if(d->mode == Connected) + if (d->mode == Connected) return d->bs; else - return 0; + return nullptr; } -void AdvancedConnector::done() -{ - cleanup(); -} +void AdvancedConnector::done() { cleanup(); } -int AdvancedConnector::errorCode() const -{ - return d->errorCode; -} +int AdvancedConnector::errorCode() const { return d->errorCode; } void AdvancedConnector::bs_connected() { #ifdef XMPP_DEBUG XDEBUG; #endif - if(d->proxy.type() == Proxy::None) { - QHostAddress h = (static_cast(d->bs))->peerAddress(); - int p = (static_cast(d->bs))->peerPort(); + if (d->proxy.type() == Proxy::None) { + QHostAddress h = (static_cast(d->bs))->peerAddress(); + quint16 p = (static_cast(d->bs))->peerPort(); setPeerAddress(h, p); } // We won't use ssl with HttpPoll since it has ow tls handler enabled for https. // The only variant for ssl is legacy port in probing or forced mde. - if(d->proxy.type() != Proxy::HttpPoll && (d->opt_ssl == Force || ( - d->opt_ssl == Probe && peerPort() == XMPP_LEGACY_PORT))) - { + if (d->proxy.type() != Proxy::HttpPoll + && (d->opt_ssl == Force || (d->opt_ssl == Probe && peerPort() == XMPP_LEGACY_PORT))) { // in case of Probe it's ok to check actual peer "port" since we are sure Proxy=None setUseSSL(true); } + if (auto bs = qobject_cast(d->bs); bs && !bs->host().isEmpty()) { + d->host = bs->host(); + } d->mode = Connected; emit connected(); } @@ -446,67 +394,64 @@ void AdvancedConnector::bs_error(int x) XDEBUG << "e:" << x; #endif - if(d->mode == Connected) { + if (d->mode == Connected) { d->errorCode = ErrStream; emit error(); return; } bool proxyError = false; - int err = ErrConnectionRefused; - int t = d->proxy.type(); + int err = ErrConnectionRefused; + int t = d->proxy.type(); #ifdef XMPP_DEBUG qDebug("bse1"); #endif // figure out the error - if(t == Proxy::None) { - if(x == BSocket::ErrHostNotFound) + if (t == Proxy::None) { + if (x == BSocket::ErrHostNotFound) err = ErrHostNotFound; else err = ErrConnectionRefused; - } - else if(t == Proxy::HttpConnect) { - if(x == HttpConnect::ErrConnectionRefused) + } else if (t == Proxy::HttpConnect) { + if (x == HttpConnect::ErrConnectionRefused) err = ErrConnectionRefused; - else if(x == HttpConnect::ErrHostNotFound) + else if (x == HttpConnect::ErrHostNotFound) err = ErrHostNotFound; else { proxyError = true; - if(x == HttpConnect::ErrProxyAuth) + if (x == HttpConnect::ErrProxyAuth) err = ErrProxyAuth; - else if(x == HttpConnect::ErrProxyNeg) + else if (x == HttpConnect::ErrProxyNeg) err = ErrProxyNeg; else err = ErrProxyConnect; } - } - else if(t == Proxy::HttpPoll) { - if(x == HttpPoll::ErrConnectionRefused) + } else if (t == Proxy::HttpPoll) { + if (x == HttpPoll::ErrConnectionRefused) err = ErrConnectionRefused; - else if(x == HttpPoll::ErrHostNotFound) + else if (x == HttpPoll::ErrHostNotFound) err = ErrHostNotFound; else { proxyError = true; - if(x == HttpPoll::ErrProxyAuth) + if (x == HttpPoll::ErrProxyAuth) err = ErrProxyAuth; - else if(x == HttpPoll::ErrProxyNeg) + else if (x == HttpPoll::ErrProxyNeg) err = ErrProxyNeg; else err = ErrProxyConnect; } - } - else if(t == Proxy::Socks) { - if(x == SocksClient::ErrConnectionRefused) + } else if (t == Proxy::Socks) { + if (x == SocksClient::ErrConnectionRefused) err = ErrConnectionRefused; - else if(x == SocksClient::ErrHostNotFound) + else if (x == SocksClient::ErrHostNotFound) err = ErrHostNotFound; else { proxyError = true; - if(x == SocksClient::ErrProxyAuth) + if (x == SocksClient::ErrProxyAuth) err = ErrProxyAuth; - else if(x == SocksClient::ErrProxyNeg) + else if (x == SocksClient::ErrProxyNeg) err = ErrProxyNeg; else err = ErrProxyConnect; @@ -514,7 +459,7 @@ void AdvancedConnector::bs_error(int x) } // no-multi or proxy error means we quit - if(proxyError) { + if (proxyError) { cleanup(); d->errorCode = err; emit error(); @@ -525,14 +470,14 @@ void AdvancedConnector::bs_error(int x) if we shall probe the ssl legacy port, and we just did that (port=legacy), then try to connect to the normal port instead */ - if(d->opt_ssl == Probe && d->port == XMPP_LEGACY_PORT) { + if (d->opt_ssl == Probe && d->port == XMPP_LEGACY_PORT) { #ifdef XMPP_DEBUG qDebug("bse1.2"); #endif - BSocket *s = static_cast(d->bs); - d->port = XMPP_DEFAULT_PORT; + BSocket *s = static_cast(d->bs); + d->port = XMPP_DEFAULT_PORT; // at this moment we already tried everything from srv. so just try the host itself - s->connectToHost(d->host, d->port); + s->connectToHost(d->host, quint16(d->port)); } /* otherwise we have no fallbacks and must have failed to connect */ else { @@ -545,22 +490,13 @@ void AdvancedConnector::bs_error(int x) } } -void AdvancedConnector::http_syncStarted() -{ - httpSyncStarted(); -} +void AdvancedConnector::http_syncStarted() { emit httpSyncStarted(); } -void AdvancedConnector::http_syncFinished() -{ - httpSyncFinished(); -} +void AdvancedConnector::http_syncFinished() { emit httpSyncFinished(); } void AdvancedConnector::t_timeout() { - //bs_error(-1); + // bs_error(-1); } -QString AdvancedConnector::host() const -{ - return d->host; -} +QString AdvancedConnector::host() const { return d->host; } diff --git a/src/xmpp/xmpp-core/parser.cpp b/src/xmpp/xmpp-core/parser.cpp index 9706afa4..5f00c3bf 100644 --- a/src/xmpp/xmpp-core/parser.cpp +++ b/src/xmpp/xmpp-core/parser.cpp @@ -1,6 +1,6 @@ /* * parser.cpp - parse an XMPP "document" - * Copyright (C) 2003 Justin Karneges + * Copyright (C) 2020 Sergey Ilinykh * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -17,788 +17,344 @@ * */ -/* - TODO: - - For XMPP::Parser to be "perfect", some things must be solved/changed in the - Qt library: - - - Fix weird QDomElement::haveAttributeNS() bug (patch submitted to - Trolltech on Aug 31st, 2003). - - Fix weird behavior in QXmlSimpleReader of reporting endElement() when - the '/' character of a self-closing tag is reached, instead of when - the final '>' is reached. - - Fix incremental parsing bugs in QXmlSimpleReader. At the moment, the - only bug I've found is related to attribute parsing, but there might - be more (search for '###' in $QTDIR/src/xml/qxml.cpp). - - We have workarounds for all of the above problems in the code below. - - - Deal with the processing instruction as an event type, so that we - can feed it back to the application properly. Right now it is completely - untrackable and is simply tacked into the first event's actualString. We - can't easily do this because QXmlSimpleReader eats an extra byte beyond - the processing instruction before reporting it. - - - Make QXmlInputSource capable of accepting data incrementally, to ensure - proper text encoding detection and processing over a network. This is - technically not a bug, as we have our own subclass below to do it, but - it would be nice if Qt had this already. -*/ - #include "parser.h" -#include -#include - -using namespace XMPP; - -static bool qt_bug_check = false; -static bool qt_bug_have; - -//---------------------------------------------------------------------------- -// StreamInput -//---------------------------------------------------------------------------- -class StreamInput : public QXmlInputSource -{ -public: - StreamInput() - { - dec = 0; - reset(); - } - - ~StreamInput() - { - delete dec; - } - - void reset() - { - delete dec; - dec = 0; - in.resize(0); - out = ""; - at = 0; - paused = false; - mightChangeEncoding = true; - checkBad = true; - last = QChar(); - v_encoding = ""; - resetLastData(); - } - - void resetLastData() - { - last_string = ""; - } - - QString lastString() const - { - return last_string; - } - - void appendData(const QByteArray &a) - { - int oldsize = in.size(); - in.resize(oldsize + a.size()); - memcpy(in.data() + oldsize, a.data(), a.size()); - processBuf(); - } - - QChar lastRead() - { - return last; - } - - QChar next() - { - if(paused) - return EndOfData; - else - return readNext(); - } - - // NOTE: setting 'peek' to true allows the same char to be read again, - // however this still advances the internal byte processing. - QChar readNext(bool peek=false) - { - QChar c; - if(mightChangeEncoding) - c = EndOfData; - else { - if(out.isEmpty()) { - QString s; - if(!tryExtractPart(&s)) - c = EndOfData; - else { - out = s; - c = out[0]; - } - } - else - c = out[0]; - if(!peek) - out.remove(0, 1); - } - if(c == EndOfData) { -#ifdef XMPP_PARSER_DEBUG - printf("next() = EOD\n"); -#endif - } - else { -#ifdef XMPP_PARSER_DEBUG - printf("next() = [%c]\n", c.latin1()); -#endif - last = c; - } - - return c; - } - - QByteArray unprocessed() const - { - QByteArray a; - a.resize(in.size() - at); - memcpy(a.data(), in.data() + at, a.size()); - return a; - } - - void pause(bool b) - { - paused = b; - } - - bool isPaused() - { - return paused; - } - - QString encoding() const - { - return v_encoding; - } - -private: - QTextDecoder *dec; - QByteArray in; - QString out; - int at; - bool paused; - bool mightChangeEncoding; - QChar last; - QString v_encoding; - QString last_string; - bool checkBad; - - void processBuf() - { -#ifdef XMPP_PARSER_DEBUG - printf("processing. size=%d, at=%d\n", in.size(), at); -#endif - if(!dec) { - QTextCodec *codec = 0; - uchar *p = (uchar *)in.data() + at; - int size = in.size() - at; - - // do we have enough information to determine the encoding? - if(size == 0) - return; - bool utf16 = false; - if(p[0] == 0xfe || p[0] == 0xff) { - // probably going to be a UTF-16 byte order mark - if(size < 2) - return; - if((p[0] == 0xfe && p[1] == 0xff) || (p[0] == 0xff && p[1] == 0xfe)) { - // ok it is UTF-16 - utf16 = true; - } - } - if(utf16) - codec = QTextCodec::codecForMib(1000); // UTF-16 - else - codec = QTextCodec::codecForMib(106); // UTF-8 - - v_encoding = codec->name(); - dec = codec->makeDecoder(); - - // for utf16, put in the byte order mark - if(utf16) { - out += dec->toUnicode((const char *)p, 2); - at += 2; - } - } - - if(mightChangeEncoding) { - while(1) { - int n = out.indexOf('<'); - if(n != -1) { - // we need a closing bracket - int n2 = out.indexOf('>', n); - if(n2 != -1) { - ++n2; - QString h = out.mid(n, n2-n); - QString enc = processXmlHeader(h); - QTextCodec *codec = 0; - if(!enc.isEmpty()) - codec = QTextCodec::codecForName(enc.toLatin1()); - - // changing codecs - if(codec) { - v_encoding = codec->name(); - delete dec; - dec = codec->makeDecoder(); - } - mightChangeEncoding = false; - out.truncate(0); - at = 0; - resetLastData(); - break; - } - } - QString s; - if(!tryExtractPart(&s)) - break; - if(checkBad && checkForBadChars(s)) { - // go to the parser - mightChangeEncoding = false; - out.truncate(0); - at = 0; - resetLastData(); - break; - } - out += s; - } - } - } - - QString processXmlHeader(const QString &h) - { - if(h.left(5) != ""); - int startPos = h.indexOf("encoding"); - if(startPos < endPos && startPos != -1) { - QString encoding; - do { - startPos++; - if(startPos > endPos) { - return ""; - } - } while(h[startPos] != '"' && h[startPos] != '\''); - startPos++; - while(h[startPos] != '"' && h[startPos] != '\'') { - encoding += h[startPos]; - startPos++; - if(startPos > endPos) { - return ""; - } - } - return encoding; - } - else - return ""; - } - - bool tryExtractPart(QString *s) - { - int size = in.size() - at; - if(size == 0) - return false; - uchar *p = (uchar *)in.data() + at; - QString nextChars; - while(1) { - nextChars = dec->toUnicode((const char *)p, 1); - ++p; - ++at; - if(!nextChars.isEmpty()) - break; - if(at == (int)in.size()) - return false; - } - last_string += nextChars; - *s = nextChars; - - // free processed data? - if(at >= 1024) { - char *p = in.data(); - int size = in.size() - at; - memmove(p, p + at, size); - in.resize(size); - at = 0; - } - - return true; - } - - bool checkForBadChars(const QString &s) - { - int len = s.indexOf('<'); - if(len == -1) - len = s.length(); - else - checkBad = false; - for(int n = 0; n < len; ++n) { - if(!s.at(n).isSpace()) - return true; - } - return false; - } -}; - - -//---------------------------------------------------------------------------- -// ParserHandler -//---------------------------------------------------------------------------- -namespace XMPP -{ - class ParserHandler : public QXmlDefaultHandler - { - public: - explicit ParserHandler(StreamInput *_in, QDomDocument *_doc) - : in(_in) - , doc(_doc) - { - } - - ~ParserHandler() - { - while (!eventList.isEmpty()) { - delete eventList.takeFirst(); - } - } - - bool startDocument() - { - depth = 0; - return true; - } - - bool endDocument() - { - return true; - } - - bool startPrefixMapping(const QString &prefix, const QString &uri) - { - if(depth == 0) { - nsnames += prefix; - nsvalues += uri; - } - return true; - } - - bool startElement(const QString &namespaceURI, const QString &localName, const QString &qName, const QXmlAttributes &atts) - { - if(depth == 0) { - Parser::Event *e = new Parser::Event; - QXmlAttributes a; - for(int n = 0; n < atts.length(); ++n) { - QString uri = atts.uri(n); - QString ln = atts.localName(n); - if(a.index(uri, ln) == -1) - a.append(atts.qName(n), uri, ln, atts.value(n)); - } - e->setDocumentOpen(namespaceURI, localName, qName, a, nsnames, nsvalues); - nsnames.clear(); - nsvalues.clear(); - e->setActualString(in->lastString()); - - in->resetLastData(); - eventList.append(e); - in->pause(true); - } - else { - QDomElement e = doc->createElementNS(namespaceURI, qName); - for(int n = 0; n < atts.length(); ++n) { - QString uri = atts.uri(n); - QString ln = atts.localName(n); - bool have; - if(!uri.isEmpty()) { - have = e.hasAttributeNS(uri, ln); - if(qt_bug_have) - have = !have; - } - else - have = e.hasAttribute(ln); - if(!have) - e.setAttributeNS(uri, atts.qName(n), atts.value(n)); - } - - if(depth == 1) { - elem = e; - current = e; - } - else { - current.appendChild(e); - current = e; - } - } - ++depth; - return true; - } - - bool endElement(const QString &namespaceURI, const QString &localName, const QString &qName) - { - --depth; - if(depth == 0) { - Parser::Event *e = new Parser::Event; - e->setDocumentClose(namespaceURI, localName, qName); - e->setActualString(in->lastString()); - in->resetLastData(); - eventList.append(e); - in->pause(true); - } - else { - // done with a depth 1 element? - if(depth == 1) { - Parser::Event *e = new Parser::Event; - e->setElement(elem); - e->setActualString(in->lastString()); - in->resetLastData(); - eventList.append(e); - in->pause(true); - - elem = QDomElement(); - current = QDomElement(); - } - else - current = current.parentNode().toElement(); - } - - if(in->lastRead() == '/') - checkNeedMore(); - - return true; - } - - bool characters(const QString &str) - { - if(depth >= 1) { - QString content = str; - if(content.isEmpty()) - return true; - - if(!current.isNull()) { - QDomText text = doc->createTextNode(content); - current.appendChild(text); - } - } - return true; - } - - /*bool processingInstruction(const QString &target, const QString &data) - { - printf("Processing: [%s], [%s]\n", target.latin1(), data.latin1()); - in->resetLastData(); - return true; - }*/ - - void checkNeedMore() - { - // Here we will work around QXmlSimpleReader strangeness and self-closing tags. - // The problem is that endElement() is called when the '/' is read, not when - // the final '>' is read. This is a potential problem when obtaining unprocessed - // bytes from StreamInput after this event, as the '>' character will end up - // in the unprocessed chunk. To work around this, we need to advance StreamInput's - // internal byte processing, but not the xml character data. This way, the '>' - // will get processed and will no longer be in the unprocessed return, but - // QXmlSimpleReader can still read it. To do this, we call StreamInput::readNext - // with 'peek' mode. - QChar c = in->readNext(true); // peek - if(c == QXmlInputSource::EndOfData) { - needMore = true; - } - else { - // We'll assume the next char is a '>'. If it isn't, then - // QXmlSimpleReader will deal with that problem on the next - // parse. We don't need to take any action here. - needMore = false; - - // there should have been a pending event - if (!eventList.isEmpty()) { - Parser::Event *e = eventList.first(); - e->setActualString(e->actualString() + '>'); - in->resetLastData(); - } - } - } - - Parser::Event *takeEvent() - { - if(needMore) - return 0; - if(eventList.isEmpty()) - return 0; - - Parser::Event *e = eventList.takeFirst(); - in->pause(false); - return e; - } - - StreamInput *in; - QDomDocument *doc; - int depth = 1; - QStringList nsnames, nsvalues; - QDomElement elem, current; - QList eventList; - bool needMore = false; - }; -}; +#include +namespace XMPP { //---------------------------------------------------------------------------- // Event //---------------------------------------------------------------------------- -class Parser::Event::Private -{ +class Parser::Event::Private : public QSharedData { public: - int type; - QString ns, ln, qn; - QXmlAttributes a; - QDomElement e; - QString str; - QStringList nsnames, nsvalues; + int type; + QString ns, ln, qn; + QXmlStreamAttributes a; + QDomElement e; + QString str; + + QXmlStreamNamespaceDeclarations nsPrefixes; }; -Parser::Event::Event() -{ - d = 0; -} +Parser::Event::Event() { } -Parser::Event::Event(const Event &from) -{ - d = 0; - *this = from; -} +Parser::Event::Event(const Event &from) : d(from.d) { } -Parser::Event & Parser::Event::operator=(const Event &from) +Parser::Event &Parser::Event::operator=(const Event &from) { - if(&from == this) - return *this; - - delete d; - d = 0; - if(from.d) - d = new Private(*from.d); + d = from.d; return *this; } -Parser::Event::~Event() -{ - delete d; -} +Parser::Event::~Event() { } + +bool Parser::Event::isNull() const { return d == nullptr; } -bool Parser::Event::isNull() const +void Parser::Event::ensureD() { - return (d ? false: true); + if (!d) + d = new Private(); } int Parser::Event::type() const { - if(isNull()) + if (isNull()) return -1; return d->type; } QString Parser::Event::nsprefix(const QString &s) const { - QStringList::ConstIterator it = d->nsnames.constBegin(); - QStringList::ConstIterator it2 = d->nsvalues.constBegin(); - for(; it != d->nsnames.constEnd(); ++it) { - if((*it) == s) - return (*it2); - ++it2; - } - return QString::null; + Q_ASSERT(d != nullptr); + auto it + = std::find_if(d->nsPrefixes.cbegin(), d->nsPrefixes.cend(), [&](auto const &v) { return v.prefix() == s; }); + if (it == d->nsPrefixes.cend()) + return QString(); + return it->namespaceUri().toString(); } QString Parser::Event::namespaceURI() const { + Q_ASSERT(d != nullptr); return d->ns; } QString Parser::Event::localName() const { + Q_ASSERT(d != nullptr); return d->ln; } QString Parser::Event::qName() const { + Q_ASSERT(d != nullptr); return d->qn; } -QXmlAttributes Parser::Event::atts() const +QXmlStreamAttributes Parser::Event::atts() const { + Q_ASSERT(d != nullptr); return d->a; } QString Parser::Event::actualString() const { + Q_ASSERT(d != nullptr); return d->str; } QDomElement Parser::Event::element() const { + Q_ASSERT(d != nullptr); return d->e; } -void Parser::Event::setDocumentOpen(const QString &namespaceURI, const QString &localName, const QString &qName, const QXmlAttributes &atts, const QStringList &nsnames, const QStringList &nsvalues) +void Parser::Event::setDocumentOpen(const QString &namespaceURI, const QString &localName, const QString &qName, + const QXmlStreamAttributes &atts, const QXmlStreamNamespaceDeclarations &nsPrefixes) { - if(!d) - d = new Private; - d->type = DocumentOpen; - d->ns = namespaceURI; - d->ln = localName; - d->qn = qName; - d->a = atts; - d->nsnames = nsnames; - d->nsvalues = nsvalues; + ensureD(); + d->type = DocumentOpen; + d->ns = namespaceURI; + d->ln = localName; + d->qn = qName; + d->a = atts; + d->nsPrefixes = nsPrefixes; } void Parser::Event::setDocumentClose(const QString &namespaceURI, const QString &localName, const QString &qName) { - if(!d) - d = new Private; + ensureD(); d->type = DocumentClose; - d->ns = namespaceURI; - d->ln = localName; - d->qn = qName; + d->ns = namespaceURI; + d->ln = localName; + d->qn = qName; } void Parser::Event::setElement(const QDomElement &elem) { - if(!d) - d = new Private; + ensureD(); d->type = Element; - d->e = elem; + d->e = elem; } void Parser::Event::setError() { - if(!d) - d = new Private; + ensureD(); d->type = Error; } void Parser::Event::setActualString(const QString &str) { + ensureD(); d->str = str; } //---------------------------------------------------------------------------- // Parser //---------------------------------------------------------------------------- -class Parser::Private -{ +class Parser::Private { public: - Private() + QDomDocument doc; + QDomElement curElement; + QDomElement element; // root part + std::list in; + QXmlStreamReader reader; + const char *completeTag = nullptr; // this is basically a workaround for bugs like QTBUG-14661 + int completeOffset = 0; + bool streamOpened = false; + bool readerStarted = false; + std::queue events; + QString streamQName; + + void pushDataToReader() { - doc = 0; - in = 0; - handler = 0; - reader = 0; - reset(); + if (completeTag) { + readerStarted = true; + while (!in.empty()) { + if (in.front().constData() != completeTag) { + reader.addData(in.front()); + in.erase(in.begin()); + } else { + // Qt has some bugs, so ensure we push data only ending with '>' + if (completeOffset == in.front().size() - 1) { + reader.addData(in.front()); + in.erase(in.begin()); + } else { + QByteArray part = in.front().left(completeOffset + 1); + reader.addData(part); + in.front().remove(0, completeOffset + 1); + } + completeTag = nullptr; + break; + } + } + } } - ~Private() + void handleStartElement() { - reset(false); + auto ns = reader.namespaceUri().toString(); + QString name = reader.name().toString(); + if (streamOpened) { + QDomElement newEl; + if (ns.isEmpty()) + newEl = doc.createElement(name); + else + newEl = doc.createElementNS(ns, name); + if (curElement.isNull()) { + curElement = newEl; + element = newEl; + } else { + curElement = curElement.appendChild(newEl).toElement(); + } + + const auto &attrs = reader.attributes(); + for (auto const &a : attrs) { + QDomAttr da; + if (a.namespaceUri().isEmpty()) + da = doc.createAttribute(a.name().toString()); + else + da = doc.createAttributeNS(a.namespaceUri().toString(), a.name().toString()); + da.setPrefix(a.prefix().toString()); + da.setValue(a.value().toString()); + if (a.namespaceUri().isEmpty()) + curElement.setAttributeNode(da); + else + curElement.setAttributeNodeNS(da); + } + } else { + Event e; + streamQName = reader.qualifiedName().toString(); + e.setDocumentOpen(ns, name, streamQName, reader.attributes(), reader.namespaceDeclarations()); + events.push(e); + streamOpened = true; + } } - void reset(bool create=true) + void handleEndElement() { - delete reader; - delete handler; - delete in; - delete doc; - - if(create) { - doc = new QDomDocument; - in = new StreamInput; - handler = new ParserHandler(in, doc); - reader = new QXmlSimpleReader; - reader->setContentHandler(handler); - - // initialize the reader - in->pause(true); - reader->parse(in, true); - in->pause(false); - } else { - reader = 0; - handler = 0; - in = 0; - doc = 0; + if (curElement.isNull() && reader.qualifiedName() == streamQName) { + Event e; + e.setDocumentClose(reader.namespaceUri().toString(), reader.name().toString(), streamQName); + events.push(e); + return; + } + Q_ASSERT_X(!curElement.isNull(), "xml parser", "XML reader hasn't reported error for invalid element close"); + Q_ASSERT_X( + curElement.namespaceURI() == reader.namespaceUri() && curElement.tagName() == reader.name(), "xml parser", + qPrintable( + QString("XML reader hasn't reported open/close tags mismatch. expected close for <%1 xmlns=\"%2\"> " + "but got close for <%3 xmlns=\"%4\">") + .arg(curElement.tagName(), curElement.namespaceURI(), reader.name().toString(), + reader.namespaceUri().toString()))); +#if 0 + if (element.isNull()) { + Event e; + qWarning("xml parser: closing not existing element: %s", qPrintable(reader.qualifiedName().toString())); + e.setError(); + return; } + + if (!(element.namespaceURI() == reader.namespaceUri() && element.tagName() == reader.name())) { + Event e; + qWarning("XML reader hasn't reported open/close tags mismatch: %s vs %s", qPrintable(element.tagName()), + qPrintable(reader.qualifiedName().toString())); + e.setError(); + return; + } +#endif + if (curElement.parentNode().isNull()) { + Event e; + e.setElement(curElement); + events.push(e); + } + curElement = curElement.parentNode().toElement(); } - QDomDocument *doc; - StreamInput *in; - ParserHandler *handler; - QXmlSimpleReader *reader; -}; + void handleText() + { + if (curElement.isNull()) { + if (!reader.isWhitespace()) + qWarning("Text node out of element (ignored): %s", qPrintable(reader.text().toString())); + return; + } + auto node = doc.createTextNode(reader.text().toString()); + curElement.appendChild(node); + } -Parser::Parser() -{ - d = new Private; - - // check for evil bug in Qt <= 3.2.1 - if(!qt_bug_check) { - qt_bug_check = true; - QDomElement e = d->doc->createElementNS("someuri", "somename"); - if(e.hasAttributeNS("someuri", "somename")) - qt_bug_have = true; - else - qt_bug_have = false; + void collectEvents() + { + auto tt = reader.readNext(); + while (tt != QXmlStreamReader::NoToken && tt != QXmlStreamReader::Invalid) { + if (tt == QXmlStreamReader::StartElement) { + handleStartElement(); + } else if (tt == QXmlStreamReader::EndElement) { + handleEndElement(); + } else if (tt == QXmlStreamReader::Characters) { + handleText(); + } else { + Q_ASSERT_X(tt != QXmlStreamReader::EntityReference, "xml parser", + qPrintable(QString("unexpected xml entity: %1").arg(reader.text()))); + } + tt = reader.readNext(); + } + if (tt == QXmlStreamReader::Invalid) { + if (reader.error() == QXmlStreamReader::PrematureEndOfDocumentError) + return; + qDebug("xml parser error: %s", qPrintable(reader.errorString())); + Event e; + e.setError(); + events.push(e); + } } -} -Parser::~Parser() -{ - delete d; -} + Parser::Event readNext() + { + Event e; + pushDataToReader(); + if (!readerStarted) + return e; + collectEvents(); + if (!events.empty()) { + e = events.front(); + events.pop(); + } + return e; + } +}; -void Parser::reset() -{ - d->reset(); -} +Parser::Parser() { reset(); } -void Parser::appendData(const QByteArray &a) -{ - d->in->appendData(a); +Parser::~Parser() { } - // if handler was waiting for more, give it a kick - if(d->handler->needMore) - d->handler->checkNeedMore(); -} +void Parser::reset() { d.reset(new Private); } -Parser::Event Parser::readNext() +void Parser::appendData(const QByteArray &a) { - Event e; - if(d->handler->needMore) - return e; - Event *ep = d->handler->takeEvent(); - if(!ep) { - if(!d->reader->parseContinue()) { - e.setError(); - return e; + if (a.isEmpty()) + return; + d->in.push_back(a); + for (int i = a.size() - 1; i >= 0; --i) { + if (a.at(i) == '>') { // this may happend in CDATA too, but let's hope Qt handles it properly + d->completeTag = a.constData(); + d->completeOffset = i; + break; } - ep = d->handler->takeEvent(); - if(!ep) - return e; } - e = *ep; - delete ep; - return e; } +Parser::Event Parser::readNext() { return d->readNext(); } + QByteArray Parser::unprocessed() const { - return d->in->unprocessed(); + QByteArray ret; + for (auto const &a : d->in) { + ret += a; + } + return ret; } -QString Parser::encoding() const -{ - return d->in->encoding(); +QStringView Parser::encoding() const { return d->reader.documentEncoding(); } + } diff --git a/src/xmpp/xmpp-core/parser.h b/src/xmpp/xmpp-core/parser.h index b31841e7..811bd4fe 100644 --- a/src/xmpp/xmpp-core/parser.h +++ b/src/xmpp/xmpp-core/parser.h @@ -1,6 +1,6 @@ /* * parser.h - parse an XMPP "document" - * Copyright (C) 2003 Justin Karneges + * Copyright (C) 2003-2020 Justin Karneges, Sergey Ilinykh * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -20,66 +20,75 @@ #ifndef PARSER_H #define PARSER_H -#include -#include +#include +#include +#include -namespace XMPP -{ - class Parser - { +#include + +namespace XMPP { + +class Parser { +public: + struct NSPrefix { + QString name; + QString value; + }; + + class Event { public: - Parser(); - ~Parser(); - - class Event - { - public: - enum Type { DocumentOpen, DocumentClose, Element, Error }; - Event(); - Event(const Event &); - Event & operator=(const Event &); - ~Event(); - - bool isNull() const; - int type() const; - - // for document open - QString nsprefix(const QString &s=QString::null) const; - - // for document open / close - QString namespaceURI() const; - QString localName() const; - QString qName() const; - QXmlAttributes atts() const; - - // for element - QDomElement element() const; - - // for any - QString actualString() const; - - // setup - void setDocumentOpen(const QString &namespaceURI, const QString &localName, const QString &qName, const QXmlAttributes &atts, const QStringList &nsnames, const QStringList &nsvalues); - void setDocumentClose(const QString &namespaceURI, const QString &localName, const QString &qName); - void setElement(const QDomElement &elem); - void setError(); - void setActualString(const QString &); - - private: - class Private; - Private *d; - }; - - void reset(); - void appendData(const QByteArray &a); - Event readNext(); - QByteArray unprocessed() const; - QString encoding() const; + enum Type { DocumentOpen, DocumentClose, Element, Error }; + Event(); + Event(const Event &); + Event &operator=(const Event &); + ~Event(); + + bool isNull() const; + int type() const; + + // for document open + QString nsprefix(const QString &s = QString()) const; + + // for document open / close + QString namespaceURI() const; + QString localName() const; + QString qName() const; + QXmlStreamAttributes atts() const; + + // for element + QDomElement element() const; + + // for any + QString actualString() const; + + // setup + void setDocumentOpen(const QString &namespaceURI, const QString &localName, const QString &qName, + const QXmlStreamAttributes &atts, const QXmlStreamNamespaceDeclarations &nsPrefixes); + void setDocumentClose(const QString &namespaceURI, const QString &localName, const QString &qName); + void setElement(const QDomElement &elem); + void setError(); + void setActualString(const QString &); private: + void ensureD(); class Private; - Private *d; + QExplicitlySharedDataPointer d; }; -} -#endif + Parser(); + ~Parser(); + + void reset(); + void appendData(const QByteArray &a); + Event readNext(); + QByteArray unprocessed() const; + QStringView encoding() const; + +private: + class Private; + std::unique_ptr d; +}; + +} // namespace XMPP + +#endif // PARSER_H diff --git a/src/xmpp/xmpp-core/protocol.cpp b/src/xmpp/xmpp-core/protocol.cpp index 42553ec1..9cb3aca3 100644 --- a/src/xmpp/xmpp-core/protocol.cpp +++ b/src/xmpp/xmpp-core/protocol.cpp @@ -1,4 +1,4 @@ - /* +/* * protocol.cpp - XMPP-Core protocol state machine * Copyright (C) 2004 Justin Karneges * @@ -23,15 +23,15 @@ #include "protocol.h" -#include -#include -#include -#include - #ifdef XMPP_TEST #include "td.h" #endif +#include +#include +#include +#include + using namespace XMPP; // printArray @@ -42,15 +42,13 @@ using namespace XMPP; static QString printArray(const QByteArray &a) { QString s; - for(int n = 0; n < a.size(); ++n) { + for (int n = 0; n < a.size(); ++n) { unsigned char c = (unsigned char)a[(int)n]; - if(c < 32 || c >= 127) { - QString str; - str.sprintf("[%02x]", c); + if (c < 32 || c >= 127) { + QString str = QString::asprintf("[%02x]", c); s += str; - } - else - s += c; + } else + s += QChar::fromLatin1(c); } return s; } @@ -60,8 +58,8 @@ static QString printArray(const QByteArray &a) // Get an element's first child element static QDomElement firstChildElement(const QDomElement &e) { - for(QDomNode n = e.firstChild(); !n.isNull(); n = n.nextSibling()) { - if(n.isElement()) + for (QDomNode n = e.firstChild(); !n.isNull(); n = n.nextSibling()) { + if (n.isElement()) return n.toElement(); } return QDomElement(); @@ -81,82 +79,74 @@ Version::Version(int maj, int min) //---------------------------------------------------------------------------- StreamFeatures::StreamFeatures() { - tls_supported = false; - sasl_supported = false; - bind_supported = false; - tls_required = false; + tls_supported = false; + sasl_supported = false; + bind_supported = false; + tls_required = false; compress_supported = false; - sm_supported = false; - session_supported = false; - session_required = false; + sm_supported = false; + session_supported = false; + session_required = false; } //---------------------------------------------------------------------------- // BasicProtocol //---------------------------------------------------------------------------- -BasicProtocol::SASLCondEntry BasicProtocol::saslCondTable[] = -{ - { "aborted", Aborted }, - { "account-disabled", AccountDisabled }, - { "credentials-expired", CredentialsExpired }, - { "encryption-required", EncryptionRequired }, - { "incorrect-encoding", IncorrectEncoding }, - { "invalid-authzid", InvalidAuthzid }, - { "invalid-mechanism", InvalidMech }, - { "malformed-request", MalformedRequest }, - { "mechanism-too-weak", MechTooWeak }, - { "not-authorized", NotAuthorized }, +BasicProtocol::SASLCondEntry BasicProtocol::saslCondTable[] = { + { "aborted", Aborted }, + { "account-disabled", AccountDisabled }, + { "credentials-expired", CredentialsExpired }, + { "encryption-required", EncryptionRequired }, + { "incorrect-encoding", IncorrectEncoding }, + { "invalid-authzid", InvalidAuthzid }, + { "invalid-mechanism", InvalidMech }, + { "malformed-request", MalformedRequest }, + { "mechanism-too-weak", MechTooWeak }, + { "not-authorized", NotAuthorized }, { "temporary-auth-failure", TemporaryAuthFailure }, - { 0, 0 }, + { nullptr, 0 }, }; -BasicProtocol::StreamCondEntry BasicProtocol::streamCondTable[] = -{ - { "bad-format", BadFormat }, - { "bad-namespace-prefix", BadNamespacePrefix }, - { "conflict", Conflict }, - { "connection-timeout", ConnectionTimeout }, - { "host-gone", HostGone }, - { "host-unknown", HostUnknown }, - { "improper-addressing", ImproperAddressing }, - { "internal-server-error", InternalServerError }, - { "invalid-from", InvalidFrom }, - { "invalid-namespace", InvalidNamespace }, - { "invalid-xml", InvalidXml }, - { "not-authorized", StreamNotAuthorized }, - { "not-well-formed", NotWellFormed }, - { "policy-violation", PolicyViolation }, +BasicProtocol::StreamCondEntry BasicProtocol::streamCondTable[] = { + { "bad-format", BadFormat }, + { "bad-namespace-prefix", BadNamespacePrefix }, + { "conflict", Conflict }, + { "connection-timeout", ConnectionTimeout }, + { "host-gone", HostGone }, + { "host-unknown", HostUnknown }, + { "improper-addressing", ImproperAddressing }, + { "internal-server-error", InternalServerError }, + { "invalid-from", InvalidFrom }, + { "invalid-namespace", InvalidNamespace }, + { "invalid-xml", InvalidXml }, + { "not-authorized", StreamNotAuthorized }, + { "not-well-formed", NotWellFormed }, + { "policy-violation", PolicyViolation }, { "remote-connection-failed", RemoteConnectionFailed }, - { "reset", StreamReset }, - { "resource-constraint", ResourceConstraint }, - { "restricted-xml", RestrictedXml }, - { "see-other-host", SeeOtherHost }, - { "system-shutdown", SystemShutdown }, - { "undefined-condition", UndefinedCondition }, - { "unsupported-encoding", UnsupportedEncoding }, - { "unsupported-stanza-type", UnsupportedStanzaType }, - { "unsupported-version", UnsupportedVersion }, - { 0, 0 }, + { "reset", StreamReset }, + { "resource-constraint", ResourceConstraint }, + { "restricted-xml", RestrictedXml }, + { "see-other-host", SeeOtherHost }, + { "system-shutdown", SystemShutdown }, + { "undefined-condition", UndefinedCondition }, + { "unsupported-encoding", UnsupportedEncoding }, + { "unsupported-stanza-type", UnsupportedStanzaType }, + { "unsupported-version", UnsupportedVersion }, + { nullptr, 0 }, }; -BasicProtocol::BasicProtocol() -:XmlProtocol() -{ - init(); -} +BasicProtocol::BasicProtocol() : XmlProtocol() { init(); } -BasicProtocol::~BasicProtocol() -{ -} +BasicProtocol::~BasicProtocol() { } void BasicProtocol::init() { - errCond = -1; - sasl_authed = false; - doShutdown = false; - delayedError = false; - closeError = false; - ready = false; + errCond = -1; + sasl_authed = false; + doShutdown = false; + delayedError = false; + closeError = false; + ready = false; stanzasPending = 0; stanzasWritten = 0; } @@ -166,14 +156,14 @@ void BasicProtocol::reset() XmlProtocol::reset(); init(); - to = QString(); - from = QString(); - id = QString(); - lang = QString(); - version = Version(1,0); - errText = QString(); + to = QString(); + from = QString(); + id = QString(); + lang = QString(); + version = Version(1, 0); + errText = QString(); errAppSpec = QDomElement(); - otherHost = QString(); + otherHost = QString(); spare.resize(0); sasl_mech = QString(); sasl_mechlist.clear(); @@ -206,19 +196,17 @@ void BasicProtocol::sendWhitespace() void BasicProtocol::clearSendQueue() { sendList.clear(); + XmlProtocol::clearSendQueue(); } QDomElement BasicProtocol::recvStanza() { QDomElement e = stanzaToRecv; - stanzaToRecv = QDomElement(); + stanzaToRecv = QDomElement(); return e; } -void BasicProtocol::shutdown() -{ - doShutdown = true; -} +void BasicProtocol::shutdown() { doShutdown = true; } void BasicProtocol::shutdownWithError(int cond, const QString &str) { @@ -226,30 +214,15 @@ void BasicProtocol::shutdownWithError(int cond, const QString &str) delayErrorAndClose(cond); } -bool BasicProtocol::isReady() const -{ - return ready; -} +bool BasicProtocol::isReady() const { return ready; } -void BasicProtocol::setReady(bool b) -{ - ready = b; -} +void BasicProtocol::setReady(bool b) { ready = b; } -QString BasicProtocol::saslMech() const -{ - return sasl_mech; -} +QString BasicProtocol::saslMech() const { return sasl_mech; } -QByteArray BasicProtocol::saslStep() const -{ - return sasl_step; -} +QByteArray BasicProtocol::saslStep() const { return sasl_step; } -void BasicProtocol::setSASLMechList(const QStringList &list) -{ - sasl_mechlist = list; -} +void BasicProtocol::setSASLMechList(const QStringList &list) { sasl_mechlist = list; } void BasicProtocol::setSASLFirst(const QString &mech, const QByteArray &step) { @@ -257,20 +230,14 @@ void BasicProtocol::setSASLFirst(const QString &mech, const QByteArray &step) sasl_step = step; } -void BasicProtocol::setSASLNext(const QByteArray &step) -{ - sasl_step = step; -} +void BasicProtocol::setSASLNext(const QByteArray &step) { sasl_step = step; } -void BasicProtocol::setSASLAuthed() -{ - sasl_authed = true; -} +void BasicProtocol::setSASLAuthed() { sasl_authed = true; } int BasicProtocol::stringToSASLCond(const QString &s) { - for(int n = 0; saslCondTable[n].str; ++n) { - if(s == saslCondTable[n].str) + for (int n = 0; saslCondTable[n].str; ++n) { + if (s == saslCondTable[n].str) return saslCondTable[n].cond; } return -1; @@ -278,8 +245,8 @@ int BasicProtocol::stringToSASLCond(const QString &s) int BasicProtocol::stringToStreamCond(const QString &s) { - for(int n = 0; streamCondTable[n].str; ++n) { - if(s == streamCondTable[n].str) + for (int n = 0; streamCondTable[n].str; ++n) { + if (s == streamCondTable[n].str) return streamCondTable[n].cond; } return -1; @@ -287,8 +254,8 @@ int BasicProtocol::stringToStreamCond(const QString &s) QString BasicProtocol::saslCondToString(int x) { - for(int n = 0; saslCondTable[n].str; ++n) { - if(x == saslCondTable[n].cond) + for (int n = 0; saslCondTable[n].str; ++n) { + if (x == saslCondTable[n].cond) return saslCondTable[n].str; } return QString(); @@ -296,8 +263,8 @@ QString BasicProtocol::saslCondToString(int x) QString BasicProtocol::streamCondToString(int x) { - for(int n = 0; streamCondTable[n].str; ++n) { - if(x == streamCondTable[n].cond) + for (int n = 0; streamCondTable[n].str; ++n) { + if (x == streamCondTable[n].cond) return streamCondTable[n].str; } return QString(); @@ -305,27 +272,26 @@ QString BasicProtocol::streamCondToString(int x) void BasicProtocol::extractStreamError(const QDomElement &e) { - QString text; - QHash langText; - QDomElement appSpec; + QString text; + QHash langText; + QDomElement appSpec; QDomElement t = firstChildElement(e); - if(t.isNull() || t.namespaceURI() != NS_STREAMS) { + if (t.isNull() || t.namespaceURI() != NS_STREAMS) { // probably old-style error errCond = -1; errText = e.text(); - } - else + } else errCond = stringToStreamCond(t.tagName()); - if(errCond != -1) { - if(errCond == SeeOtherHost) + if (errCond != -1) { + if (errCond == SeeOtherHost) otherHost = t.text(); auto nodes = e.elementsByTagNameNS(NS_STREAMS, "text"); if (nodes.count()) { for (int i = 0; i < nodes.count(); i++) { - auto e = nodes.item(i).toElement(); + auto e = nodes.item(i).toElement(); QString lang = e.attributeNS(NS_STREAMS, "lang", ""); langText.insert(lang, e.text()); } @@ -334,38 +300,32 @@ void BasicProtocol::extractStreamError(const QDomElement &e) // find first non-standard namespaced element QDomNodeList nl = e.childNodes(); - for(int n = 0; n < nl.count(); ++n) { + for (int n = 0; n < nl.count(); ++n) { QDomNode i = nl.item(n); - if(i.isElement() && i.namespaceURI() != NS_STREAMS) { + if (i.isElement() && i.namespaceURI() != NS_STREAMS) { appSpec = i.toElement(); break; } } - errText = text; + errText = text; errLangText = langText; - errAppSpec = appSpec; + errAppSpec = appSpec; } } -void BasicProtocol::send(const QDomElement &e, bool clip) -{ - writeElement(e, TypeElement, false, clip, false); -} +void BasicProtocol::send(const QDomElement &e, bool clip) { writeElement(e, TypeElement, false, clip, false); } -void BasicProtocol::sendUrgent(const QDomElement &e, bool clip) -{ - writeElement(e, TypeElement, false, clip, true); -} +void BasicProtocol::sendUrgent(const QDomElement &e, bool clip) { writeElement(e, TypeElement, false, clip, true); } void BasicProtocol::sendStreamError(int cond, const QString &text, const QDomElement &appSpec) { - QDomElement se = doc.createElementNS(NS_ETHERX, "stream:error"); + QDomElement se = doc.createElementNS(NS_ETHERX, "stream:error"); QDomElement err = doc.createElementNS(NS_STREAMS, streamCondToString(cond)); - if(!otherHost.isEmpty()) + if (!otherHost.isEmpty()) err.appendChild(doc.createTextNode(otherHost)); se.appendChild(err); - if(!text.isEmpty()) { + if (!text.isEmpty()) { QDomElement te = doc.createElementNS(NS_STREAMS, "text"); te.setAttributeNS(NS_XML, "xml:lang", "en"); te.appendChild(doc.createTextNode(text)); @@ -387,8 +347,8 @@ void BasicProtocol::sendStreamError(const QString &text) bool BasicProtocol::errorAndClose(int cond, const QString &text, const QDomElement &appSpec) { closeError = true; - errCond = cond; - errText = text; + errCond = cond; + errText = text; errAppSpec = appSpec; sendStreamError(cond, text, appSpec); return close(); @@ -396,23 +356,23 @@ bool BasicProtocol::errorAndClose(int cond, const QString &text, const QDomEleme bool BasicProtocol::error(int code) { - event = EError; + event = EError; errorCode = code; return true; } void BasicProtocol::delayErrorAndClose(int cond, const QString &text, const QDomElement &appSpec) { - errorCode = ErrStream; - errCond = cond; - errText = text; - errAppSpec = appSpec; + errorCode = ErrStream; + errCond = cond; + errText = text; + errAppSpec = appSpec; delayedError = true; } void BasicProtocol::delayError(int code) { - errorCode = code; + errorCode = code; delayedError = true; } @@ -421,79 +381,78 @@ QDomElement BasicProtocol::docElement() // create the root element QDomElement e = doc.createElementNS(NS_ETHERX, "stream:stream"); - QString defns = defaultNamespace(); - const QStringList list = extraNamespaces(); + QString defns = defaultNamespace(); + const QStringList list = extraNamespaces(); // HACK: using attributes seems to be the only way to get additional namespaces in here - if(!defns.isEmpty()) - e.setAttribute("xmlns", defns); - for(QStringList::ConstIterator it = list.begin(); it != list.end();) { + if (!defns.isEmpty()) + e.setAttribute(QString::fromLatin1("xmlns"), defns); + for (QStringList::ConstIterator it = list.begin(); it != list.end();) { QString prefix = *(it++); - QString uri = *(it++); - e.setAttribute(QString("xmlns:") + prefix, uri); + QString uri = *(it++); + e.setAttribute(QString::fromLatin1("xmlns:") + prefix, uri); } // additional attributes - if(!isIncoming() && !to.isEmpty()) - e.setAttribute("to", to); - if(isIncoming() && !from.isEmpty()) - e.setAttribute("from", from); - if(!id.isEmpty()) - e.setAttribute("id", id); - if(!lang.isEmpty()) - e.setAttributeNS(NS_XML, "xml:lang", lang); - if(version.major > 0 || version.minor > 0) - e.setAttribute("version", QString::number(version.major) + '.' + QString::number(version.minor)); + if (!isIncoming() && !to.isEmpty()) + e.setAttribute(QString::fromLatin1("to"), to); + if (isIncoming() && !from.isEmpty()) + e.setAttribute(QString::fromLatin1("from"), from); + if (!id.isEmpty()) + e.setAttribute(QString::fromLatin1("id"), id); + if (!lang.isEmpty()) + e.setAttributeNS(QString::fromLatin1(NS_XML), QString::fromLatin1("xml:lang"), lang); + if (version.major > 0 || version.minor > 0) + e.setAttribute(QString::fromLatin1("version"), + QString::number(version.major) + '.' + QString::number(version.minor)); return e; } void BasicProtocol::handleDocOpen(const Parser::Event &pe) { - if(isIncoming()) { - if(xmlEncoding() != "UTF-8") { + if (isIncoming()) { + if (xmlEncoding() != "UTF-8") { delayErrorAndClose(UnsupportedEncoding); return; } } - if(pe.namespaceURI() == NS_ETHERX && pe.localName() == "stream") { - QXmlAttributes atts = pe.atts(); + if (pe.namespaceURI() == NS_ETHERX && pe.localName() == "stream") { + auto atts = pe.atts(); // grab the version - int major = 0; - int minor = 0; - QString verstr = atts.value("version"); - if(!verstr.isEmpty()) { + int major = 0; + int minor = 0; + auto verstr = atts.value("version"); + if (!verstr.isEmpty()) { int n = verstr.indexOf('.'); - if(n != -1) { + if (n != -1) { major = verstr.mid(0, n).toInt(); - minor = verstr.mid(n+1).toInt(); - } - else { + minor = verstr.mid(n + 1).toInt(); + } else { major = verstr.toInt(); minor = 0; } } version = Version(major, minor); - if(isIncoming()) { - to = atts.value("to"); - QString peerLang = atts.value(NS_XML, "lang"); - if(!peerLang.isEmpty()) - lang = peerLang; + if (isIncoming()) { + to = atts.value("to").toString(); + auto peerLang = atts.value(NS_XML, "lang"); + if (!peerLang.isEmpty()) + lang = peerLang.toString(); } // outgoing else { - from = atts.value("from"); - lang = atts.value(NS_XML, "lang"); - id = atts.value("id"); + from = atts.value("from").toString(); + lang = atts.value(NS_XML, "lang").toString(); + id = atts.value("id").toString(); } handleStreamOpen(pe); - } - else { - if(isIncoming()) + } else { + if (isIncoming()) delayErrorAndClose(BadFormat); else delayError(ErrProtocol); @@ -502,7 +461,7 @@ void BasicProtocol::handleDocOpen(const Parser::Event &pe) bool BasicProtocol::handleError() { - if(isIncoming()) + if (isIncoming()) return errorAndClose(NotWellFormed); else return error(ErrParse); @@ -510,12 +469,11 @@ bool BasicProtocol::handleError() bool BasicProtocol::handleCloseFinished() { - if(closeError) { - event = EError; + if (closeError) { + event = EError; errorCode = ErrStream; // note: errCond and friends are already set at this point - } - else + } else event = EClosed; return true; } @@ -523,64 +481,63 @@ bool BasicProtocol::handleCloseFinished() bool BasicProtocol::doStep(const QDomElement &e) { // handle pending error - if(delayedError) { - if(isIncoming()) + if (delayedError) { + if (isIncoming()) return errorAndClose(errCond, errText, errAppSpec); else return error(errorCode); } // shutdown? - if(doShutdown) { + if (doShutdown) { doShutdown = false; return close(); } - if(!e.isNull()) { + if (!e.isNull()) { // check for error - if(e.namespaceURI() == NS_ETHERX && e.tagName() == "error") { + if (e.namespaceURI() == NS_ETHERX && e.tagName() == "error") { extractStreamError(e); return error(ErrStream); } } - if(ready) { + if (ready) { // stanzas written? - if(stanzasWritten > 0) { + if (stanzasWritten > 0) { --stanzasWritten; event = EStanzaSent; return true; } // send items? - if(!sendList.isEmpty()) { + if (!sendList.isEmpty()) { SendItem i; { QList::Iterator it = sendList.begin(); - i = (*it); + i = (*it); sendList.erase(it); } // outgoing stanza? - if(!i.stanzaToSend.isNull()) { + if (!i.stanzaToSend.isNull()) { ++stanzasPending; writeElement(i.stanzaToSend, TypeStanza, true); event = ESend; } // direct send? - else if(!i.stringToSend.isEmpty()) { + else if (!i.stringToSend.isEmpty()) { writeString(i.stringToSend, TypeDirect, true); event = ESend; } // whitespace keepalive? - else if(i.doWhitespace) { + else if (i.doWhitespace) { writeString("\n", TypePing, false); event = ESend; } return true; - } - else { + } else { // if we have pending outgoing stanzas, ask for write notification - if(stanzasPending) + if (stanzasPending) notify |= NSend; } } @@ -590,7 +547,7 @@ bool BasicProtocol::doStep(const QDomElement &e) void BasicProtocol::itemWritten(int id, int) { - if(id == TypeStanza) { + if (id == TypeStanza) { --stanzasPending; ++stanzasWritten; } @@ -616,15 +573,11 @@ void BasicProtocol::handleStreamOpen(const Parser::Event &) //---------------------------------------------------------------------------- // CoreProtocol //---------------------------------------------------------------------------- -CoreProtocol::CoreProtocol() -:BasicProtocol() -{ - init(); -} +CoreProtocol::CoreProtocol() : BasicProtocol() { init(); } CoreProtocol::~CoreProtocol() { - //fprintf(stderr, "\tCoreProtocol::~CoreProtocol()\n"); + // fprintf(stderr, "\tCoreProtocol::~CoreProtocol()\n"); } void CoreProtocol::init() @@ -632,29 +585,29 @@ void CoreProtocol::init() step = Start; // ?? - server = false; - dialback = false; + server = false; + dialback = false; dialback_verify = false; // settings - jid_ = Jid(); - password = QString(); - oldOnly = false; + jid_ = Jid(); + password = QString(); + oldOnly = false; allowPlain = false; - doTLS = true; - doAuth = true; + doTLS = true; + doAuth = true; doCompress = true; - doBinding = true; + doBinding = true; // input user = QString(); host = QString(); // status - old = false; - digest = false; - tls_started = false; - sasl_started = false; + old = false; + digest = false; + tls_started = false; + sasl_started = false; compress_started = false; sm.reset(); @@ -666,13 +619,15 @@ void CoreProtocol::reset() init(); } -void CoreProtocol::needTimer(int seconds) { +void CoreProtocol::needTimer(int seconds) +{ notify |= NTimeout; - need = NNotify; + need = NNotify; timeout_sec = seconds; } -void CoreProtocol::sendStanza(const QDomElement &e) { +void CoreProtocol::sendStanza(const QDomElement &e) +{ if (sm.isActive()) { int len = sm.addUnacknowledgedStanza(e); if (len > 5 && len % 4 == 0) @@ -684,43 +639,44 @@ void CoreProtocol::sendStanza(const QDomElement &e) { void CoreProtocol::startClientOut(const Jid &_jid, bool _oldOnly, bool tlsActive, bool _doAuth, bool _doCompress) { - jid_ = _jid; - to = _jid.domain(); - oldOnly = _oldOnly; - doAuth = _doAuth; - doCompress = _doCompress; + jid_ = _jid; + to = _jid.domain(); + oldOnly = _oldOnly; + doAuth = _doAuth; + doCompress = _doCompress; tls_started = tlsActive; - if(oldOnly) - version = Version(0,0); + if (oldOnly) + version = Version(0, 0); startConnect(); } void CoreProtocol::startServerOut(const QString &_to) { server = true; - to = _to; + to = _to; startConnect(); } void CoreProtocol::startDialbackOut(const QString &_to, const QString &_from) { - server = true; - dialback = true; - to = _to; + server = true; + dialback = true; + to = _to; self_from = _from; startConnect(); } -void CoreProtocol::startDialbackVerifyOut(const QString &_to, const QString &_from, const QString &id, const QString &key) +void CoreProtocol::startDialbackVerifyOut(const QString &_to, const QString &_from, const QString &id, + const QString &key) { - server = true; - dialback = true; + server = true; + dialback = true; dialback_verify = true; - to = _to; - self_from = _from; - dialback_id = id; - dialback_key = key; + to = _to; + self_from = _from; + dialback_id = id; + dialback_key = key; startConnect(); } @@ -733,49 +689,25 @@ void CoreProtocol::startClientIn(const QString &_id) void CoreProtocol::startServerIn(const QString &_id) { server = true; - id = _id; + id = _id; startAccept(); } -void CoreProtocol::setLang(const QString &s) -{ - lang = s; -} +void CoreProtocol::setLang(const QString &s) { lang = s; } -void CoreProtocol::setAllowTLS(bool b) -{ - doTLS = b; -} +void CoreProtocol::setAllowTLS(bool b) { doTLS = b; } -void CoreProtocol::setAllowBind(bool b) -{ - doBinding = b; -} +void CoreProtocol::setAllowBind(bool b) { doBinding = b; } -void CoreProtocol::setAllowPlain(bool b) -{ - allowPlain = b; -} +void CoreProtocol::setAllowPlain(bool b) { allowPlain = b; } -const Jid& CoreProtocol::jid() const -{ - return jid_; -} +const Jid &CoreProtocol::jid() const { return jid_; } -void CoreProtocol::setPassword(const QString &s) -{ - password = s; -} +void CoreProtocol::setPassword(const QString &s) { password = s; } -void CoreProtocol::setFrom(const QString &s) -{ - from = s; -} +void CoreProtocol::setFrom(const QString &s) { from = s; } -void CoreProtocol::setDialbackKey(const QString &s) -{ - dialback_key = s; -} +void CoreProtocol::setDialbackKey(const QString &s) { dialback_key = s; } bool CoreProtocol::loginComplete() { @@ -794,10 +726,10 @@ bool CoreProtocol::loginComplete() send(e); } event = ESend; - step = GetSMResponse; + step = GetSMResponse; } else { event = EReady; - step = Done; + step = Done; } return true; } @@ -805,7 +737,7 @@ bool CoreProtocol::loginComplete() int CoreProtocol::getOldErrorCode(const QDomElement &e) { QDomElement err = e.elementsByTagNameNS(NS_CLIENT, "error").item(0).toElement(); - if(err.isNull() || !err.hasAttribute("code")) + if (err.isNull() || !err.hasAttribute("code")) return -1; return err.attribute("code").toInt(); } @@ -825,9 +757,9 @@ int CoreProtocol::getOldErrorCode(const QDomElement &e) bool CoreProtocol::stepAdvancesParser() const { - if(stepRequiresElement()) + if (stepRequiresElement()) return true; - else if(isReady()) + else if (isReady()) return true; return false; } @@ -835,18 +767,18 @@ bool CoreProtocol::stepAdvancesParser() const // all element-needing steps need to be registered here bool CoreProtocol::stepRequiresElement() const { - switch(step) { - case GetFeatures: - case GetTLSProceed: - case GetCompressProceed: - case GetSASLChallenge: - case GetBindResponse: - case GetAuthGetResponse: - case GetAuthSetResponse: - case GetRequest: - case GetSASLResponse: - case GetSMResponse: - return true; + switch (step) { + case GetFeatures: + case GetTLSProceed: + case GetCompressProceed: + case GetSASLChallenge: + case GetBindResponse: + case GetAuthGetResponse: + case GetAuthSetResponse: + case GetRequest: + case GetSASLResponse: + case GetSMResponse: + return true; } return false; } @@ -867,7 +799,7 @@ void CoreProtocol::stringRecv(const QString &s) QString CoreProtocol::defaultNamespace() { - if(server) + if (server) return NS_SERVER; else return NS_CLIENT; @@ -876,7 +808,7 @@ QString CoreProtocol::defaultNamespace() QStringList CoreProtocol::extraNamespaces() { QStringList list; - if(dialback) { + if (dialback) { list += "db"; list += NS_DIALBACK; } @@ -885,33 +817,29 @@ QStringList CoreProtocol::extraNamespaces() void CoreProtocol::handleStreamOpen(const Parser::Event &pe) { - if(isIncoming()) { + if (isIncoming()) { QString ns = pe.nsprefix(); QString db; - if(server) { + if (server) { db = pe.nsprefix("db"); - if(!db.isEmpty()) + if (!db.isEmpty()) dialback = true; } // verify namespace - if((!server && ns != NS_CLIENT) || (server && ns != NS_SERVER) || (dialback && db != NS_DIALBACK)) { + if ((!server && ns != NS_CLIENT) || (server && ns != NS_SERVER) || (dialback && db != NS_DIALBACK)) { delayErrorAndClose(InvalidNamespace); return; } // verify version - if(version.major < 1 && !dialback) { + if (version.major < 1 && !dialback) { delayErrorAndClose(UnsupportedVersion); return; } - } - else { - if(!dialback) { - if(version.major >= 1 && !oldOnly) - old = false; - else - old = true; + } else { + if (!dialback) { + old = version.major < 1 || oldOnly; } } } @@ -932,7 +860,7 @@ void CoreProtocol::elementRecv(const QDomElement &e) bool CoreProtocol::doStep2(const QDomElement &e) { - if(dialback) + if (dialback) return dialbackStep(e); else return normalStep(e); @@ -940,18 +868,16 @@ bool CoreProtocol::doStep2(const QDomElement &e) bool CoreProtocol::isValidStanza(const QDomElement &e) const { - QString s = e.tagName(); + QString s = e.tagName(); Stanza::Kind kind = Stanza::kind(s); - if(e.namespaceURI() == (server ? NS_SERVER : NS_CLIENT) && (kind == Stanza::Message || kind == Stanza::Presence || kind == Stanza::IQ)) - return true; - else - return false; + return e.namespaceURI() == (server ? NS_SERVER : NS_CLIENT) + && (kind == Stanza::Message || kind == Stanza::Presence || kind == Stanza::IQ); } bool CoreProtocol::streamManagementHandleStanza(const QDomElement &e) { QString s = e.tagName(); - if(s == "r") { + if (s == "r") { #ifdef IRIS_SM_DEBUG qDebug() << "Stream Management: [<-?] Received request from server"; #endif @@ -959,7 +885,7 @@ bool CoreProtocol::streamManagementHandleStanza(const QDomElement &e) event = ESend; return true; } else if (s == "a") { - quint32 last_id = e.attribute("h").toULong(); + quint32 last_id = e.attribute("h").toUInt(); #ifdef IRIS_SM_DEBUG qDebug() << "Stream Management: [<--] Received ack response from server with h =" << last_id; #endif @@ -987,11 +913,11 @@ bool CoreProtocol::needSMRequest() bool CoreProtocol::grabPendingItem(const Jid &to, const Jid &from, int type, DBItem *item) { - for(QList::Iterator it = dbpending.begin(); it != dbpending.end(); ++it) { + for (QList::Iterator it = dbpending.begin(); it != dbpending.end(); ++it) { const DBItem &i = *it; - if(i.type == type && i.to.compare(to) && i.from.compare(from)) { + if (i.type == type && i.to.compare(to) && i.from.compare(from)) { const DBItem &i = (*it); - *item = i; + *item = i; dbpending.erase(it); return true; } @@ -1001,44 +927,41 @@ bool CoreProtocol::grabPendingItem(const Jid &to, const Jid &from, int type, DBI bool CoreProtocol::dialbackStep(const QDomElement &e) { - if(step == Start) { + if (step == Start) { setReady(true); - step = Done; + step = Done; event = EReady; return true; } - if(!dbrequests.isEmpty()) { + if (!dbrequests.isEmpty()) { // process a request DBItem i; { QList::Iterator it = dbrequests.begin(); - i = (*it); + i = (*it); dbrequests.erase(it); } QDomElement r; - if(i.type == DBItem::ResultRequest) { + if (i.type == DBItem::ResultRequest) { r = doc.createElementNS(NS_DIALBACK, "db:result"); r.setAttribute("to", i.to.full()); r.setAttribute("from", i.from.full()); r.appendChild(doc.createTextNode(i.key)); dbpending += i; - } - else if(i.type == DBItem::ResultGrant) { + } else if (i.type == DBItem::ResultGrant) { r = doc.createElementNS(NS_DIALBACK, "db:result"); r.setAttribute("to", i.to.full()); r.setAttribute("from", i.from.full()); r.setAttribute("type", i.ok ? "valid" : "invalid"); - if(i.ok) { + if (i.ok) { i.type = DBItem::Validated; dbvalidated += i; - } - else { + } else { // TODO: disconnect after writing element } - } - else if(i.type == DBItem::VerifyRequest) { + } else if (i.type == DBItem::VerifyRequest) { r = doc.createElementNS(NS_DIALBACK, "db:verify"); r.setAttribute("to", i.to.full()); r.setAttribute("from", i.from.full()); @@ -1060,60 +983,54 @@ bool CoreProtocol::dialbackStep(const QDomElement &e) return true; } - if(!e.isNull()) { - if(e.namespaceURI() == NS_DIALBACK) { - if(e.tagName() == "result") { + if (!e.isNull()) { + if (e.namespaceURI() == NS_DIALBACK) { + if (e.tagName() == "result") { Jid to(Jid(e.attribute("to")).domain()); Jid from(Jid(e.attribute("from")).domain()); - if(isIncoming()) { - QString key = e.text(); + if (isIncoming()) { + [[maybe_unused]] QString key = e.text(); // TODO: report event - } - else { - bool ok = (e.attribute("type") == "valid") ? true: false; + } else { + bool ok = e.attribute("type") == "valid"; DBItem i; - if(grabPendingItem(from, to, DBItem::ResultRequest, &i)) { - if(ok) { + if (grabPendingItem(from, to, DBItem::ResultRequest, &i)) { + if (ok) { i.type = DBItem::Validated; - i.ok = true; + i.ok = true; dbvalidated += i; // TODO: report event - } - else { + } else { // TODO: report event } } } - } - else if(e.tagName() == "verify") { - Jid to(Jid(e.attribute("to")).domain()); - Jid from(Jid(e.attribute("from")).domain()); - QString id = e.attribute("id"); - if(isIncoming()) { - QString key = e.text(); + } else if (e.tagName() == "verify") { + Jid to(Jid(e.attribute("to")).domain()); + Jid from(Jid(e.attribute("from")).domain()); + [[maybe_unused]] QString id = e.attribute("id"); + if (isIncoming()) { + [[maybe_unused]] QString key = e.text(); // TODO: report event - } - else { - bool ok = (e.attribute("type") == "valid") ? true: false; + } else { + bool ok = e.attribute("type") == "valid"; DBItem i; - if(grabPendingItem(from, to, DBItem::VerifyRequest, &i)) { - if(ok) { + if (grabPendingItem(from, to, DBItem::VerifyRequest, &i)) { + if (ok) { // TODO: report event - } - else { + } else { // TODO: report event } } } } - } - else { - if(isReady()) { - if(isValidStanza(e)) { + } else { + if (isReady()) { + if (isValidStanza(e)) { // TODO: disconnect if stanza is from unverified sender // TODO: ignore packets from receiving servers stanzaToRecv = e; - event = EStanzaReady; + event = EStanzaReady; return true; } } @@ -1127,33 +1044,30 @@ bool CoreProtocol::dialbackStep(const QDomElement &e) bool CoreProtocol::normalStep(const QDomElement &e) { - if(step == Start) { - if(isIncoming()) { + if (step == Start) { + if (isIncoming()) { need = NSASLMechs; step = SendFeatures; return false; - } - else { - if(old) { - if(doAuth) + } else { + if (old) { + if (doAuth) step = HandleAuthGet; else return loginComplete(); - } - else + } else step = GetFeatures; return processStep(); } - } - else if(step == HandleFeatures) { + } else if (step == HandleFeatures) { // deal with TLS? - if(doTLS && !tls_started && !sasl_authed && features.tls_supported) { + if (doTLS && !tls_started && !sasl_authed && features.tls_supported) { QDomElement e = doc.createElementNS(NS_TLS, "starttls"); send(e, true); event = ESend; - step = GetTLSProceed; + step = GetTLSProceed; return true; } @@ -1162,28 +1076,29 @@ bool CoreProtocol::normalStep(const QDomElement &e) return loginComplete(); // Deal with compression - if (doCompress && !compress_started && features.compress_supported && features.compression_mechs.contains("zlib")) { + if (doCompress && !compress_started && features.compress_supported + && features.compression_mechs.contains("zlib")) { QDomElement e = doc.createElementNS(NS_COMPRESS_PROTOCOL, "compress"); QDomElement m = doc.createElementNS(NS_COMPRESS_PROTOCOL, "method"); m.appendChild(doc.createTextNode("zlib")); e.appendChild(m); - send(e,true); + send(e, true); event = ESend; - step = GetCompressProceed; + step = GetCompressProceed; return true; } // deal with SASL? - if(!sasl_authed) { - if(!features.sasl_supported) { + if (!sasl_authed) { + if (!features.sasl_supported) { // SASL MUST be supported - //event = EError; - //errorCode = ErrProtocol; - //return true; + // event = EError; + // errorCode = ErrProtocol; + // return true; // Fall back on auth for non-compliant servers step = HandleAuthGet; - old = true; + old = true; return true; } @@ -1195,18 +1110,17 @@ bool CoreProtocol::normalStep(const QDomElement &e) return false; } - if(server) { + if (server) { return loginComplete(); - } - else { - if(!doBinding) + } else { + if (!doBinding) return loginComplete(); } // deal with bind - if(!features.bind_supported) { + if (!features.bind_supported) { // bind MUST be supported - event = EError; + event = EError; errorCode = ErrProtocol; return true; } @@ -1222,7 +1136,7 @@ bool CoreProtocol::normalStep(const QDomElement &e) // request specific resource? QString resource = jid_.resource(); - if(!resource.isEmpty()) { + if (!resource.isEmpty()) { QDomElement r = doc.createElement("resource"); r.appendChild(doc.createTextNode(jid_.resource())); b.appendChild(r); @@ -1232,14 +1146,13 @@ bool CoreProtocol::normalStep(const QDomElement &e) send(e); event = ESend; - step = GetBindResponse; + step = GetBindResponse; return true; } - } - else if(step == GetSASLFirst) { + } else if (step == GetSASLFirst) { QDomElement e = doc.createElementNS(NS_SASL, "auth"); e.setAttribute("mechanism", sasl_mech); - if(!sasl_step.isEmpty()) { + if (!sasl_step.isEmpty()) { #ifdef XMPP_TEST TD::msg(QString("SASL OUT: [%1]").arg(printArray(sasl_step))); #endif @@ -1248,38 +1161,34 @@ bool CoreProtocol::normalStep(const QDomElement &e) send(e, true); event = ESend; - step = GetSASLChallenge; + step = GetSASLChallenge; return true; - } - else if(step == GetSASLNext) { - if(isIncoming()) { - if(sasl_authed) { + } else if (step == GetSASLNext) { + if (isIncoming()) { + if (sasl_authed) { QDomElement e = doc.createElementNS(NS_SASL, "success"); send(e, true); event = ESend; - step = IncHandleSASLSuccess; + step = IncHandleSASLSuccess; return true; - } - else { - QByteArray stepData = sasl_step; - QDomElement e = doc.createElementNS(NS_SASL, "challenge"); - if(!stepData.isEmpty()) + } else { + QByteArray stepData = sasl_step; + QDomElement e = doc.createElementNS(NS_SASL, "challenge"); + if (!stepData.isEmpty()) e.appendChild(doc.createTextNode(QCA::Base64().arrayToString(stepData))); send(e, true); event = ESend; - step = GetSASLResponse; + step = GetSASLResponse; return true; } - } - else { + } else { // already authed? then ignore last client step // (this happens if "additional data with success" // is used) - if(sasl_authed) - { + if (sasl_authed) { event = ESASLSuccess; - step = HandleSASLSuccess; + step = HandleSASLSuccess; return true; } @@ -1288,22 +1197,20 @@ bool CoreProtocol::normalStep(const QDomElement &e) TD::msg(QString("SASL OUT: [%1]").arg(printArray(sasl_step))); #endif QDomElement e = doc.createElementNS(NS_SASL, "response"); - if(!stepData.isEmpty()) + if (!stepData.isEmpty()) e.appendChild(doc.createTextNode(QCA::Base64().arrayToString(stepData))); send(e, true); event = ESend; - step = GetSASLChallenge; + step = GetSASLChallenge; return true; } - } - else if(step == HandleSASLSuccess) { - need = NSASLLayer; + } else if (step == HandleSASLSuccess) { + need = NSASLLayer; spare = resetStream(); - step = Start; + step = Start; return false; - } - else if(step == HandleAuthGet) { + } else if (step == HandleAuthGet) { QDomElement e = doc.createElement("iq"); e.setAttribute("to", to); e.setAttribute("type", "get"); @@ -1316,10 +1223,9 @@ bool CoreProtocol::normalStep(const QDomElement &e) send(e); event = ESend; - step = GetAuthGetResponse; + step = GetAuthGetResponse; return true; - } - else if(step == HandleAuthSet) { + } else if (step == HandleAuthSet) { QDomElement e = doc.createElement("iq"); e.setAttribute("to", to); e.setAttribute("type", "set"); @@ -1329,16 +1235,15 @@ bool CoreProtocol::normalStep(const QDomElement &e) u.appendChild(doc.createTextNode(jid_.node())); q.appendChild(u); QDomElement p; - if(digest) { + if (digest) { // need SHA1 here - //if(!QCA::isSupported(QCA::CAP_SHA1)) + // if(!QCA::isSupported(QCA::CAP_SHA1)) // QCA::insertProvider(createProviderHash()); - p = doc.createElement("digest"); + p = doc.createElement("digest"); QByteArray cs = id.toUtf8() + password.toUtf8(); p.appendChild(doc.createTextNode(QCA::Hash("sha1").hashToString(cs))); - } - else { + } else { p = doc.createElement("password"); p.appendChild(doc.createTextNode(password)); } @@ -1350,26 +1255,25 @@ bool CoreProtocol::normalStep(const QDomElement &e) send(e, true); event = ESend; - step = GetAuthSetResponse; + step = GetAuthSetResponse; return true; } // server - else if(step == SendFeatures) { + else if (step == SendFeatures) { QDomElement f = doc.createElementNS(NS_ETHERX, "stream:features"); - if(!tls_started && !sasl_authed) { // don't offer tls if we are already sasl'd + if (!tls_started && !sasl_authed) { // don't offer tls if we are already sasl'd QDomElement tls = doc.createElementNS(NS_TLS, "starttls"); f.appendChild(tls); } - if(sasl_authed) { - if(!server) { + if (sasl_authed) { + if (!server) { QDomElement bind = doc.createElementNS(NS_BIND, "bind"); f.appendChild(bind); } - } - else { + } else { QDomElement mechs = doc.createElementNS(NS_SASL, "mechanisms"); - foreach (const QString & it, sasl_mechlist) { + for (const QString &it : std::as_const(sasl_mechlist)) { QDomElement m = doc.createElement("mechanism"); m.appendChild(doc.createTextNode(it)); mechs.appendChild(m); @@ -1379,31 +1283,30 @@ bool CoreProtocol::normalStep(const QDomElement &e) send(f); event = ESend; - step = GetRequest; + step = GetRequest; return true; } // server - else if(step == HandleTLS) { + else if (step == HandleTLS) { tls_started = true; - need = NStartTLS; - spare = resetStream(); - step = Start; + need = NStartTLS; + spare = resetStream(); + step = Start; return false; } // server - else if(step == IncHandleSASLSuccess) { + else if (step == IncHandleSASLSuccess) { event = ESASLSuccess; spare = resetStream(); - step = Start; + step = Start; printf("sasl success\n"); return true; - } - else if(step == GetFeatures) { + } else if (step == GetFeatures) { // we are waiting for stream features - if(e.namespaceURI() == NS_ETHERX && e.tagName() == QLatin1String("features")) { + if (e.namespaceURI() == NS_ETHERX && e.tagName() == QLatin1String("features")) { // extract features - StreamFeatures f; - QDomNodeList nl = e.childNodes(); + StreamFeatures f; + QDomNodeList nl = e.childNodes(); QList unhandled; for (int i = 0; i < nl.size(); i++) { QDomElement c = nl.item(i).toElement(); @@ -1412,18 +1315,18 @@ bool CoreProtocol::normalStep(const QDomElement &e) } if (c.localName() == QLatin1String("starttls") && c.namespaceURI() == NS_TLS) { f.tls_supported = true; - f.tls_required = c.elementsByTagNameNS(NS_TLS, QLatin1String("required")).count() > 0; + f.tls_required = c.elementsByTagNameNS(NS_TLS, QLatin1String("required")).count() > 0; } else if (c.localName() == QLatin1String("mechanisms") && c.namespaceURI() == NS_SASL) { f.sasl_supported = true; - QDomNodeList l = c.elementsByTagNameNS(NS_SASL, QLatin1String("mechanism")); - for(int n = 0; n < l.count(); ++n) + QDomNodeList l = c.elementsByTagNameNS(NS_SASL, QLatin1String("mechanism")); + for (int n = 0; n < l.count(); ++n) f.sasl_mechs += l.item(n).toElement().text(); } else if (c.localName() == QLatin1String("compression") && c.namespaceURI() == NS_COMPRESS_FEATURE) { f.compress_supported = true; - QDomNodeList l = c.elementsByTagNameNS(NS_COMPRESS_FEATURE, QLatin1String("method")); - for(int n = 0; n < l.count(); ++n) + QDomNodeList l = c.elementsByTagNameNS(NS_COMPRESS_FEATURE, QLatin1String("method")); + for (int n = 0; n < l.count(); ++n) f.compression_mechs += l.item(n).toElement().text(); } else if (c.localName() == QLatin1String("bind") && c.namespaceURI() == NS_BIND) { @@ -1431,7 +1334,7 @@ bool CoreProtocol::normalStep(const QDomElement &e) } else if (c.localName() == QLatin1String("hosts") && c.namespaceURI() == NS_HOSTS) { QDomNodeList l = c.elementsByTagNameNS(NS_HOSTS, QLatin1String("host")); - for(int n = 0; n < l.count(); ++n) + for (int n = 0; n < l.count(); ++n) f.hosts += l.item(n).toElement().text(); hosts += f.hosts; @@ -1441,7 +1344,7 @@ bool CoreProtocol::normalStep(const QDomElement &e) } else if (c.localName() == QLatin1String("session") && c.namespaceURI() == NS_SESSION) { f.session_supported = true; - f.session_required = c.elementsByTagName(QLatin1String("optional")).count() == 0; + f.session_required = c.elementsByTagName(QLatin1String("optional")).count() == 0; // more details https://tools.ietf.org/html/draft-cridland-xmpp-session-01 } else { @@ -1449,341 +1352,315 @@ bool CoreProtocol::normalStep(const QDomElement &e) } } - if(f.tls_supported) { + if (f.tls_supported) { #ifdef XMPP_TEST QString s = "STARTTLS is available"; - if(f.tls_required) + if (f.tls_required) s += " (required)"; TD::msg(s); #endif } - if(f.sasl_supported) { + if (f.sasl_supported) { #ifdef XMPP_TEST QString s = "SASL mechs:"; - for(QStringList::ConstIterator it = f.sasl_mechs.begin(); it != f.sasl_mechs.end(); ++it) - s += QString(" [%1]").arg((*it)); + for (const auto &saslMech : std::as_const(f.sasl_mechs)) + s += QString(" [%1]").arg(saslMech); TD::msg(s); #endif } - if(f.compress_supported) { + if (f.compress_supported) { #ifdef XMPP_TEST QString s = "Compression mechs:"; - for(QStringList::ConstIterator it = f.compression_mechs.begin(); it != f.compression_mechs.end(); ++it) - s += QString(" [%1]").arg((*it)); + for (const auto &comprMech : std::as_const(f.compression_mechs)) + s += QString(" [%1]").arg(comprMech); TD::msg(s); #endif } - event = EFeatures; - features = f; + event = EFeatures; + features = f; unhandledFeatures = unhandled; - step = HandleFeatures; + step = HandleFeatures; return true; - } - else { + } else { // ignore } - } - else if(step == GetTLSProceed) { + } else if (step == GetTLSProceed) { // waiting for proceed to starttls - if(e.namespaceURI() == NS_TLS) { - if(e.tagName() == "proceed") { + if (e.namespaceURI() == NS_TLS) { + if (e.tagName() == "proceed") { #ifdef XMPP_TEST TD::msg("Server wants us to proceed with ssl handshake"); #endif tls_started = true; - need = NStartTLS; - spare = resetStream(); - step = Start; + need = NStartTLS; + spare = resetStream(); + step = Start; return false; - } - else if(e.tagName() == "failure") { - event = EError; + } else if (e.tagName() == "failure") { + event = EError; errorCode = ErrStartTLS; return true; - } - else { - event = EError; + } else { + event = EError; errorCode = ErrProtocol; return true; } - } - else { + } else { // ignore } - } - else if(step == GetCompressProceed) { + } else if (step == GetCompressProceed) { // waiting for proceed to compression - if(e.namespaceURI() == NS_COMPRESS_PROTOCOL) { - if(e.tagName() == "compressed") { + if (e.namespaceURI() == NS_COMPRESS_PROTOCOL) { + if (e.tagName() == "compressed") { #ifdef XMPP_TEST TD::msg("Server wants us to proceed with compression"); #endif compress_started = true; - need = NCompress; - spare = resetStream(); - step = Start; + need = NCompress; + spare = resetStream(); + step = Start; return false; - } - else if(e.tagName() == "failure") { - event = EError; + } else if (e.tagName() == "failure") { + event = EError; errorCode = ErrCompress; return true; - } - else { - event = EError; + } else { + event = EError; errorCode = ErrProtocol; return true; } - } - else { + } else { // ignore } - } - else if(step == GetSASLChallenge) { + } else if (step == GetSASLChallenge) { // waiting for sasl challenge/success/fail - if(e.namespaceURI() == NS_SASL) { - if(e.tagName() == "challenge") { + if (e.namespaceURI() == NS_SASL) { + if (e.tagName() == "challenge") { QByteArray a = QCA::Base64().stringToArray(e.text()).toByteArray(); #ifdef XMPP_TEST TD::msg(QString("SASL IN: [%1]").arg(printArray(a))); #endif sasl_step = a; - need = NSASLNext; - step = GetSASLNext; + need = NSASLNext; + step = GetSASLNext; return false; - } - else if(e.tagName() == "success") { + } else if (e.tagName() == "success") { QString str = e.text(); // "additional data with success" ? - if(!str.isEmpty()) - { + if (!str.isEmpty()) { QByteArray a = QCA::Base64().stringToArray(str).toByteArray(); - sasl_step = a; - sasl_authed = true; - need = NSASLNext; - step = GetSASLNext; + sasl_step = a; + sasl_authed = true; + need = NSASLNext; + step = GetSASLNext; return false; } sasl_authed = true; - event = ESASLSuccess; - step = HandleSASLSuccess; + event = ESASLSuccess; + step = HandleSASLSuccess; return true; - } - else if(e.tagName() == "failure") { + } else if (e.tagName() == "failure") { QDomElement t = firstChildElement(e); - if(t.isNull() || t.namespaceURI() != NS_SASL) + if (t.isNull() || t.namespaceURI() != NS_SASL) errCond = -1; else errCond = stringToSASLCond(t.tagName()); // handle text elements - auto nodes = e.elementsByTagNameNS(NS_SASL, QLatin1String("text")); + auto nodes = e.elementsByTagNameNS(NS_SASL, QLatin1String("text")); decltype(errLangText) lt; for (int i = 0; i < nodes.count(); i++) { - auto e = nodes.item(i).toElement(); + auto e = nodes.item(i).toElement(); QString lang = e.attributeNS(NS_SASL, "lang", ""); lt.insert(lang, e.text()); } errLangText = lt; - event = EError; - errorCode = ErrAuth; + event = EError; + errorCode = ErrAuth; return true; - } - else { - event = EError; + } else { + event = EError; errorCode = ErrProtocol; return true; } } - } - else if(step == GetBindResponse) { - if(e.namespaceURI() == NS_CLIENT && e.tagName() == "iq") { + } else if (step == GetBindResponse) { + if (e.namespaceURI() == NS_CLIENT && e.tagName() == "iq") { QString type(e.attribute("type")); QString id(e.attribute("id")); - if(id == "bind_1" && (type == "result" || type == "error")) { - if(type == "result") { + if (id == "bind_1" && (type == "result" || type == "error")) { + if (type == "result") { QDomElement b = e.elementsByTagNameNS(NS_BIND, "bind").item(0).toElement(); - Jid j; - if(!b.isNull()) { + Jid j; + if (!b.isNull()) { QDomElement je = e.elementsByTagName("jid").item(0).toElement(); - j = je.text(); + j = je.text(); } - if(!j.isValid()) { - event = EError; + if (!j.isValid()) { + event = EError; errorCode = ErrProtocol; return true; } jid_ = j; return loginComplete(); - } - else { + } else { errCond = -1; QDomElement err = e.elementsByTagNameNS(NS_CLIENT, "error").item(0).toElement(); - if(!err.isNull()) { + if (!err.isNull()) { // get error condition QDomNodeList nl = err.childNodes(); - QDomElement t; - for(int n = 0; n < nl.count(); ++n) { + QDomElement t; + for (int n = 0; n < nl.count(); ++n) { QDomNode i = nl.item(n); - if(i.isElement()) { + if (i.isElement()) { t = i.toElement(); break; } } - if(!t.isNull() && t.namespaceURI() == NS_STANZAS) { + if (!t.isNull() && t.namespaceURI() == NS_STANZAS) { QString cond = t.tagName(); - if(cond == "not-allowed") + if (cond == "not-allowed") errCond = BindNotAllowed; - else if(cond == "conflict") + else if (cond == "conflict") errCond = BindConflict; } } - event = EError; + event = EError; errorCode = ErrBind; return true; } - } - else { + } else { // ignore } - } - else { + } else { // ignore } - } - else if(step == GetAuthGetResponse) { + } else if (step == GetAuthGetResponse) { // waiting for an iq - if(e.namespaceURI() == NS_CLIENT && e.tagName() == "iq") { - Jid from(e.attribute("from")); + if (e.namespaceURI() == NS_CLIENT && e.tagName() == "iq") { + Jid from(e.attribute("from")); QString type(e.attribute("type")); QString id(e.attribute("id")); bool okfrom = (from.isEmpty() || from.compare(Jid(to))); - if(okfrom && id == "auth_1" && (type == "result" || type == "error")) { - if(type == "result") { + if (okfrom && id == "auth_1" && (type == "result" || type == "error")) { + if (type == "result") { QDomElement q = e.elementsByTagNameNS("jabber:iq:auth", "query").item(0).toElement(); - if(q.isNull() || q.elementsByTagName("username").item(0).isNull() || q.elementsByTagName("resource").item(0).isNull()) { - event = EError; + if (q.isNull() || q.elementsByTagName("username").item(0).isNull() + || q.elementsByTagName("resource").item(0).isNull()) { + event = EError; errorCode = ErrProtocol; return true; } - bool plain_supported = !q.elementsByTagName("password").item(0).isNull(); + bool plain_supported = !q.elementsByTagName("password").item(0).isNull(); bool digest_supported = !q.elementsByTagName("digest").item(0).isNull(); - if(!digest_supported && !plain_supported) { - event = EError; + if (!digest_supported && !plain_supported) { + event = EError; errorCode = ErrProtocol; return true; } // plain text not allowed? - if(!digest_supported && !allowPlain) { - event = EError; + if (!digest_supported && !allowPlain) { + event = EError; errorCode = ErrPlain; return true; } digest = digest_supported; - need = NPassword; - step = HandleAuthSet; + need = NPassword; + step = HandleAuthSet; return false; - } - else { + } else { errCond = getOldErrorCode(e); - event = EError; + event = EError; errorCode = ErrAuth; return true; } - } - else { + } else { // ignore } - } - else { + } else { // ignore } - } - else if(step == GetAuthSetResponse) { + } else if (step == GetAuthSetResponse) { // waiting for an iq - if(e.namespaceURI() == NS_CLIENT && e.tagName() == "iq") { - Jid from(e.attribute("from")); + if (e.namespaceURI() == NS_CLIENT && e.tagName() == "iq") { + Jid from(e.attribute("from")); QString type(e.attribute("type")); QString id(e.attribute("id")); bool okfrom = (from.isEmpty() || from.compare(Jid(to))); - if(okfrom && id == "auth_2" && (type == "result" || type == "error")) { - if(type == "result") { + if (okfrom && id == "auth_2" && (type == "result" || type == "error")) { + if (type == "result") { return loginComplete(); - } - else { + } else { errCond = getOldErrorCode(e); - event = EError; + event = EError; errorCode = ErrAuth; return true; } - } - else { + } else { // ignore } - } - else { + } else { // ignore } } // server - else if(step == GetRequest) { + else if (step == GetRequest) { printf("get request: [%s], %s\n", e.namespaceURI().toLatin1().data(), e.tagName().toLatin1().data()); - if(e.namespaceURI() == NS_TLS && e.localName() == "starttls") { + if (e.namespaceURI() == NS_TLS && e.localName() == "starttls") { // TODO: don't let this be done twice QDomElement e = doc.createElementNS(NS_TLS, "proceed"); send(e, true); event = ESend; - step = HandleTLS; + step = HandleTLS; return true; } - if(e.namespaceURI() == NS_SASL) { - if(e.localName() == "auth") { - if(sasl_started) { + if (e.namespaceURI() == NS_SASL) { + if (e.localName() == "auth") { + if (sasl_started) { // TODO printf("error\n"); return false; } sasl_started = true; - sasl_mech = e.attribute("mechanism"); + sasl_mech = e.attribute("mechanism"); // TODO: if child text missing, don't pass it sasl_step = QCA::Base64().stringToArray(e.text()).toByteArray(); - need = NSASLFirst; - step = GetSASLNext; + need = NSASLFirst; + step = GetSASLNext; return false; - } - else { + } else { // TODO printf("unknown sasl tag\n"); return false; } } - if(e.namespaceURI() == NS_CLIENT && e.tagName() == "iq") { + if (e.namespaceURI() == NS_CLIENT && e.tagName() == "iq") { QDomElement b = e.elementsByTagNameNS(NS_BIND, "bind").item(0).toElement(); - if(!b.isNull()) { - QDomElement res = b.elementsByTagName("resource").item(0).toElement(); - QString resource = res.text(); + if (!b.isNull()) { + QDomElement res = b.elementsByTagName("resource").item(0).toElement(); + QString resource = res.text(); QDomElement r = doc.createElement("iq"); r.setAttribute("type", "result"); r.setAttribute("id", e.attribute("id")); QDomElement bind = doc.createElementNS(NS_BIND, "bind"); - QDomElement jid = doc.createElement("jid"); - Jid j = QString(user + '@' + host + '/' + resource); + QDomElement jid = doc.createElement("jid"); + Jid j = QString(user + '@' + host + '/' + resource); jid.appendChild(doc.createTextNode(j.full())); bind.appendChild(jid); r.appendChild(bind); @@ -1792,21 +1669,18 @@ bool CoreProtocol::normalStep(const QDomElement &e) event = ESend; // TODO return true; - } - else { + } else { // TODO } } - } - else if(step == GetSASLResponse) { - if(e.namespaceURI() == NS_SASL && e.localName() == "response") { + } else if (step == GetSASLResponse) { + if (e.namespaceURI() == NS_SASL && e.localName() == "response") { sasl_step = QCA::Base64().stringToArray(e.text()).toByteArray(); - need = NSASLNext; - step = GetSASLNext; + need = NSASLNext; + step = GetSASLNext; return false; } - } - else if(step == GetSMResponse) { + } else if (step == GetSMResponse) { #ifdef IRIS_SM_DEBUG qWarning() << "HandleSM: step"; #endif @@ -1824,24 +1698,25 @@ bool CoreProtocol::normalStep(const QDomElement &e) #endif QString location = e.attribute("location").trimmed(); if (!location.isEmpty()) { - int port_off = 0; - QStringRef sm_host; - int sm_port = 0; + int port_off = 0; + QStringView sm_host; + int sm_port = 0; + auto location_view = QStringView { location }; if (location.startsWith('[')) { // ipv6 port_off = location.indexOf(']'); if (port_off != -1) { // looks valid - sm_host = location.midRef(1, port_off - 1); + sm_host = location_view.mid(1, port_off - 1); if (location.length() > port_off + 2 && location.at(port_off + 1) == ':') - sm_port = location.mid(port_off + 2).toUInt(); + sm_port = location_view.mid(port_off + 2).toUInt(); } } if (port_off == 0) { port_off = location.indexOf(':'); if (port_off != -1) { - sm_host = location.leftRef(port_off); - sm_port = location.mid(port_off + 1).toUInt(); + sm_host = location_view.left(port_off); + sm_port = location_view.mid(port_off + 1).toUInt(); } else { - sm_host = location.midRef(0); + sm_host = location_view.mid(0); } } sm.setLocation(sm_host.toString(), sm_port); @@ -1849,11 +1724,11 @@ bool CoreProtocol::normalStep(const QDomElement &e) } // else resumption is not supported on this server needTimer(SM_TIMER_INTERVAL_SECS); event = EReady; - step = Done; + step = Done; return true; } else if (e.localName() == "resumed") { - sm.resume(e.attribute("h").toULong()); - while(true) { + sm.resume(e.attribute("h").toUInt()); + while (true) { QDomElement st = sm.getUnacknowledgedStanza(); if (st.isNull()) break; @@ -1861,12 +1736,12 @@ bool CoreProtocol::normalStep(const QDomElement &e) } needTimer(SM_TIMER_INTERVAL_SECS); event = EReady; - step = Done; + step = Done; return true; } else if (e.localName() == "failed") { if (sm.state().isResumption()) { // tried to resume? ok, then try to just enable sm.state().resumption_id.clear(); - //step = HandleFeatures; + // step = HandleFeatures; event = ESMResumeFailed; return true; } @@ -1874,11 +1749,11 @@ bool CoreProtocol::normalStep(const QDomElement &e) } } - if(isReady()) { + if (isReady()) { if (!e.isNull()) { - if(isValidStanza(e)) { + if (isValidStanza(e)) { stanzaToRecv = e; - event = EStanzaReady; + event = EStanzaReady; setIncomingAsExternal(); return true; } else if (sm.isActive()) { diff --git a/src/xmpp/xmpp-core/protocol.h b/src/xmpp/xmpp-core/protocol.h index 764be73b..2db6352f 100644 --- a/src/xmpp/xmpp-core/protocol.h +++ b/src/xmpp/xmpp-core/protocol.h @@ -20,369 +20,358 @@ #ifndef PROTOCOL_H #define PROTOCOL_H -#include +#include "sm.h" +#include "xmlprotocol.h" +#include "xmpp.h" + #include +#include #include #include -#include "xmlprotocol.h" -#include "xmpp.h" -#include "sm.h" - -#define NS_ETHERX "http://etherx.jabber.org/streams" -#define NS_CLIENT "jabber:client" -#define NS_SERVER "jabber:server" +#define NS_ETHERX "http://etherx.jabber.org/streams" +#define NS_CLIENT "jabber:client" +#define NS_SERVER "jabber:server" #define NS_DIALBACK "jabber:server:dialback" -#define NS_STREAMS "urn:ietf:params:xml:ns:xmpp-streams" -#define NS_TLS "urn:ietf:params:xml:ns:xmpp-tls" -#define NS_SASL "urn:ietf:params:xml:ns:xmpp-sasl" -#define NS_SESSION "urn:ietf:params:xml:ns:xmpp-session" -#define NS_STANZAS "urn:ietf:params:xml:ns:xmpp-stanzas" -#define NS_BIND "urn:ietf:params:xml:ns:xmpp-bind" -#define NS_CAPS "http://jabber.org/protocol/caps" +#define NS_STREAMS "urn:ietf:params:xml:ns:xmpp-streams" +#define NS_TLS "urn:ietf:params:xml:ns:xmpp-tls" +#define NS_SASL "urn:ietf:params:xml:ns:xmpp-sasl" +#define NS_SESSION "urn:ietf:params:xml:ns:xmpp-session" +#define NS_STANZAS "urn:ietf:params:xml:ns:xmpp-stanzas" +#define NS_BIND "urn:ietf:params:xml:ns:xmpp-bind" +#define NS_CAPS "http://jabber.org/protocol/caps" +#define NS_CAPS_OPTIMIZE "http://jabber.org/protocol/caps#optimize" #define NS_COMPRESS_FEATURE "http://jabber.org/features/compress" #define NS_COMPRESS_PROTOCOL "http://jabber.org/protocol/compress" -#define NS_HOSTS "http://barracuda.com/xmppextensions/hosts" +#define NS_HOSTS "http://barracuda.com/xmppextensions/hosts" + +namespace XMPP { +class Version { +public: + Version(int maj = 0, int min = 0); + + int major, minor; +}; + +class StreamFeatures { +public: + StreamFeatures(); + + bool tls_supported, sasl_supported, bind_supported, compress_supported; + bool tls_required; + bool sm_supported; + bool session_supported; + bool session_required; + QStringList sasl_mechs; + QStringList compression_mechs; + QStringList hosts; +}; + +class BasicProtocol : public XmlProtocol { +public: + // xmpp 1.0 error conditions // rfc6120 + enum SASLCond { + Aborted, // server confirms auth abort + AccountDisabled, // account temporrily disabled + CredentialsExpired, // credential expired + EncryptionRequired, // can't use mech without TLS + IncorrectEncoding, // Incorrect encoding. should not happen + InvalidAuthzid, // bad input JID + InvalidMech, // bad mechanism + MalformedRequest, // malformded request + MechTooWeak, // can't use mech with this authzid + NotAuthorized, // bad user, bad password, bad creditials + TemporaryAuthFailure, // please try again later! + }; + enum StreamCond { + BadFormat, + BadNamespacePrefix, + Conflict, + ConnectionTimeout, + HostGone, + HostUnknown, + ImproperAddressing, + InternalServerError, + InvalidFrom, + InvalidNamespace, + InvalidXml, + StreamNotAuthorized, + PolicyViolation, + RemoteConnectionFailed, + StreamReset, + ResourceConstraint, + RestrictedXml, + SeeOtherHost, + SystemShutdown, + UndefinedCondition, + UnsupportedEncoding, + UnsupportedStanzaType, + UnsupportedVersion, + NotWellFormed + }; + enum BindCond { BindBadRequest, BindNotAllowed, BindConflict }; + + // extend the XmlProtocol enums + enum Need { + NSASLMechs = XmlProtocol::NCustom, // need SASL mechlist + NStartTLS, // need to switch on TLS layer + NCompress, // need to switch on compression layer + NSASLFirst, // need SASL first step + NSASLNext, // need SASL next step + NSASLLayer, // need to switch on SASL layer + NCustom = XmlProtocol::NCustom + 10 + }; + enum Event { + EFeatures = XmlProtocol::ECustom, // breakpoint after features packet is received + ESASLSuccess, // breakpoint after successful sasl auth + EStanzaReady, // a stanza was received + EStanzaSent, // a stanza was sent + EReady, // stream is ready for stanza use + EAck, // received SM ack response from server + ECustom = XmlProtocol::ECustom + 10 + }; + enum Error { + ErrProtocol = XmlProtocol::ErrCustom, // there was an error in the xmpp-core protocol exchange + ErrStream, // , see errCond, errText, and errAppSpec for details + ErrStartTLS, // server refused starttls + ErrCompress, // server refused compression + ErrAuth, // authorization error. errCond holds sasl condition (or numeric code for old-protocol) + ErrBind, // server refused resource bind + ErrCustom = XmlProtocol::ErrCustom + 10 + }; -namespace XMPP -{ - class Version - { - public: - Version(int maj=0, int min=0); + BasicProtocol(); + ~BasicProtocol(); + + void reset(); + + // for outgoing xml + QDomDocument doc; + + // sasl-related + QString saslMech() const; + QByteArray saslStep() const; + void setSASLMechList(const QStringList &list); + void setSASLFirst(const QString &mech, const QByteArray &step); + void setSASLNext(const QByteArray &step); + void setSASLAuthed(); + + // send / recv + void sendStanza(const QDomElement &e); + void sendDirect(const QString &s); + void sendWhitespace(); + void clearSendQueue(); + QDomElement recvStanza(); + + // shutdown + void shutdown(); + void shutdownWithError(int cond, const QString &otherHost = ""); + + // information + QString to, from, id, lang; + Version version; + + // error output + int errCond; + QString errText; + QHash errLangText; + QDomElement errAppSpec; + QString otherHost; + + QByteArray spare; // filled with unprocessed data on NStartTLS and NSASLLayer + + bool isReady() const; + + enum { TypeElement, TypeStanza, TypeDirect, TypePing }; + +protected: + static int stringToSASLCond(const QString &s); + static int stringToStreamCond(const QString &s); + static QString saslCondToString(int); + static QString streamCondToString(int); + + void send(const QDomElement &e, bool clip = false); + void sendUrgent(const QDomElement &e, bool clip = false); + void sendStreamError(int cond, const QString &text = "", const QDomElement &appSpec = QDomElement()); + void sendStreamError(const QString &text); // old-style + + bool errorAndClose(int cond, const QString &text = "", const QDomElement &appSpec = QDomElement()); + bool error(int code); + void delayErrorAndClose(int cond, const QString &text = "", const QDomElement &appSpec = QDomElement()); + void delayError(int code); + + // reimplemented + QDomElement docElement(); + void handleDocOpen(const Parser::Event &pe); + bool handleError(); + bool handleCloseFinished(); + bool doStep(const QDomElement &e); + void itemWritten(int id, int size); + + virtual QString defaultNamespace(); + virtual QStringList extraNamespaces(); // stringlist: prefix,uri,prefix,uri, [...] + virtual void handleStreamOpen(const Parser::Event &pe); + virtual bool doStep2(const QDomElement &e) = 0; + + void setReady(bool b); + + QString sasl_mech; + QStringList sasl_mechlist; + QByteArray sasl_step; + bool sasl_authed; + + QDomElement stanzaToRecv; + +private: + struct SASLCondEntry { + const char *str; + int cond; + }; + static SASLCondEntry saslCondTable[]; - int major, minor; + struct StreamCondEntry { + const char *str; + int cond; }; + static StreamCondEntry streamCondTable[]; - class StreamFeatures - { - public: - StreamFeatures(); - - bool tls_supported, sasl_supported, bind_supported, compress_supported; - bool tls_required; - bool sm_supported; - bool session_supported; - bool session_required; - QStringList sasl_mechs; - QStringList compression_mechs; - QStringList hosts; + struct SendItem { + QDomElement stanzaToSend; + QString stringToSend; + bool doWhitespace; }; + QList sendList; - class BasicProtocol : public XmlProtocol - { - public: - // xmpp 1.0 error conditions // rfc6120 - enum SASLCond { - Aborted, // server confirms auth abort - AccountDisabled, // account temporrily disabled - CredentialsExpired, // credential expired - EncryptionRequired, // can't use mech without TLS - IncorrectEncoding, // Incorrect encoding. should not happen - InvalidAuthzid, // bad input JID - InvalidMech, // bad mechanism - MalformedRequest, // malformded request - MechTooWeak, // can't use mech with this authzid - NotAuthorized, // bad user, bad password, bad creditials - TemporaryAuthFailure, // please try again later! - }; - enum StreamCond { - BadFormat, - BadNamespacePrefix, - Conflict, - ConnectionTimeout, - HostGone, - HostUnknown, - ImproperAddressing, - InternalServerError, - InvalidFrom, - InvalidNamespace, - InvalidXml, - StreamNotAuthorized, - PolicyViolation, - RemoteConnectionFailed, - StreamReset, - ResourceConstraint, - RestrictedXml, - SeeOtherHost, - SystemShutdown, - UndefinedCondition, - UnsupportedEncoding, - UnsupportedStanzaType, - UnsupportedVersion, - NotWellFormed - }; - enum BindCond { - BindBadRequest, - BindNotAllowed, - BindConflict - }; - - // extend the XmlProtocol enums - enum Need { - NSASLMechs = XmlProtocol::NCustom, // need SASL mechlist - NStartTLS, // need to switch on TLS layer - NCompress, // need to switch on compression layer - NSASLFirst, // need SASL first step - NSASLNext, // need SASL next step - NSASLLayer, // need to switch on SASL layer - NCustom = XmlProtocol::NCustom+10 - }; - enum Event { - EFeatures = XmlProtocol::ECustom, // breakpoint after features packet is received - ESASLSuccess, // breakpoint after successful sasl auth - EStanzaReady, // a stanza was received - EStanzaSent, // a stanza was sent - EReady, // stream is ready for stanza use - EAck, // received SM ack response from server - ECustom = XmlProtocol::ECustom+10 - }; - enum Error { - ErrProtocol = XmlProtocol::ErrCustom, // there was an error in the xmpp-core protocol exchange - ErrStream, // , see errCond, errText, and errAppSpec for details - ErrStartTLS, // server refused starttls - ErrCompress, // server refused compression - ErrAuth, // authorization error. errCond holds sasl condition (or numeric code for old-protocol) - ErrBind, // server refused resource bind - ErrCustom = XmlProtocol::ErrCustom+10 - }; - - BasicProtocol(); - ~BasicProtocol(); - - void reset(); - - // for outgoing xml - QDomDocument doc; - - // sasl-related - QString saslMech() const; - QByteArray saslStep() const; - void setSASLMechList(const QStringList &list); - void setSASLFirst(const QString &mech, const QByteArray &step); - void setSASLNext(const QByteArray &step); - void setSASLAuthed(); - - // send / recv - void sendStanza(const QDomElement &e); - void sendDirect(const QString &s); - void sendWhitespace(); - void clearSendQueue(); - QDomElement recvStanza(); - - // shutdown - void shutdown(); - void shutdownWithError(int cond, const QString &otherHost=""); - - // information - QString to, from, id, lang; - Version version; - - // error output - int errCond; - QString errText; - QHash errLangText; - QDomElement errAppSpec; - QString otherHost; - - QByteArray spare; // filled with unprocessed data on NStartTLS and NSASLLayer - - bool isReady() const; - - enum { TypeElement, TypeStanza, TypeDirect, TypePing }; - - protected: - static int stringToSASLCond(const QString &s); - static int stringToStreamCond(const QString &s); - static QString saslCondToString(int); - static QString streamCondToString(int); - - void send(const QDomElement &e, bool clip=false); - void sendUrgent(const QDomElement &e, bool clip=false); - void sendStreamError(int cond, const QString &text="", const QDomElement &appSpec=QDomElement()); - void sendStreamError(const QString &text); // old-style - - bool errorAndClose(int cond, const QString &text="", const QDomElement &appSpec=QDomElement()); - bool error(int code); - void delayErrorAndClose(int cond, const QString &text="", const QDomElement &appSpec=QDomElement()); - void delayError(int code); - - // reimplemented - QDomElement docElement(); - void handleDocOpen(const Parser::Event &pe); - bool handleError(); - bool handleCloseFinished(); - bool doStep(const QDomElement &e); - void itemWritten(int id, int size); - - virtual QString defaultNamespace(); - virtual QStringList extraNamespaces(); // stringlist: prefix,uri,prefix,uri, [...] - virtual void handleStreamOpen(const Parser::Event &pe); - virtual bool doStep2(const QDomElement &e)=0; - - void setReady(bool b); - - QString sasl_mech; - QStringList sasl_mechlist; - QByteArray sasl_step; - bool sasl_authed; - - QDomElement stanzaToRecv; - - private: - struct SASLCondEntry - { - const char *str; - int cond; - }; - static SASLCondEntry saslCondTable[]; - - struct StreamCondEntry - { - const char *str; - int cond; - }; - static StreamCondEntry streamCondTable[]; - - struct SendItem - { - QDomElement stanzaToSend; - QString stringToSend; - bool doWhitespace; - }; - QList sendList; - - bool doShutdown, delayedError, closeError, ready; - int stanzasPending, stanzasWritten; - - void init(); - void extractStreamError(const QDomElement &e); + bool doShutdown, delayedError, closeError, ready; + int stanzasPending, stanzasWritten; + + void init(); + void extractStreamError(const QDomElement &e); +}; + +class CoreProtocol : public BasicProtocol { +public: + enum { + NPassword = NCustom, // need password for old-mode + EDBVerify = ECustom, // breakpoint after db:verify request + ErrPlain = ErrCustom // server only supports plain, but allowPlain is false locally }; - class CoreProtocol : public BasicProtocol - { + CoreProtocol(); + ~CoreProtocol(); + + void reset(); + void needTimer(int seconds); + + // reimplemented to do SM + void sendStanza(const QDomElement &e); + + void startClientOut(const Jid &jid, bool oldOnly, bool tlsActive, bool doAuth, bool doCompression); + void startServerOut(const QString &to); + void startDialbackOut(const QString &to, const QString &from); + void startDialbackVerifyOut(const QString &to, const QString &from, const QString &id, const QString &key); + void startClientIn(const QString &id); + void startServerIn(const QString &id); + + void setLang(const QString &s); + void setAllowTLS(bool b); + void setAllowBind(bool b); + void setAllowPlain(bool b); // old-mode + const Jid &jid() const; + + void setPassword(const QString &s); + void setFrom(const QString &s); + void setDialbackKey(const QString &s); + + // input + QString user, host; + + // status + bool old; + + StreamFeatures features; + QList unhandledFeatures; + QStringList hosts; + + // static QString xmlToString(const QDomElement &e, bool clip=false); + + StreamManagement sm; + + class DBItem { public: - enum { - NPassword = NCustom, // need password for old-mode - EDBVerify = ECustom, // breakpoint after db:verify request - ErrPlain = ErrCustom // server only supports plain, but allowPlain is false locally - }; - - CoreProtocol(); - ~CoreProtocol(); - - void reset(); - void needTimer(int seconds); - - // reimplemented to do SM - void sendStanza(const QDomElement &e); - - void startClientOut(const Jid &jid, bool oldOnly, bool tlsActive, bool doAuth, bool doCompression); - void startServerOut(const QString &to); - void startDialbackOut(const QString &to, const QString &from); - void startDialbackVerifyOut(const QString &to, const QString &from, const QString &id, const QString &key); - void startClientIn(const QString &id); - void startServerIn(const QString &id); - - void setLang(const QString &s); - void setAllowTLS(bool b); - void setAllowBind(bool b); - void setAllowPlain(bool b); // old-mode - const Jid& jid() const; - - void setPassword(const QString &s); - void setFrom(const QString &s); - void setDialbackKey(const QString &s); - - // input - QString user, host; - - // status - bool old; - - StreamFeatures features; - QList unhandledFeatures; - QStringList hosts; - - //static QString xmlToString(const QDomElement &e, bool clip=false); - - StreamManagement sm; - - class DBItem - { - public: - enum { ResultRequest, ResultGrant, VerifyRequest, VerifyGrant, Validated }; - int type; - Jid to, from; - QString key, id; - bool ok; - }; - private: - enum Step { - Start, - Done, - SendFeatures, - GetRequest, - HandleTLS, - GetSASLResponse, - IncHandleSASLSuccess, - GetFeatures, // read features packet - HandleFeatures, // act on features, by initiating tls, sasl, or bind - GetTLSProceed, // read tls response - GetCompressProceed, // read compression response - GetSASLFirst, // perform sasl first step using provided data - GetSASLChallenge, // read server sasl challenge - GetSASLNext, // perform sasl next step using provided data - HandleSASLSuccess, // handle what must be done after reporting sasl success - GetBindResponse, // read bind response - HandleAuthGet, // send old-protocol auth-get - GetAuthGetResponse, // read auth-get response - HandleAuthSet, // send old-protocol auth-set - GetAuthSetResponse, // read auth-set response - GetSMResponse // read SM init response - }; - - QList dbrequests, dbpending, dbvalidated; - - bool server, dialback, dialback_verify; - int step; - - bool digest; - bool tls_started, sasl_started, compress_started; - - Jid jid_; - bool oldOnly; - bool allowPlain; - bool doTLS, doAuth, doBinding, doCompress; - QString password; - - QString dialback_id, dialback_key; - QString self_from; - - void init(); - static int getOldErrorCode(const QDomElement &e); - bool loginComplete(); - - bool isValidStanza(const QDomElement &e) const; - bool streamManagementHandleStanza(const QDomElement &e); - bool grabPendingItem(const Jid &to, const Jid &from, int type, DBItem *item); - bool normalStep(const QDomElement &e); - bool dialbackStep(const QDomElement &e); - - bool needSMRequest(); - - // reimplemented - bool stepAdvancesParser() const; - bool stepRequiresElement() const; - void stringSend(const QString &s); - void stringRecv(const QString &s); - QString defaultNamespace(); - QStringList extraNamespaces(); - void handleStreamOpen(const Parser::Event &pe); - bool doStep2(const QDomElement &e); - void elementSend(const QDomElement &e); - void elementRecv(const QDomElement &e); + enum { ResultRequest, ResultGrant, VerifyRequest, VerifyGrant, Validated }; + int type; + Jid to, from; + QString key, id; + bool ok; + }; + +private: + enum Step { + Start, + Done, + SendFeatures, + GetRequest, + HandleTLS, + GetSASLResponse, + IncHandleSASLSuccess, + GetFeatures, // read features packet + HandleFeatures, // act on features, by initiating tls, sasl, or bind + GetTLSProceed, // read tls response + GetCompressProceed, // read compression response + GetSASLFirst, // perform sasl first step using provided data + GetSASLChallenge, // read server sasl challenge + GetSASLNext, // perform sasl next step using provided data + HandleSASLSuccess, // handle what must be done after reporting sasl success + GetBindResponse, // read bind response + HandleAuthGet, // send old-protocol auth-get + GetAuthGetResponse, // read auth-get response + HandleAuthSet, // send old-protocol auth-set + GetAuthSetResponse, // read auth-set response + GetSMResponse // read SM init response }; -} -#endif + QList dbrequests, dbpending, dbvalidated; + + bool server, dialback, dialback_verify; + int step; + + bool digest; + bool tls_started, sasl_started, compress_started; + + Jid jid_; + bool oldOnly; + bool allowPlain; + bool doTLS, doAuth, doBinding, doCompress; + QString password; + + QString dialback_id, dialback_key; + QString self_from; + + void init(); + static int getOldErrorCode(const QDomElement &e); + bool loginComplete(); + + bool isValidStanza(const QDomElement &e) const; + bool streamManagementHandleStanza(const QDomElement &e); + bool grabPendingItem(const Jid &to, const Jid &from, int type, DBItem *item); + bool normalStep(const QDomElement &e); + bool dialbackStep(const QDomElement &e); + + bool needSMRequest(); + + // reimplemented + bool stepAdvancesParser() const; + bool stepRequiresElement() const; + void stringSend(const QString &s); + void stringRecv(const QString &s); + QString defaultNamespace(); + QStringList extraNamespaces(); + void handleStreamOpen(const Parser::Event &pe); + bool doStep2(const QDomElement &e); + void elementSend(const QDomElement &e); + void elementRecv(const QDomElement &e); +}; +} // namespace XMPP + +#endif // PROTOCOL_H diff --git a/src/xmpp/xmpp-core/securestream.cpp b/src/xmpp/xmpp-core/securestream.cpp index ca192722..94c2d0d2 100644 --- a/src/xmpp/xmpp-core/securestream.cpp +++ b/src/xmpp/xmpp-core/securestream.cpp @@ -27,23 +27,21 @@ #include "securestream.h" -#include -#include -#include - +#include "compressionhandler.h" #ifdef USE_TLSHANDLER #include "xmpp.h" #endif -#include "compressionhandler.h" + +#include +#include +#include //---------------------------------------------------------------------------- // LayerTracker //---------------------------------------------------------------------------- -class LayerTracker -{ +class LayerTracker { public: - struct Item - { + struct Item { int plain; int encoded; }; @@ -53,16 +51,13 @@ class LayerTracker void reset(); void addPlain(int plain); void specifyEncoded(int encoded, int plain); - int finished(int encoded); + int finished(int encoded); - int p; + int p; QList list; }; -LayerTracker::LayerTracker() -{ - p = 0; -} +LayerTracker::LayerTracker() { p = 0; } void LayerTracker::reset() { @@ -70,19 +65,16 @@ void LayerTracker::reset() list.clear(); } -void LayerTracker::addPlain(int plain) -{ - p += plain; -} +void LayerTracker::addPlain(int plain) { p += plain; } void LayerTracker::specifyEncoded(int encoded, int plain) { // can't specify more bytes than we have - if(plain > p) + if (plain > p) plain = p; p -= plain; Item i; - i.plain = plain; + i.plain = plain; i.encoded = encoded; list += i; } @@ -90,11 +82,11 @@ void LayerTracker::specifyEncoded(int encoded, int plain) int LayerTracker::finished(int encoded) { int plain = 0; - for(QList::Iterator it = list.begin(); it != list.end();) { + for (QList::Iterator it = list.begin(); it != list.end();) { Item &i = *it; // not enough? - if(encoded < i.encoded) { + if (encoded < i.encoded) { i.encoded -= encoded; break; } @@ -109,14 +101,13 @@ int LayerTracker::finished(int encoded) //---------------------------------------------------------------------------- // SecureStream //---------------------------------------------------------------------------- -class SecureLayer : public QObject -{ +class SecureLayer : public QObject { Q_OBJECT public: enum { TLS, SASL, TLSH, Compression }; int type; union { - QCA::TLS *tls; + QCA::TLS *tls; QCA::SASL *sasl; #ifdef USE_TLSHANDLER XMPP::TLSHandler *tlsHandler; @@ -124,12 +115,12 @@ class SecureLayer : public QObject CompressionHandler *compressionHandler; } p; LayerTracker layer; - bool tls_done; - int prebytes; + bool tls_done; + int prebytes; SecureLayer(QCA::TLS *t) { - type = TLS; + type = TLS; p.tls = t; init(); connect(p.tls, SIGNAL(handshaken()), SLOT(tls_handshaken())); @@ -141,7 +132,7 @@ class SecureLayer : public QObject SecureLayer(QCA::SASL *s) { - type = SASL; + type = SASL; p.sasl = s; init(); connect(p.sasl, SIGNAL(readyRead()), SLOT(sasl_readyRead())); @@ -152,7 +143,7 @@ class SecureLayer : public QObject SecureLayer(CompressionHandler *t) { t->setParent(this); // automatically clean up CompressionHandler when SecureLayer is destroyed - type = Compression; + type = Compression; p.compressionHandler = t; init(); connect(p.compressionHandler, SIGNAL(readyRead()), SLOT(compressionHandler_readyRead())); @@ -163,14 +154,15 @@ class SecureLayer : public QObject #ifdef USE_TLSHANDLER SecureLayer(XMPP::TLSHandler *t) { - type = TLSH; + type = TLSH; p.tlsHandler = t; init(); connect(p.tlsHandler, SIGNAL(success()), SLOT(tlsHandler_success())); connect(p.tlsHandler, SIGNAL(fail()), SLOT(tlsHandler_fail())); connect(p.tlsHandler, SIGNAL(closed()), SLOT(tlsHandler_closed())); connect(p.tlsHandler, SIGNAL(readyRead(QByteArray)), SLOT(tlsHandler_readyRead(QByteArray))); - connect(p.tlsHandler, SIGNAL(readyReadOutgoing(QByteArray,int)), SLOT(tlsHandler_readyReadOutgoing(QByteArray,int))); + connect(p.tlsHandler, SIGNAL(readyReadOutgoing(QByteArray, int)), + SLOT(tlsHandler_readyReadOutgoing(QByteArray, int))); } #endif @@ -183,25 +175,49 @@ class SecureLayer : public QObject void write(const QByteArray &a) { layer.addPlain(a.size()); - switch(type) { - case TLS: { p.tls->write(a); break; } - case SASL: { p.sasl->write(a); break; } + switch (type) { + case TLS: { + p.tls->write(a); + break; + } + case SASL: { + p.sasl->write(a); + break; + } #ifdef USE_TLSHANDLER - case TLSH: { p.tlsHandler->write(a); break; } + case TLSH: { + p.tlsHandler->write(a); + break; + } #endif - case Compression: { p.compressionHandler->write(a); break; } + case Compression: { + p.compressionHandler->write(a); + break; + } } } void writeIncoming(const QByteArray &a) { - switch(type) { - case TLS: { p.tls->writeIncoming(a); break; } - case SASL: { p.sasl->writeIncoming(a); break; } + switch (type) { + case TLS: { + p.tls->writeIncoming(a); + break; + } + case SASL: { + p.sasl->writeIncoming(a); + break; + } #ifdef USE_TLSHANDLER - case TLSH: { p.tlsHandler->writeIncoming(a); break; } + case TLSH: { + p.tlsHandler->writeIncoming(a); + break; + } #endif - case Compression: { p.compressionHandler->writeIncoming(a); break; } + case Compression: { + p.compressionHandler->writeIncoming(a); + break; + } } } @@ -210,13 +226,12 @@ class SecureLayer : public QObject int written = 0; // deal with prebytes (bytes sent prior to this security layer) - if(prebytes > 0) { - if(prebytes >= plain) { + if (prebytes > 0) { + if (prebytes >= plain) { written += plain; prebytes -= plain; plain = 0; - } - else { + } else { written += prebytes; plain -= prebytes; prebytes = 0; @@ -224,7 +239,7 @@ class SecureLayer : public QObject } // put remainder into the layer tracker - if(type == SASL || tls_done) + if (type == SASL || tls_done) written += layer.finished(plain); return written; @@ -241,121 +256,102 @@ private slots: void tls_handshaken() { tls_done = true; - tlsHandshaken(); + emit tlsHandshaken(); } void tls_readyRead() { QByteArray a = p.tls->read(); - readyRead(a); + emit readyRead(a); } void tls_readyReadOutgoing(int plainBytes) { QByteArray a = p.tls->readOutgoing(); - if(tls_done) + if (tls_done) layer.specifyEncoded(a.size(), plainBytes); - needWrite(a); + emit needWrite(a); } void tls_closed() { QByteArray a = p.tls->readUnprocessed(); - tlsClosed(a); + emit tlsClosed(a); } - void tls_error(int x) - { - error(x); - } + void tls_error(int x) { emit error(x); } void sasl_readyRead() { QByteArray a = p.sasl->read(); - readyRead(a); + emit readyRead(a); } void sasl_readyReadOutgoing() { - int plainBytes; + int plainBytes; QByteArray a = p.sasl->readOutgoing(&plainBytes); layer.specifyEncoded(a.size(), plainBytes); - needWrite(a); + emit needWrite(a); } - void sasl_error() - { - error(p.sasl->errorCode()); - } + void sasl_error() { emit error(p.sasl->errorCode()); } void compressionHandler_readyRead() { QByteArray a = p.compressionHandler->read(); - readyRead(a); + emit readyRead(a); } void compressionHandler_readyReadOutgoing() { - int plainBytes; + int plainBytes; QByteArray a = p.compressionHandler->readOutgoing(&plainBytes); layer.specifyEncoded(a.size(), plainBytes); - needWrite(a); + emit needWrite(a); } - void compressionHandler_error() - { - error(p.compressionHandler->errorCode()); - } + void compressionHandler_error() { emit error(p.compressionHandler->errorCode()); } #ifdef USE_TLSHANDLER void tlsHandler_success() { tls_done = true; - tlsHandshaken(); + emit tlsHandshaken(); } - void tlsHandler_fail() - { - error(0); - } + void tlsHandler_fail() { emit error(0); } - void tlsHandler_closed() - { - tlsClosed(QByteArray()); - } + void tlsHandler_closed() { emit tlsClosed(QByteArray()); } - void tlsHandler_readyRead(const QByteArray &a) - { - readyRead(a); - } + void tlsHandler_readyRead(const QByteArray &a) { emit readyRead(a); } void tlsHandler_readyReadOutgoing(const QByteArray &a, int plainBytes) { - if(tls_done) + if (tls_done) layer.specifyEncoded(a.size(), plainBytes); - needWrite(a); + emit needWrite(a); } #endif }; #include "securestream.moc" -class SecureStream::Private -{ +class SecureStream::Private { public: - ByteStream *bs; - QList layers; - int pending; - int errorCode; - bool active; - bool topInProgress; + ByteStream *bs; + QList layers; + int pending; + int errorCode; + bool active; + bool topInProgress; bool haveTLS() const { - foreach(SecureLayer *s, layers) { - if(s->type == SecureLayer::TLS + for (SecureLayer *s : layers) { + if (s->type == SecureLayer::TLS #ifdef USE_TLSHANDLER - || s->type == SecureLayer::TLSH + || s->type == SecureLayer::TLSH #endif ) { return true; @@ -366,8 +362,8 @@ class SecureStream::Private bool haveSASL() const { - foreach(SecureLayer *s, layers) { - if(s->type == SecureLayer::SASL) + for (SecureLayer *s : layers) { + if (s->type == SecureLayer::SASL) return true; } return false; @@ -375,8 +371,8 @@ class SecureStream::Private bool haveCompress() const { - foreach(SecureLayer *s, layers) { - if(s->type == SecureLayer::Compression) + for (SecureLayer *s : layers) { + if (s->type == SecureLayer::Compression) return true; } return false; @@ -389,8 +385,7 @@ class SecureStream::Private } }; -SecureStream::SecureStream(ByteStream *s) -:ByteStream(0) +SecureStream::SecureStream(ByteStream *s) : ByteStream(nullptr) { d = new Private; @@ -398,8 +393,8 @@ SecureStream::SecureStream(ByteStream *s) connect(d->bs, SIGNAL(readyRead()), SLOT(bs_readyRead())); connect(d->bs, SIGNAL(bytesWritten(qint64)), SLOT(bs_bytesWritten(qint64))); - d->pending = 0; - d->active = true; + d->pending = 0; + d->active = true; d->topInProgress = false; setOpenMode(QIODevice::ReadWrite); } @@ -422,7 +417,7 @@ void SecureStream::linkLayer(QObject *s) int SecureStream::calcPrebytes() const { int x = 0; - foreach(SecureLayer *s, d->layers) { + for (SecureLayer *s : std::as_const(d->layers)) { x += s->prebytes; } return (d->pending - x); @@ -430,11 +425,11 @@ int SecureStream::calcPrebytes() const void SecureStream::startTLSClient(QCA::TLS *t, const QByteArray &spare) { - if(!d->active || d->topInProgress || d->haveTLS()) + if (!d->active || d->topInProgress || d->haveTLS()) return; SecureLayer *s = new SecureLayer(t); - s->prebytes = calcPrebytes(); + s->prebytes = calcPrebytes(); linkLayer(s); d->layers.append(s); d->topInProgress = true; @@ -444,11 +439,11 @@ void SecureStream::startTLSClient(QCA::TLS *t, const QByteArray &spare) void SecureStream::startTLSServer(QCA::TLS *t, const QByteArray &spare) { - if(!d->active || d->topInProgress || d->haveTLS()) + if (!d->active || d->topInProgress || d->haveTLS()) return; SecureLayer *s = new SecureLayer(t); - s->prebytes = calcPrebytes(); + s->prebytes = calcPrebytes(); linkLayer(s); d->layers.append(s); d->topInProgress = true; @@ -456,13 +451,13 @@ void SecureStream::startTLSServer(QCA::TLS *t, const QByteArray &spare) insertData(spare); } -void SecureStream::setLayerCompress(const QByteArray& spare) +void SecureStream::setLayerCompress(const QByteArray &spare) { - if(!d->active || d->topInProgress || d->haveCompress()) + if (!d->active || d->topInProgress || d->haveCompress()) return; SecureLayer *s = new SecureLayer(new CompressionHandler()); - s->prebytes = calcPrebytes(); + s->prebytes = calcPrebytes(); linkLayer(s); d->layers.append(s); @@ -471,11 +466,11 @@ void SecureStream::setLayerCompress(const QByteArray& spare) void SecureStream::setLayerSASL(QCA::SASL *sasl, const QByteArray &spare) { - if(!d->active || d->topInProgress || d->haveSASL()) + if (!d->active || d->topInProgress || d->haveSASL()) return; SecureLayer *s = new SecureLayer(sasl); - s->prebytes = calcPrebytes(); + s->prebytes = calcPrebytes(); linkLayer(s); d->layers.append(s); @@ -485,11 +480,11 @@ void SecureStream::setLayerSASL(QCA::SASL *sasl, const QByteArray &spare) #ifdef USE_TLSHANDLER void SecureStream::startTLSClient(XMPP::TLSHandler *t, const QString &server, const QByteArray &spare) { - if(!d->active || d->topInProgress || d->haveTLS()) + if (!d->active || d->topInProgress || d->haveTLS()) return; SecureLayer *s = new SecureLayer(t); - s->prebytes = calcPrebytes(); + s->prebytes = calcPrebytes(); linkLayer(s); d->layers.append(s); d->topInProgress = true; @@ -505,25 +500,19 @@ void SecureStream::closeTLS() { if (!d->layers.isEmpty()) { SecureLayer *s = d->layers.last(); - if(s->type == SecureLayer::TLS) { + if (s->type == SecureLayer::TLS) { s->p.tls->close(); } } } -int SecureStream::errorCode() const -{ - return d->errorCode; -} +int SecureStream::errorCode() const { return d->errorCode; } -bool SecureStream::isOpen() const -{ - return d->active; -} +bool SecureStream::isOpen() const { return d->active; } void SecureStream::write(const QByteArray &a) { - if(!isOpen()) { + if (!isOpen()) { qDebug("Writing to closed stream!"); return; } @@ -534,16 +523,12 @@ void SecureStream::write(const QByteArray &a) if (!d->layers.isEmpty()) { SecureLayer *s = d->layers.last(); s->write(a); - } - else { + } else { writeRawData(a); } } -qint64 SecureStream::bytesToWrite() const -{ - return d->pending; -} +qint64 SecureStream::bytesToWrite() const { return d->pending; } void SecureStream::bs_readyRead() { @@ -553,28 +538,27 @@ void SecureStream::bs_readyRead() if (!d->layers.isEmpty()) { SecureLayer *s = d->layers.first(); s->writeIncoming(a); - } - else { + } else { incomingData(a); } } void SecureStream::bs_bytesWritten(qint64 bytes) { - foreach(SecureLayer *s, d->layers) { - bytes = s->finished(bytes); + for (SecureLayer *s : std::as_const(d->layers)) { + bytes = s->finished(int(bytes)); } - if(bytes > 0) { + if (bytes > 0) { d->pending -= bytes; - bytesWritten(bytes); + emit bytesWritten(bytes); } } void SecureStream::layer_tlsHandshaken() { d->topInProgress = false; - tlsHandshaken(); + emit tlsHandshaken(); } void SecureStream::layer_tlsClosed(const QByteArray &) @@ -582,14 +566,14 @@ void SecureStream::layer_tlsClosed(const QByteArray &) setOpenMode(QIODevice::NotOpen); d->active = false; d->deleteLayers(); - tlsClosed(); + emit tlsClosed(); } void SecureStream::layer_readyRead(const QByteArray &a) { - SecureLayer *s = static_cast(sender()); - QList::Iterator it(d->layers.begin()); - while((*it) != s) { + SecureLayer *s = static_cast(sender()); + QList::Iterator it(d->layers.begin()); + while ((*it) != s) { Q_ASSERT(it != d->layers.end()); ++it; } @@ -600,17 +584,16 @@ void SecureStream::layer_readyRead(const QByteArray &a) if (it != d->layers.end()) { s = (*it); s->writeIncoming(a); - } - else { + } else { incomingData(a); } } void SecureStream::layer_needWrite(const QByteArray &a) { - SecureLayer *s = static_cast(sender()); - QList::Iterator it(d->layers.begin()); - while((*it) != s) { + SecureLayer *s = static_cast(sender()); + QList::Iterator it(d->layers.begin()); + while ((*it) != s) { Q_ASSERT(it != d->layers.end()); ++it; } @@ -621,51 +604,46 @@ void SecureStream::layer_needWrite(const QByteArray &a) --it; s = (*it); s->write(a); - } - else { + } else { writeRawData(a); } } void SecureStream::layer_error(int x) { - SecureLayer *s = static_cast(sender()); - int type = s->type; - d->errorCode = x; + SecureLayer *s = static_cast(sender()); + int type = s->type; + d->errorCode = x; setOpenMode(QIODevice::NotOpen); d->active = false; d->deleteLayers(); - if(type == SecureLayer::TLS) + if (type == SecureLayer::TLS) setError(ErrTLS); - else if(type == SecureLayer::SASL) + else if (type == SecureLayer::SASL) setError(ErrSASL); #ifdef USE_TLSHANDLER - else if(type == SecureLayer::TLSH) + else if (type == SecureLayer::TLSH) setError(ErrTLS); #endif } void SecureStream::insertData(const QByteArray &a) { - if(!a.isEmpty()) { + if (!a.isEmpty()) { if (!d->layers.isEmpty()) { SecureLayer *s = d->layers.last(); s->writeIncoming(a); - } - else { + } else { incomingData(a); } } } -void SecureStream::writeRawData(const QByteArray &a) -{ - d->bs->write(a); -} +void SecureStream::writeRawData(const QByteArray &a) { d->bs->write(a); } void SecureStream::incomingData(const QByteArray &a) { appendRead(a); - if(bytesAvailable()) + if (bytesAvailable()) emit readyRead(); } diff --git a/src/xmpp/xmpp-core/securestream.h b/src/xmpp/xmpp-core/securestream.h index 5607c959..15f16401 100644 --- a/src/xmpp/xmpp-core/securestream.h +++ b/src/xmpp/xmpp-core/securestream.h @@ -20,42 +20,40 @@ #ifndef SECURESTREAM_H #define SECURESTREAM_H -#include #include "bytestream.h" -#define USE_TLSHANDLER +#include +#define USE_TLSHANDLER #ifdef USE_TLSHANDLER -namespace XMPP -{ - class TLSHandler; +namespace XMPP { +class TLSHandler; } #endif class CompressionHandler; -class SecureStream : public ByteStream -{ +class SecureStream : public ByteStream { Q_OBJECT public: enum Error { ErrTLS = ErrCustom, ErrSASL }; SecureStream(ByteStream *s); ~SecureStream(); - void startTLSClient(QCA::TLS *t, const QByteArray &spare=QByteArray()); - void startTLSServer(QCA::TLS *t, const QByteArray &spare=QByteArray()); - void setLayerCompress(const QByteArray &spare=QByteArray()); - void setLayerSASL(QCA::SASL *s, const QByteArray &spare=QByteArray()); + void startTLSClient(QCA::TLS *t, const QByteArray &spare = QByteArray()); + void startTLSServer(QCA::TLS *t, const QByteArray &spare = QByteArray()); + void setLayerCompress(const QByteArray &spare = QByteArray()); + void setLayerSASL(QCA::SASL *s, const QByteArray &spare = QByteArray()); #ifdef USE_TLSHANDLER - void startTLSClient(XMPP::TLSHandler *t, const QString &server, const QByteArray &spare=QByteArray()); + void startTLSClient(XMPP::TLSHandler *t, const QString &server, const QByteArray &spare = QByteArray()); #endif void closeTLS(); - int errorCode() const; + int errorCode() const; // reimplemented - bool isOpen() const; - void write(const QByteArray &); + bool isOpen() const; + void write(const QByteArray &); qint64 bytesToWrite() const; signals: @@ -74,7 +72,7 @@ private slots: private: void linkLayer(QObject *); - int calcPrebytes() const; + int calcPrebytes() const; void insertData(const QByteArray &a); void writeRawData(const QByteArray &a); void incomingData(const QByteArray &a); @@ -83,4 +81,4 @@ private slots: Private *d; }; -#endif +#endif // SECURESTREAM_H diff --git a/src/xmpp/xmpp-core/simplesasl.cpp b/src/xmpp/xmpp-core/simplesasl.cpp index db72cd04..c61f7bca 100644 --- a/src/xmpp/xmpp-core/simplesasl.cpp +++ b/src/xmpp/xmpp-core/simplesasl.cpp @@ -19,103 +19,93 @@ #include "simplesasl.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "xmpp/sasl/plainmessage.h" #include "xmpp/sasl/digestmd5response.h" -#include "xmpp/sasl/scramsha1response.h" +#include "xmpp/sasl/plainmessage.h" #include "xmpp/sasl/scramsha1message.h" +#include "xmpp/sasl/scramsha1response.h" #include "xmpp/sasl/scramsha1signature.h" -#include "xmpp/base/randrandomnumbergenerator.h" + +#include +#include +#include +#include +#include +#include +#include +#include namespace XMPP { -class SimpleSASLContext : public QCA::SASLContext -{ +class SimpleSASLContext : public QCA::SASLContext { Q_OBJECT public: - class ParamsMutable - { - public: - /** - User is held - */ - bool user; - - /** - Authorization ID is held - */ - bool authzid; - - /** - Password is held - */ - bool pass; - - /** - Realm is held - */ - bool realm; - }; + class ParamsMutable { + public: + /** + User is held + */ + bool user; + + /** + Authorization ID is held + */ + bool authzid; + + /** + Password is held + */ + bool pass; + + /** + Realm is held + */ + bool realm; + }; // core props QString service, host; // state - int step; - bool capable; - bool allow_plain; + int step; + bool capable; + bool allow_plain; QByteArray out_buf, in_buf; - QString mechanism_; - QString out_mech; - - ParamsMutable need; - ParamsMutable have; - QString user, authz, realm; - QCA::SecureArray pass; - Result result_; + QString mechanism_; + QString out_mech; + + ParamsMutable need; + ParamsMutable have; + QString user, authz, realm; + QCA::SecureArray pass; + Result result_; QCA::SASL::AuthCondition authCondition_; - QByteArray result_to_net_, result_to_app_; - int encoded_; + QByteArray result_to_net_, result_to_app_; + int encoded_; // scram specific stuff - QByteArray client_first_message; + QByteArray client_first_message; QCA::SecureArray server_signature; - SimpleSASLContext(QCA::Provider* p) : QCA::SASLContext(p) - { - reset(); - } + SimpleSASLContext(QCA::Provider *p) : QCA::SASLContext(p) { reset(); } - ~SimpleSASLContext() - { - reset(); - } + ~SimpleSASLContext() { reset(); } void reset() { resetState(); - capable = true; - allow_plain = false; - need.user = false; + capable = true; + allow_plain = false; + need.user = false; need.authzid = false; - need.pass = false; - need.realm = false; - have.user = false; + need.pass = false; + need.realm = false; + have.user = false; have.authzid = false; - have.pass = false; - have.realm = false; - user = QString(); - authz = QString(); - pass = QCA::SecureArray(); - realm = QString(); + have.pass = false; + have.realm = false; + user = QString(); + authz = QString(); + pass = QCA::SecureArray(); + realm = QString(); } void resetState() @@ -125,24 +115,28 @@ class SimpleSASLContext : public QCA::SASLContext authCondition_ = QCA::SASL::AuthFail; } - virtual void setConstraints(QCA::SASL::AuthFlags flags, int ssfMin, int) { - if(flags & (QCA::SASL::RequireForwardSecrecy | QCA::SASL::RequirePassCredentials | QCA::SASL::RequireMutualAuth) || ssfMin > 0) - capable = false; - else - capable = true; + virtual void setConstraints(QCA::SASL::AuthFlags flags, int ssfMin, int) + { + capable = !( + flags + & (QCA::SASL::RequireForwardSecrecy | QCA::SASL::RequirePassCredentials | QCA::SASL::RequireMutualAuth) + || ssfMin > 0); allow_plain = flags & QCA::SASL::AllowPlain; } - virtual void setup(const QString& _service, const QString& _host, const QCA::SASLContext::HostPort*, const QCA::SASLContext::HostPort*, const QString&, int) { + virtual void setup(const QString &_service, const QString &_host, const QCA::SASLContext::HostPort *, + const QCA::SASLContext::HostPort *, const QString &, int) + { service = _service; - host = _host; + host = _host; } - virtual void startClient(const QStringList &mechlist, bool allowClientSendFirst) { + virtual void startClient(const QStringList &mechlist, bool allowClientSendFirst) + { Q_UNUSED(allowClientSendFirst); mechanism_ = QString(); - foreach(QString mech, mechlist) { + for (const QString &mech : mechlist) { if (mech == "SCRAM-SHA-1") { mechanism_ = "SCRAM-SHA-1"; break; @@ -155,8 +149,8 @@ class SimpleSASLContext : public QCA::SASLContext mechanism_ = "PLAIN"; } - if(!capable || mechanism_.isEmpty()) { - result_ = Error; + if (!capable || mechanism_.isEmpty()) { + result_ = Error; authCondition_ = QCA::SASL::NoMechanism; if (!capable) qWarning("simplesasl.cpp: Not enough capabilities"); @@ -168,34 +162,36 @@ class SimpleSASLContext : public QCA::SASLContext resetState(); result_ = Continue; - step = 0; + step = 0; tryAgain(); } - virtual void nextStep(const QByteArray &from_net) { + virtual void nextStep(const QByteArray &from_net) + { in_buf = from_net; tryAgain(); } - virtual void tryAgain() { + virtual void tryAgain() + { // All exits of the method must emit the ready signal // so all exits go through a goto ready; - if(step == 0) { + if (step == 0) { out_mech = mechanism_; // PLAIN if (out_mech == "PLAIN" || out_mech == "SCRAM-SHA-1") { // First, check if we have everything - if(need.user || need.pass) { + if (need.user || need.pass) { qWarning("simplesasl.cpp: Did not receive necessary auth parameters"); result_ = Error; goto ready; } - if(!have.user) + if (!have.user) need.user = true; - if(!have.pass) + if (!have.pass) need.pass = true; - if(need.user || need.pass) { + if (need.user || need.pass) { result_ = Params; goto ready; } @@ -204,9 +200,9 @@ class SimpleSASLContext : public QCA::SASLContext out_buf = PLAINMessage(authz, user, pass.toByteArray()).getValue(); } else if (out_mech == "SCRAM-SHA-1") { // send client-first-message - SCRAMSHA1Message msg(authz, user, QByteArray(0, ' '), RandRandomNumberGenerator()); + SCRAMSHA1Message msg(authz, user, QByteArray(0, ' ')); if (msg.isValid()) { - out_buf = msg.getValue(); + out_buf = msg.getValue(); client_first_message = out_buf; } else { qWarning("simplesasl.cpp: SASLprep failed."); @@ -219,32 +215,32 @@ class SimpleSASLContext : public QCA::SASLContext result_ = Success; else result_ = Continue; - } else if(step == 1) { + } else if (step == 1) { Q_ASSERT(out_mech != "PLAIN"); if (out_mech == "DIGEST-MD5") { // if we still need params, then the app has failed us! - if(need.user || need.authzid || need.pass || need.realm) { + if (need.user || need.authzid || need.pass || need.realm) { qWarning("simplesasl.cpp: Did not receive necessary auth parameters"); result_ = Error; goto ready; } // see if some params are needed - if(!have.user) + if (!have.user) need.user = true; - //if(!have.authzid) + // if(!have.authzid) // need.authzid = true; - if(!have.pass) + if (!have.pass) need.pass = true; - if(need.user || need.authzid || need.pass) { + if (need.user || need.authzid || need.pass) { result_ = Params; goto ready; } - DIGESTMD5Response response(in_buf, service, host, realm, user, authz, pass.toByteArray(), RandRandomNumberGenerator()); + DIGESTMD5Response response(in_buf, service, host, realm, user, authz, pass.toByteArray()); if (!response.isValid()) { authCondition_ = QCA::SASL::BadProtocol; - result_ = Error; + result_ = Error; goto ready; } out_buf = response.getValue(); @@ -252,38 +248,38 @@ class SimpleSASLContext : public QCA::SASLContext result_ = Continue; } else if (out_mech == "SCRAM-SHA-1") { // if we still need params, then the app has failed us! - if(need.user || need.pass) { + if (need.user || need.pass) { qWarning("simplesasl.cpp: Did not receive necessary auth parameters"); result_ = Error; goto ready; } // see if some params are needed - if(!have.user) + if (!have.user) need.user = true; - //if(!have.authzid) + // if(!have.authzid) // need.authzid = true; - if(!have.pass) + if (!have.pass) need.pass = true; - if(need.user || need.pass) { + if (need.user || need.pass) { result_ = Params; goto ready; } // parse server-first-message, send client-final-message QVariant prop = property("scram-salted-password-base64"); - QString salted_password_base64; + QString salted_password_base64; if (prop.isValid()) { salted_password_base64 = prop.toString(); } - SCRAMSHA1Response response(in_buf, pass.toByteArray(), client_first_message, salted_password_base64, RandRandomNumberGenerator()); + SCRAMSHA1Response response(in_buf, pass.toByteArray(), client_first_message, salted_password_base64); if (!response.isValid()) { authCondition_ = QCA::SASL::BadProtocol; - result_ = Error; + result_ = Error; goto ready; } setProperty("scram-salted-password-base64", QVariant(response.getSaltedPassword())); server_signature = response.getServerSignature(); - out_buf = response.getValue(); + out_buf = response.getValue(); ++step; result_ = Continue; } @@ -295,7 +291,7 @@ class SimpleSASLContext : public QCA::SASLContext } else { qWarning() << "ServerSignature doesn't match the one we've calculated."; authCondition_ = QCA::SASL::AuthFail; - result_ = Error; + result_ = Error; goto ready; } } @@ -309,87 +305,72 @@ class SimpleSASLContext : public QCA::SASLContext out_buf.resize(0); result_ = Success; } -ready: + ready: QMetaObject::invokeMethod(this, "resultsReady", Qt::QueuedConnection); } - virtual void update(const QByteArray &from_net, const QByteArray &from_app) { + virtual void update(const QByteArray &from_net, const QByteArray &from_app) + { result_to_app_ = from_net; result_to_net_ = from_app; - encoded_ = from_app.size(); - result_ = Success; + encoded_ = from_app.size(); + result_ = Success; QMetaObject::invokeMethod(this, "resultsReady", Qt::QueuedConnection); } - virtual bool waitForResultsReady(int msecs) { + virtual bool waitForResultsReady(int msecs) + { // TODO: for now, all operations block anyway Q_UNUSED(msecs); return true; } - virtual Result result() const { - return result_; - } + virtual Result result() const { return result_; } - virtual QStringList mechlist() const { - return QStringList(); - } + virtual QStringList mechlist() const { return QStringList(); } - virtual QString mech() const { - return out_mech; - } + virtual QString mech() const { return out_mech; } - virtual bool haveClientInit() const { - return out_mech == "PLAIN"; - } + virtual bool haveClientInit() const { return out_mech == "PLAIN"; } - virtual QByteArray stepData() const { - return out_buf; - } + virtual QByteArray stepData() const { return out_buf; } - virtual QByteArray to_net() { - return result_to_net_; - } + virtual QByteArray to_net() { return result_to_net_; } - virtual int encoded() const { - return encoded_; - } + virtual int encoded() const { return encoded_; } - virtual QByteArray to_app() { - return result_to_app_; - } + virtual QByteArray to_app() { return result_to_app_; } - virtual int ssf() const { - return 0; - } + virtual int ssf() const { return 0; } - virtual QCA::SASL::AuthCondition authCondition() const { - return authCondition_; - } + virtual QCA::SASL::AuthCondition authCondition() const { return authCondition_; } - virtual QCA::SASL::Params clientParams() const { + virtual QCA::SASL::Params clientParams() const + { return QCA::SASL::Params(need.user, need.authzid, need.pass, need.realm); } - virtual void setClientParams(const QString *_user, const QString *_authzid, const QCA::SecureArray *_pass, const QString *_realm) { - if(_user) { - user = *_user; + virtual void setClientParams(const QString *_user, const QString *_authzid, const QCA::SecureArray *_pass, + const QString *_realm) + { + if (_user) { + user = *_user; need.user = false; have.user = true; } - if(_authzid) { - authz = *_authzid; + if (_authzid) { + authz = *_authzid; need.authzid = false; have.authzid = true; } - if(_pass) { - pass = *_pass; + if (_pass) { + pass = *_pass; need.pass = false; have.pass = true; } - if(_realm) { - realm = *_realm; + if (_realm) { + realm = *_realm; need.realm = false; have.realm = true; } @@ -401,66 +382,50 @@ class SimpleSASLContext : public QCA::SASLContext return QStringList(); } - virtual QString username() const { - return QString(); - } + virtual QString username() const { return QString(); } - virtual QString authzid() const { - return QString(); - } + virtual QString authzid() const { return QString(); } - virtual QCA::Provider::Context* clone() const { - SimpleSASLContext* s = new SimpleSASLContext(provider()); + virtual QCA::Provider::Context *clone() const + { + SimpleSASLContext *s = new SimpleSASLContext(provider()); // TODO: Copy all the members return s; } - virtual void startServer(const QString &, bool) { - result_ = QCA::SASLContext::Error; + virtual void startServer(const QString &, bool) + { + result_ = QCA::SASLContext::Error; QMetaObject::invokeMethod(this, "resultsReady", Qt::QueuedConnection); } - virtual void serverFirstStep(const QString &, const QByteArray *) { - result_ = QCA::SASLContext::Error; + virtual void serverFirstStep(const QString &, const QByteArray *) + { + result_ = QCA::SASLContext::Error; QMetaObject::invokeMethod(this, "resultsReady", Qt::QueuedConnection); } - }; -class QCASimpleSASL : public QCA::Provider -{ +class QCASimpleSASL : public QCA::Provider { public: - QCASimpleSASL() {} - ~QCASimpleSASL() {} + QCASimpleSASL() { } + ~QCASimpleSASL() { } - void init() - { - } + void init() { } - QString name() const { - return "simplesasl"; - } + QString name() const { return "simplesasl"; } - QStringList features() const { - return QStringList("sasl"); - } + QStringList features() const { return QStringList("sasl"); } - QCA::Provider::Context* createContext(const QString& cap) + QCA::Provider::Context *createContext(const QString &cap) { - if(cap == "sasl") + if (cap == "sasl") return new SimpleSASLContext(this); - return 0; - } - int qcaVersion() const - { - return QCA_VERSION; + return nullptr; } + int qcaVersion() const { return QCA_VERSION; } }; -QCA::Provider *createProviderSimpleSASL() -{ - return (new QCASimpleSASL); -} - -} +QCA::Provider *createProviderSimpleSASL() { return (new QCASimpleSASL); } +} // namespace XMPP #include "simplesasl.moc" diff --git a/src/xmpp/xmpp-core/simplesasl.h b/src/xmpp/xmpp-core/simplesasl.h index c6d1769d..62acf1dd 100644 --- a/src/xmpp/xmpp-core/simplesasl.h +++ b/src/xmpp/xmpp-core/simplesasl.h @@ -21,12 +21,11 @@ #define SIMPLESASL_H namespace QCA { - class Provider; +class Provider; } -namespace XMPP -{ - QCA::Provider* createProviderSimpleSASL(); +namespace XMPP { +QCA::Provider *createProviderSimpleSASL(); } -#endif +#endif // SIMPLESASL_H diff --git a/src/xmpp/xmpp-core/sm.cpp b/src/xmpp/xmpp-core/sm.cpp index 0c508b77..417a67cb 100644 --- a/src/xmpp/xmpp-core/sm.cpp +++ b/src/xmpp/xmpp-core/sm.cpp @@ -17,13 +17,12 @@ * */ +#include "sm.h" + #ifdef IRIS_SM_DEBUG #include #endif - -#include "sm.h" - using namespace XMPP; SMState::SMState() @@ -37,28 +36,23 @@ SMState::SMState() void SMState::resetCounters() { - received_count = 0; + received_count = 0; server_last_handled = 0; send_queue.clear(); } - -StreamManagement::StreamManagement(QObject *parent) - : QObject(parent) - , sm_started(false) - , sm_resumed(false) - , sm_stanzas_notify(0) - , sm_resend_pos(0) +StreamManagement::StreamManagement(QObject *parent) : + QObject(parent), sm_started(false), sm_resumed(false), sm_stanzas_notify(0), sm_resend_pos(0) { } void StreamManagement::reset() { - sm_started = false; - sm_resumed = false; - sm_stanzas_notify = 0; - sm_resend_pos = 0; - sm_timeout_data.elapsed_timer = QElapsedTimer(); + sm_started = false; + sm_resumed = false; + sm_stanzas_notify = 0; + sm_resend_pos = 0; + sm_timeout_data.elapsed_timer = QElapsedTimer(); sm_timeout_data.waiting_answer = false; } @@ -67,13 +61,13 @@ void StreamManagement::start(const QString &resumption_id) reset(); state_.resetCounters(); state_.resumption_id = resumption_id; - sm_started = true; + sm_started = true; sm_timeout_data.elapsed_timer.start(); } void StreamManagement::resume(quint32 last_handled) { - sm_resumed = true; + sm_resumed = true; sm_resend_pos = 0; processAcknowledgement(last_handled); sm_timeout_data.waiting_answer = false; @@ -83,7 +77,7 @@ void StreamManagement::resume(quint32 last_handled) void StreamManagement::setLocation(const QString &host, int port) { state_.resumption_location.host = host; - state_.resumption_location.port = port; + state_.resumption_location.port = quint16(port); } int StreamManagement::lastAckElapsed() const @@ -91,8 +85,8 @@ int StreamManagement::lastAckElapsed() const if (!sm_timeout_data.elapsed_timer.isValid()) return 0; - int msecs = sm_timeout_data.elapsed_timer.elapsed(); - int secs = msecs / 1000; + int msecs = int(sm_timeout_data.elapsed_timer.elapsed()); + int secs = msecs / 1000; if (msecs % 1000 != 0) ++secs; return secs; @@ -100,7 +94,7 @@ int StreamManagement::lastAckElapsed() const int StreamManagement::takeAckedCount() { - int cnt = sm_stanzas_notify; + int cnt = sm_stanzas_notify; sm_stanzas_notify = 0; return cnt; } @@ -149,7 +143,8 @@ void StreamManagement::processAcknowledgement(quint32 last_handled) if (f) { qDebug() << "Stream Management: [INF] Send queue length is changed: " << state_.send_queue.length(); if (state_.send_queue.isEmpty() && last_handled != state_.server_last_handled) - qDebug() << "Stream Management: [ERR] Send queue is empty but last_handled != server_last_handled " << last_handled << state_.server_last_handled; + qDebug() << "Stream Management: [ERR] Send queue is empty but last_handled != server_last_handled " + << last_handled << state_.server_last_handled; } #endif } diff --git a/src/xmpp/xmpp-core/sm.h b/src/xmpp/xmpp-core/sm.h index cbcd94c5..e85c85ec 100644 --- a/src/xmpp/xmpp-core/sm.h +++ b/src/xmpp/xmpp-core/sm.h @@ -20,73 +20,70 @@ #ifndef XMPP_SM_H #define XMPP_SM_H -#include -#include #include #include +#include +#include -#define NS_STREAM_MANAGEMENT "urn:xmpp:sm:3" +#define NS_STREAM_MANAGEMENT "urn:xmpp:sm:3" #define SM_TIMER_INTERVAL_SECS 40 -//#define IRIS_SM_DEBUG +// #define IRIS_SM_DEBUG -namespace XMPP -{ - class SMState - { - public: - SMState(); - void resetCounters(); - bool isResumption() const { return !resumption_id.isEmpty(); } - bool isEnabled() const { return enabled; } - bool isLocationValid() { return !resumption_location.host.isEmpty() && resumption_location.port != 0; } - void setEnabled(bool e) { enabled = e; } +namespace XMPP { +class SMState { +public: + SMState(); + void resetCounters(); + bool isResumption() const { return !resumption_id.isEmpty(); } + bool isEnabled() const { return enabled; } + bool isLocationValid() { return !resumption_location.host.isEmpty() && resumption_location.port != 0; } + void setEnabled(bool e) { enabled = e; } - public: - bool enabled; - quint32 received_count; - quint32 server_last_handled; - QQueue send_queue; - QString resumption_id; - struct { - QString host; - quint16 port; - } resumption_location; - }; +public: + bool enabled; + quint32 received_count; + quint32 server_last_handled; + QQueue send_queue; + QString resumption_id; + struct { + QString host; + quint16 port; + } resumption_location; +}; - class StreamManagement : QObject - { - public: - StreamManagement(QObject *parent = 0); - XMPP::SMState &state() { return state_; } - const XMPP::SMState &state() const { return state_; } - bool isActive() const { return sm_started || sm_resumed; } - bool isResumed() const { return sm_resumed; } - void reset(); - void start(const QString &resumption_id); - void resume(quint32 last_handled); - void setLocation(const QString &host, int port); - int lastAckElapsed() const; - int takeAckedCount(); - void countInputRawData(int bytes); - QDomElement getUnacknowledgedStanza(); - int addUnacknowledgedStanza(const QDomElement &e); - void processAcknowledgement(quint32 last_handled); - void markStanzaHandled(); - QDomElement generateRequestStanza(QDomDocument &doc); - QDomElement makeResponseStanza(QDomDocument &doc); +class StreamManagement : QObject { +public: + StreamManagement(QObject *parent = nullptr); + XMPP::SMState &state() { return state_; } + const XMPP::SMState &state() const { return state_; } + bool isActive() const { return sm_started || sm_resumed; } + bool isResumed() const { return sm_resumed; } + void reset(); + void start(const QString &resumption_id); + void resume(quint32 last_handled); + void setLocation(const QString &host, int port); + int lastAckElapsed() const; + int takeAckedCount(); + void countInputRawData(int bytes); + QDomElement getUnacknowledgedStanza(); + int addUnacknowledgedStanza(const QDomElement &e); + void processAcknowledgement(quint32 last_handled); + void markStanzaHandled(); + QDomElement generateRequestStanza(QDomDocument &doc); + QDomElement makeResponseStanza(QDomDocument &doc); - private: - SMState state_; - bool sm_started; - bool sm_resumed; - int sm_stanzas_notify; - int sm_resend_pos; - struct { - QElapsedTimer elapsed_timer; - bool waiting_answer = false; - } sm_timeout_data; - }; -} +private: + SMState state_; + bool sm_started; + bool sm_resumed; + int sm_stanzas_notify; + int sm_resend_pos; + struct { + QElapsedTimer elapsed_timer; + bool waiting_answer = false; + } sm_timeout_data; +}; +} // namespace XMPP -#endif //XMPP_SM_H +#endif // XMPP_SM_H diff --git a/src/xmpp/xmpp-core/stream.cpp b/src/xmpp/xmpp-core/stream.cpp index 8285055b..0384166c 100644 --- a/src/xmpp/xmpp-core/stream.cpp +++ b/src/xmpp/xmpp-core/stream.cpp @@ -41,55 +41,47 @@ - sasl anonymous */ -#include "xmpp.h" - -#include -#include -#include -#include -#include -#include -#include -//#include -#include - #include "bytestream.h" -#include "simplesasl.h" -#include "securestream.h" -#include "protocol.h" - #ifndef NO_IRISNET -#include "irisnetglobal_p.h" +#include "irisnet/corelib/irisnetglobal_p.h" #endif - +#include "protocol.h" +#include "securestream.h" +#include "simplesasl.h" #ifdef XMPP_TEST #include "td.h" #endif +#include +#include +#include +#include +#include +#include +#include +// #include +#include -//#define XMPP_DEBUG +// #define XMPP_DEBUG using namespace XMPP; -static Debug *debug_ptr = 0; -void XMPP::setDebug(Debug *p) -{ - debug_ptr = p; -} +static Debug *debug_ptr = nullptr; +void XMPP::setDebug(Debug *p) { debug_ptr = p; } static QByteArray randomArray(int size) { QByteArray a; a.resize(size); - for(int n = 0; n < size; ++n) - a[n] = (char)(256.0*rand()/(RAND_MAX+1.0)); + for (int n = 0; n < size; ++n) + a[n] = (char)(256.0 * rand() / (RAND_MAX + 1.0)); return a; } static QString genId() { // need SHA1 here - //if(!QCA::isSupported(QCA::CAP_SHA1)) + // if(!QCA::isSupported(QCA::CAP_SHA1)) // QCA::insertProvider(createProviderHash()); return QCA::Hash("sha1").hashToString(randomArray(128)); @@ -98,30 +90,21 @@ static QString genId() //---------------------------------------------------------------------------- // Stream //---------------------------------------------------------------------------- -static XmlProtocol *foo = 0; -Stream::Stream(QObject *parent) -:QObject(parent) -{ -} +static XmlProtocol *foo = nullptr; +Stream::Stream(QObject *parent) : QObject(parent) { } -Stream::~Stream() -{ -} +Stream::~Stream() { } Stanza Stream::createStanza(Stanza::Kind k, const Jid &to, const QString &type, const QString &id) { return Stanza(this, k, to, type, id); } -Stanza Stream::createStanza(const QDomElement &e) -{ - return Stanza(this, e); -} +Stanza Stream::createStanza(const QDomElement &e) { return Stanza(this, e); } QString Stream::xmlToString(const QDomElement &e, bool clip) { - if(!foo) - { + if (!foo) { foo = new CoreProtocol; #ifndef NO_IRISNET irisNetAddPostRoutine(cleanup); @@ -133,77 +116,64 @@ QString Stream::xmlToString(const QDomElement &e, bool clip) void Stream::cleanup() { delete foo; - foo = 0; + foo = nullptr; } //---------------------------------------------------------------------------- // ClientStream //---------------------------------------------------------------------------- -enum { - Idle, - Connecting, - WaitVersion, - WaitTLS, - NeedParams, - AuthAbort, - Active, - Closing -}; +enum { Idle, Connecting, WaitVersion, WaitTLS, NeedParams, AuthAbort, Active, Closing }; -enum { - Client, - Server -}; +enum { Client, Server }; -class ClientStream::Private -{ +class ClientStream::Private { public: Private() = default; void reset() { - state = Idle; - notify = 0; + state = Idle; + notify = 0; newStanzas = false; - sasl_ssf = 0; + sasl_ssf = 0; tls_warned = false; - using_tls = false; + using_tls = false; } - Jid jid; - QString server; - bool oldOnly = false; - bool mutualAuth = false; - AllowPlainType allowPlain = NoAllowPlain; - bool haveLocalAddr = false; - QHostAddress localAddr; - quint16 localPort; - QString connectHost; - int minimumSSF = 0; - int maximumSSF = 0; - QString sasl_mech; - QMap mechProviders; // mech to provider map - bool doBinding = true; + Jid jid; + QString server; + bool oldOnly = false; + bool mutualAuth = false; + AllowPlainType allowPlain = NoAllowPlain; + bool haveLocalAddr = false; + QHostAddress localAddr; + quint16 localPort; + QString connectHost; + int minimumSSF = 0; + int maximumSSF = 0; + QString sasl_mech; + QMap mechProviders; // mech to provider map + bool doBinding = true; bool in_rrsig = false; - Connector *conn = nullptr; - ByteStream *bs = nullptr; - TLSHandler *tlsHandler = nullptr; - QCA::TLS *tls = nullptr; - QCA::SASL *sasl = nullptr; - SecureStream *ss = nullptr; - CoreProtocol client; - CoreProtocol srv; - QString lang; + Connector *conn = nullptr; + ByteStream *bs = nullptr; + TLSHandler *tlsHandler = nullptr; + QCA::TLS *tls = nullptr; + QCA::SASL *sasl = nullptr; + SecureStream *ss = nullptr; + CoreProtocol client; + CoreProtocol srv; + QString lang; QString defRealm; - int mode; - int state = Idle; - int notify = 0; + int mode; + int state = Idle; + int notify = 0; bool newStanzas = false; - int sasl_ssf = 0; + int sasl_ssf = 0; bool tls_warned = false; bool using_tls; bool doAuth; @@ -211,23 +181,22 @@ class ClientStream::Private QStringList sasl_mechlist; - int errCond; - QString errText; - QHash errLangText; // xml:lang => error text - QDomElement errAppSpec; + int errCond; + QString errText; + QHash errLangText; // xml:lang => error text + QDomElement errAppSpec; - QList in; + QList in; QTimer timeout_timer; QTimer noopTimer; - int noop_time; - bool quiet_reconnection = false; + int noop_time; + bool quiet_reconnection = false; }; -ClientStream::ClientStream(Connector *conn, TLSHandler *tlsHandler, QObject *parent) -:Stream(parent) +ClientStream::ClientStream(Connector *conn, TLSHandler *tlsHandler, QObject *parent) : Stream(parent) { - d = new Private; + d = new Private; d->mode = Client; d->conn = conn; connect(d->conn, SIGNAL(connected()), SLOT(cr_connected())); @@ -239,17 +208,17 @@ ClientStream::ClientStream(Connector *conn, TLSHandler *tlsHandler, QObject *par d->tlsHandler = tlsHandler; } -ClientStream::ClientStream(const QString &host, const QString &defRealm, ByteStream *bs, QCA::TLS *tls, QObject *parent) -:Stream(parent) +ClientStream::ClientStream(const QString &host, const QString &defRealm, ByteStream *bs, QCA::TLS *tls, + QObject *parent) : Stream(parent) { - d = new Private; + d = new Private; d->mode = Server; - d->bs = bs; + d->bs = bs; connect(d->bs, SIGNAL(connectionClosed()), SLOT(bs_connectionClosed())); connect(d->bs, SIGNAL(delayedCloseFinished()), SLOT(bs_delayedCloseFinished())); connect(d->bs, SIGNAL(error(int)), SLOT(bs_error(int))); - QByteArray spare = d->bs->readAll(); + [[maybe_unused]] QByteArray spare = d->bs->readAll(); d->ss = new SecureStream(d->bs); connect(d->ss, SIGNAL(readyRead()), SLOT(ss_readyRead())); @@ -258,66 +227,66 @@ ClientStream::ClientStream(const QString &host, const QString &defRealm, ByteStr connect(d->ss, SIGNAL(tlsClosed()), SLOT(ss_tlsClosed())); connect(d->ss, SIGNAL(error(int)), SLOT(ss_error(int))); - d->server = host; + d->server = host; d->defRealm = defRealm; d->tls = tls; d->srv.startClientIn(genId()); - //d->srv.startServerIn(genId()); - //d->state = Connecting; - //d->jid = Jid(); - //d->server = QString(); + // d->srv.startServerIn(genId()); + // d->state = Connecting; + // d->jid = Jid(); + // d->server = QString(); connect(&(d->timeout_timer), SIGNAL(timeout()), SLOT(sm_timeout())); } ClientStream::~ClientStream() { - //fprintf(stderr, "\tClientStream::~ClientStream\n"); - //fflush(stderr); + // fprintf(stderr, "\tClientStream::~ClientStream\n"); + // fflush(stderr); reset(); delete d; } void ClientStream::reset(bool all) { - //fprintf(stderr, "\tClientStream::reset\n"); - //fflush(stderr); + // fprintf(stderr, "\tClientStream::reset\n"); + // fflush(stderr); d->reset(); d->noopTimer.stop(); // delete securestream delete d->ss; - d->ss = 0; + d->ss = nullptr; // reset sasl delete d->sasl; - d->sasl = 0; + d->sasl = nullptr; - if(all) { + if (all) { while (!d->in.isEmpty()) { delete d->in.takeFirst(); } } else { QSharedPointer sd; - foreach (Stanza *s, d->in) { + for (Stanza *s : std::as_const(d->in)) { sd = s->unboundDocument(sd); } } // client - if(d->mode == Client) { + if (d->mode == Client) { // reset tls // FIXME: Temporarily disabled - //if(d->tlsHandler) + // if(d->tlsHandler) // d->tlsHandler->reset(); // reset connector - if(d->bs) { + if (d->bs) { d->bs->close(); - d->bs = 0; + d->bs = nullptr; } d->conn->done(); @@ -326,28 +295,25 @@ void ClientStream::reset(bool all) } // server else { - if(d->tls) + if (d->tls) d->tls->reset(); - if(d->bs) { + if (d->bs) { d->bs->close(); - d->bs = 0; + d->bs = nullptr; } d->srv.reset(); } } -Jid ClientStream::jid() const -{ - return d->jid; -} +Jid ClientStream::jid() const { return d->jid; } void ClientStream::connectToServer(const Jid &jid, bool auth) { reset(true); - d->state = Connecting; - d->jid = jid; + d->state = Connecting; + d->jid = jid; d->doAuth = auth; d->server = d->jid.domain(); @@ -356,18 +322,17 @@ void ClientStream::connectToServer(const Jid &jid, bool auth) void ClientStream::continueAfterWarning() { - if(d->state == WaitVersion) { + if (d->state == WaitVersion) { // if we don't have TLS yet, then we're never going to get it - if(!d->tls_warned && !d->using_tls) { + if (!d->tls_warned && !d->using_tls) { d->tls_warned = true; - d->state = WaitTLS; - warning(WarnNoTLS); + d->state = WaitTLS; + emit warning(WarnNoTLS); return; } d->state = Connecting; processNext(); - } - else if(d->state == WaitTLS) { + } else if (d->state == WaitTLS) { d->state = Connecting; processNext(); } @@ -379,59 +344,50 @@ void ClientStream::accept() processNext(); } -bool ClientStream::isActive() const -{ - return (d->state != Idle) ? true: false; -} +bool ClientStream::isActive() const { return d->state != Idle; } -bool ClientStream::isAuthenticated() const -{ - return (d->state == Active) ? true: false; -} +bool ClientStream::isAuthenticated() const { return d->state == Active; } void ClientStream::setUsername(const QString &s) { - if(d->sasl) + if (d->sasl) d->sasl->setUsername(s); } void ClientStream::setPassword(const QString &s) { - if(d->client.old) { + if (d->client.old) { d->client.setPassword(s); - } - else { - if(d->sasl) + } else { + if (d->sasl) d->sasl->setPassword(QCA::SecureArray(s.toUtf8())); } } void ClientStream::setRealm(const QString &s) { - if(d->sasl) + if (d->sasl) d->sasl->setRealm(s); } void ClientStream::setAuthzid(const QString &s) { - if(d->sasl) + if (d->sasl) d->sasl->setAuthzid(s); } void ClientStream::continueAfterParams() { - if(d->state == NeedParams) { + if (d->state == NeedParams) { d->state = Connecting; - if(d->client.old) { + if (d->client.old) { processNext(); - } - else { - if(d->sasl) + } else { + if (d->sasl) d->sasl->continueAfterParams(); } } else if (d->state == AuthAbort) { - auto e = doc().createElement("abort"); // FIXME it's kind of wrong to forge xml here - e.setAttribute("xmlns", NS_SASL); + auto e = doc().createElementNS(NS_SASL, "abort"); d->client.sendStanza(e); processNext(); } @@ -445,32 +401,28 @@ void ClientStream::abortAuth() d->state = AuthAbort; } -void ClientStream::setSaslMechanismProvider(const QString &m, const QString &p) -{ - d->mechProviders.insert(m, p); -} +void ClientStream::setSaslMechanismProvider(const QString &m, const QString &p) { d->mechProviders.insert(m, p); } -QString ClientStream::saslMechanismProvider(const QString &m) const -{ - return d->mechProviders.value(m); -} +QString ClientStream::saslMechanismProvider(const QString &m) const { return d->mechProviders.value(m); } QCA::Provider::Context *ClientStream::currentSASLContext() const { if (d->sasl) { return d->sasl->context(); } - return 0; + return nullptr; } -void ClientStream::setSCRAMStoredSaltedHash(const QString &s) { +void ClientStream::setSCRAMStoredSaltedHash(const QString &s) +{ QCA::SASLContext *context = (QCA::SASLContext *)(d->sasl->context()); if (context) { context->setProperty("scram-salted-password-base64", s); } } -const QString ClientStream::getSCRAMStoredSaltedHash() { +const QString ClientStream::getSCRAMStoredSaltedHash() +{ QCA::SASLContext *context = (QCA::SASLContext *)(d->sasl->context()); if (context) { return context->property("scram-salted-password-base64").toString(); @@ -478,114 +430,67 @@ const QString ClientStream::getSCRAMStoredSaltedHash() { return QString(); } -void ClientStream::setResourceBinding(bool b) -{ - d->doBinding = b; -} +void ClientStream::setResourceBinding(bool b) { d->doBinding = b; } -void ClientStream::setLang(const QString& lang) -{ - d->lang = lang; -} +void ClientStream::setLang(const QString &lang) { d->lang = lang; } void ClientStream::setNoopTime(int mills) { d->noop_time = mills; - if(d->state != Active) + if (d->state != Active) return; - if(d->noop_time == 0) { + if (d->noop_time == 0) { d->noopTimer.stop(); return; } d->noopTimer.start(d->noop_time); } -QString ClientStream::saslMechanism() const -{ - return d->client.saslMech(); -} +QString ClientStream::saslMechanism() const { return d->client.saslMech(); } -int ClientStream::saslSSF() const -{ - return d->sasl_ssf; -} +int ClientStream::saslSSF() const { return d->sasl_ssf; } -void ClientStream::setSASLMechanism(const QString &s) -{ - d->sasl_mech = s; -} +void ClientStream::setSASLMechanism(const QString &s) { d->sasl_mech = s; } void ClientStream::setLocalAddr(const QHostAddress &addr, quint16 port) { d->haveLocalAddr = true; - d->localAddr = addr; - d->localPort = port; + d->localAddr = addr; + d->localPort = port; } -void ClientStream::setCompress(bool compress) -{ - d->doCompress = compress; -} +void ClientStream::setCompress(bool compress) { d->doCompress = compress; } -int ClientStream::errorCondition() const -{ - return d->errCond; -} +int ClientStream::errorCondition() const { return d->errCond; } -QString ClientStream::errorText() const -{ - return d->errText; -} +QString ClientStream::errorText() const { return d->errText; } +QHash ClientStream::errorLangText() const { return d->errLangText; } -QHash ClientStream::errorLangText() const -{ - return d->errLangText; -} - -QDomElement ClientStream::errorAppSpec() const -{ - return d->errAppSpec; -} +QDomElement ClientStream::errorAppSpec() const { return d->errAppSpec; } -bool ClientStream::old() const -{ - return d->client.old; -} +bool ClientStream::old() const { return d->client.old; } void ClientStream::close() { - if(d->state == Active) { + if (d->state == Active) { d->state = Closing; d->client.shutdown(); processNext(); - } - else if(d->state != Idle && d->state != Closing) { + } else if (d->state != Idle && d->state != Closing) { reset(); } } -QDomDocument & ClientStream::doc() const -{ - return d->client.doc; -} +QDomDocument &ClientStream::doc() const { return d->client.doc; } -QString ClientStream::baseNS() const -{ - return NS_CLIENT; -} +QString ClientStream::baseNS() const { return NS_CLIENT; } -void ClientStream::setAllowPlain(AllowPlainType a) -{ - d->allowPlain = a; -} +void ClientStream::setAllowPlain(AllowPlainType a) { d->allowPlain = a; } -void ClientStream::setRequireMutualAuth(bool b) -{ - d->mutualAuth = b; -} +void ClientStream::setRequireMutualAuth(bool b) { d->mutualAuth = b; } void ClientStream::setSSFRange(int low, int high) { @@ -593,23 +498,17 @@ void ClientStream::setSSFRange(int low, int high) d->maximumSSF = high; } -void ClientStream::setOldOnly(bool b) -{ - d->oldOnly = b; -} +void ClientStream::setOldOnly(bool b) { d->oldOnly = b; } -bool ClientStream::stanzaAvailable() const -{ - return (!d->in.isEmpty()); -} +bool ClientStream::stanzaAvailable() const { return (!d->in.isEmpty()); } Stanza ClientStream::read() { - if(d->in.isEmpty()) + if (d->in.isEmpty()) return Stanza(); else { Stanza *sp = d->in.takeFirst(); - Stanza s = *sp; + Stanza s = *sp; delete sp; return s; } @@ -617,21 +516,18 @@ Stanza ClientStream::read() void ClientStream::write(const Stanza &s) { - if(d->state == Active) { + if (d->state == Active) { d->client.sendStanza(s.element()); processNext(); } } -void ClientStream::clearSendQueue() -{ - d->client.clearSendQueue(); -} +void ClientStream::clearSendQueue() { d->client.clearSendQueue(); } void ClientStream::cr_connected() { d->connectHost = d->conn->host(); - d->bs = d->conn->stream(); + d->bs = d->conn->stream(); connect(d->bs, SIGNAL(connectionClosed()), SLOT(bs_connectionClosed())); connect(d->bs, SIGNAL(delayedCloseFinished()), SLOT(bs_delayedCloseFinished())); @@ -644,11 +540,11 @@ void ClientStream::cr_connected() connect(d->ss, SIGNAL(tlsClosed()), SLOT(ss_tlsClosed())); connect(d->ss, SIGNAL(error(int)), SLOT(ss_error(int))); - //d->client.startDialbackOut("andbit.net", "im.pyxa.org"); - //d->client.startServerOut(d->server); + // d->client.startDialbackOut("andbit.net", "im.pyxa.org"); + // d->client.startServerOut(d->server); d->client.startClientOut(d->jid, d->oldOnly, d->conn->useSSL(), d->doAuth, d->doCompress); - d->client.setAllowTLS(d->tlsHandler ? true: false); + d->client.setAllowTLS(d->tlsHandler != nullptr); d->client.setAllowBind(d->doBinding); d->client.setAllowPlain(d->allowPlain == AllowPlain || (d->allowPlain == AllowPlainOverTLS && d->conn->useSSL())); d->client.setLang(d->lang); @@ -664,15 +560,14 @@ void ClientStream::cr_connected() QPointer self = this; if (!d->quiet_reconnection) emit connected(); - if(!self) + if (!self) return; // immediate SSL? - if(d->conn->useSSL()) { + if (d->conn->useSSL()) { d->using_tls = true; d->ss->startTLSClient(d->tlsHandler, d->server, spare); - } - else { + } else { d->client.addIncomingData(spare); processNext(); } @@ -681,13 +576,13 @@ void ClientStream::cr_connected() void ClientStream::cr_error() { reset(); - error(ErrConnection); + emit error(ErrConnection); } void ClientStream::bs_connectionClosed() { reset(); - connectionClosed(); + emit connectionClosed(); } void ClientStream::bs_delayedCloseFinished() @@ -708,15 +603,14 @@ void ClientStream::ss_readyRead() qDebug("ClientStream: recv: %d [%s]\n", a.size(), a.data()); #endif - if(d->mode == Client) { + if (d->mode == Client) { d->client.addIncomingData(a); d->client.sm.countInputRawData(a.size()); - } - else { + } else { d->srv.addIncomingData(a); d->srv.sm.countInputRawData(a.size()); } - if(d->notify & CoreProtocol::NRecv) { + if (d->notify & CoreProtocol::NRecv) { #ifdef XMPP_DEBUG qDebug("We needed data, so let's process it\n"); #endif @@ -726,12 +620,12 @@ void ClientStream::ss_readyRead() void ClientStream::ss_bytesWritten(qint64 bytes) { - if(d->mode == Client) - d->client.outgoingDataWritten(bytes); + if (d->mode == Client) + d->client.outgoingDataWritten(int(bytes)); else - d->srv.outgoingDataWritten(bytes); + d->srv.outgoingDataWritten(int(bytes)); - if(d->notify & CoreProtocol::NSend) { + if (d->notify & CoreProtocol::NSend) { #ifdef XMPP_DEBUG qDebug("We were waiting for data to be written, so let's process\n"); #endif @@ -743,8 +637,8 @@ void ClientStream::ss_tlsHandshaken() { QPointer self = this; if (!d->quiet_reconnection) - securityLayerActivated(LayerTLS); - if(!self) + emit securityLayerActivated(LayerTLS); + if (!self) return; d->client.setAllowPlain(d->allowPlain == AllowPlain || d->allowPlain == AllowPlainOverTLS); processNext(); @@ -753,70 +647,68 @@ void ClientStream::ss_tlsHandshaken() void ClientStream::ss_tlsClosed() { reset(); - connectionClosed(); + emit connectionClosed(); } void ClientStream::ss_error(int x) { - if(x == SecureStream::ErrTLS) { + if (x == SecureStream::ErrTLS) { reset(); d->errCond = TLSFail; - error(ErrTLS); - } - else { + emit error(ErrTLS); + } else { reset(); - error(ErrSecurityLayer); + emit error(ErrSecurityLayer); } } -void ClientStream::sasl_clientFirstStep(bool, const QByteArray& ba) +void ClientStream::sasl_clientFirstStep(bool, const QByteArray &ba) { d->client.setSASLFirst(d->sasl->mechanism(), ba); - //d->client.sasl_mech = mech; - //d->client.sasl_firstStep = stepData ? true : false; - //d->client.sasl_step = stepData ? *stepData : QByteArray(); + // d->client.sasl_mech = mech; + // d->client.sasl_firstStep = stepData ? true : false; + // d->client.sasl_step = stepData ? *stepData : QByteArray(); processNext(); } void ClientStream::sasl_nextStep(const QByteArray &stepData) { - if(d->mode == Client) + if (d->mode == Client) d->client.setSASLNext(stepData); - //d->client.sasl_step = stepData; + // d->client.sasl_step = stepData; else d->srv.setSASLNext(stepData); - //d->srv.sasl_step = stepData; + // d->srv.sasl_step = stepData; processNext(); } -void ClientStream::sasl_needParams(const QCA::SASL::Params& p) +void ClientStream::sasl_needParams(const QCA::SASL::Params &p) { #ifdef XMPP_DEBUG qDebug("need params: needUsername: %d, canSendAuthzid: %d, needPassword: %d, canSendRealm: %d\n", - p.needUsername()?1:0, p.canSendAuthzid()? 1:0, p.needPassword()? 1:0, p.canSendRealm()? 1:0); + p.needUsername() ? 1 : 0, p.canSendAuthzid() ? 1 : 0, p.needPassword() ? 1 : 0, p.canSendRealm() ? 1 : 0); #endif /*if(p.authzid && !p.user) { d->sasl->setAuthzid(d->jid.bare()); //d->sasl->setAuthzid("infiniti.homelesshackers.org"); }*/ - if(p.needUsername() || p.needPassword() || p.canSendRealm()) { + if (p.needUsername() || p.needPassword() || p.canSendRealm()) { d->state = NeedParams; - needAuthParams(p.needUsername(), p.needPassword(), p.canSendRealm()); - } - else + emit needAuthParams(p.needUsername(), p.needPassword(), p.canSendRealm()); + } else d->sasl->continueAfterParams(); } void ClientStream::sasl_authCheck(const QString &user, const QString &) { -//#ifdef XMPP_DEBUG -// qDebug("authcheck: [%s], [%s]\n", user.latin1(), authzid.latin1()); -//#endif + // #ifdef XMPP_DEBUG + // qDebug("authcheck: [%s], [%s]\n", user.latin1(), authzid.latin1()); + // #endif QString u = user; - int n = u.indexOf('@'); - if(n != -1) + int n = u.indexOf('@'); + if (n != -1) u.truncate(n); d->srv.user = u; d->sasl->continueAfterAuthCheck(); @@ -829,7 +721,7 @@ void ClientStream::sasl_authenticated() #endif d->sasl_ssf = d->sasl->ssf(); - if(d->mode == Server) { + if (d->mode == Server) { d->srv.setSASLAuthed(); processNext(); } @@ -841,39 +733,38 @@ void ClientStream::sasl_error() qDebug("sasl error: %d\n", d->sasl->authCondition()); #endif // has to be auth error - int x = convertedSASLCond(); + int x = convertedSASLCond(); d->errText = tr("Offered mechanisms: ") + d->client.features.sasl_mechs.join(", "); reset(); d->errCond = x; - error(ErrAuth); + emit error(ErrAuth); } void ClientStream::srvProcessNext() { - while(1) { + while (1) { qDebug("Processing step...\n"); - if(!d->srv.processStep()) { + if (!d->srv.processStep()) { int need = d->srv.need; - if(need == CoreProtocol::NNotify) { + if (need == CoreProtocol::NNotify) { d->notify = d->srv.notify; - if(d->notify & CoreProtocol::NSend) + if (d->notify & CoreProtocol::NSend) qDebug("More data needs to be written to process next step\n"); - if(d->notify & CoreProtocol::NRecv) + if (d->notify & CoreProtocol::NRecv) qDebug("More data is needed to process next step\n"); - } - else if(need == CoreProtocol::NSASLMechs) { - if(!d->sasl) { + } else if (need == CoreProtocol::NSASLMechs) { + if (!d->sasl) { d->sasl = new QCA::SASL; - connect(d->sasl, SIGNAL(authCheck(QString,QString)), SLOT(sasl_authCheck(QString,QString))); + connect(d->sasl, SIGNAL(authCheck(QString, QString)), SLOT(sasl_authCheck(QString, QString))); connect(d->sasl, SIGNAL(nextStep(QByteArray)), SLOT(sasl_nextStep(QByteArray))); connect(d->sasl, SIGNAL(authenticated()), SLOT(sasl_authenticated())); connect(d->sasl, SIGNAL(error()), SLOT(sasl_error())); - //d->sasl->setAllowAnonymous(false); - //d->sasl->setRequirePassCredentials(true); - //d->sasl->setExternalAuthID("localhost"); - QCA::SASL::AuthFlags auth_flags = (QCA::SASL::AuthFlags) 0; - d->sasl->setConstraints(auth_flags,0,256); + // d->sasl->setAllowAnonymous(false); + // d->sasl->setRequirePassCredentials(true); + // d->sasl->setExternalAuthID("localhost"); + QCA::SASL::AuthFlags auth_flags = (QCA::SASL::AuthFlags)0; + d->sasl->setConstraints(auth_flags, 0, 256); QStringList list; // TODO: d->server is probably wrong here @@ -882,30 +773,26 @@ void ClientStream::srvProcessNext() } d->srv.setSASLMechList(d->sasl_mechlist); continue; - } - else if(need == CoreProtocol::NStartTLS) { + } else if (need == CoreProtocol::NStartTLS) { qDebug("Need StartTLS\n"); - //if(!d->tls->startServer()) { + // if(!d->tls->startServer()) { d->tls->startServer(); QByteArray a = d->srv.spare; d->ss->startTLSServer(d->tls, a); - } - else if(need == CoreProtocol::NSASLFirst) { + } else if (need == CoreProtocol::NSASLFirst) { qDebug("Need SASL First Step\n"); QByteArray a = d->srv.saslStep(); d->sasl->putServerFirstStep(d->srv.saslMech(), a); - } - else if(need == CoreProtocol::NSASLNext) { + } else if (need == CoreProtocol::NSASLNext) { qDebug("Need SASL Next Step\n"); QByteArray a = d->srv.saslStep(); qDebug("[%s]\n", a.data()); d->sasl->putStep(a); - } - else if(need == CoreProtocol::NSASLLayer) { + } else if (need == CoreProtocol::NSASLLayer) { } // now we can announce stanzas - //if(!d->in.isEmpty()) + // if(!d->in.isEmpty()) // readyRead(); return; } @@ -914,101 +801,99 @@ void ClientStream::srvProcessNext() int event = d->srv.event; qDebug("event: %d\n", event); - switch(event) { - case CoreProtocol::EError: { - qDebug("Error! Code=%d\n", d->srv.errorCode); - reset(); - error(ErrProtocol); - //handleError(); - return; - } - case CoreProtocol::ESend: { - while (true) { - QByteArray a = d->srv.takeOutgoingData(); - if (a.isEmpty()) - break; + switch (event) { + case CoreProtocol::EError: { + qDebug("Error! Code=%d\n", d->srv.errorCode); + reset(); + emit error(ErrProtocol); + // handleError(); + return; + } + case CoreProtocol::ESend: { + while (true) { + QByteArray a = d->srv.takeOutgoingData(); + if (a.isEmpty()) + break; #ifdef XMPP_DEBUG - qDebug("Need Send: {%s}\n", a.data()); + qDebug("Need Send: {%s}\n", a.data()); #endif - d->ss->write(a); - } - break; - } - case CoreProtocol::ERecvOpen: { - qDebug("Break (RecvOpen)\n"); - - // calculate key - QByteArray str = QCA::Hash("sha1").hashToString("secret").toUtf8(); - str = QCA::Hash("sha1").hashToString(QByteArray(str + "im.pyxa.org")).toUtf8(); - str = QCA::Hash("sha1").hashToString(QByteArray(str + d->srv.id.toUtf8())).toUtf8(); - d->srv.setDialbackKey(str); - - //d->srv.setDialbackKey("3c5d721ea2fcc45b163a11420e4e358f87e3142a"); - - if(d->srv.to != d->server) { - // host-gone, host-unknown, see-other-host - d->srv.shutdownWithError(CoreProtocol::HostUnknown); - } - else - d->srv.setFrom(d->server); - break; - } - case CoreProtocol::ESASLSuccess: { - qDebug("Break SASL Success\n"); - disconnect(d->sasl, SIGNAL(error()), this, SLOT(sasl_error())); - QByteArray a = d->srv.spare; - d->ss->setLayerSASL(d->sasl, a); - break; - } - case CoreProtocol::EPeerClosed: { - // TODO: this isn' an error - qDebug("peer closed\n"); - reset(); - error(ErrProtocol); - return; + d->ss->write(a); } + break; + } + case CoreProtocol::ERecvOpen: { + qDebug("Break (RecvOpen)\n"); + + // calculate key + QByteArray str = QCA::Hash("sha1").hashToString("secret").toUtf8(); + str = QCA::Hash("sha1").hashToString(QByteArray(str + "im.pyxa.org")).toUtf8(); + str = QCA::Hash("sha1").hashToString(QByteArray(str + d->srv.id.toUtf8())).toUtf8(); + d->srv.setDialbackKey(str); + + // d->srv.setDialbackKey("3c5d721ea2fcc45b163a11420e4e358f87e3142a"); + + if (d->srv.to != d->server) { + // host-gone, host-unknown, see-other-host + d->srv.shutdownWithError(CoreProtocol::HostUnknown); + } else + d->srv.setFrom(d->server); + break; + } + case CoreProtocol::ESASLSuccess: { + qDebug("Break SASL Success\n"); + disconnect(d->sasl, SIGNAL(error()), this, SLOT(sasl_error())); + QByteArray a = d->srv.spare; + d->ss->setLayerSASL(d->sasl, a); + break; + } + case CoreProtocol::EPeerClosed: { + // TODO: this isn' an error + qDebug("peer closed\n"); + reset(); + emit error(ErrProtocol); + return; + } } } } void ClientStream::doReadyRead() { - //QGuardedPtr self = this; + // QGuardedPtr self = this; if (isActive()) emit readyRead(); - //if(!self) + // if(!self) // return; - //d->in_rrsig = false; + // d->in_rrsig = false; } void ClientStream::processNext() { - if(d->mode == Server) { + if (d->mode == Server) { srvProcessNext(); return; } QPointer self = this; - while(1) { + while (1) { #ifdef XMPP_DEBUG qDebug("Processing step...\n"); #endif bool ok = d->client.processStep(); // deal with send/received items - foreach (const XmlProtocol::TransferItem &i, d->client.transferItemList) { - if(i.isExternal) + for (const XmlProtocol::TransferItem &i : std::as_const(d->client.transferItemList)) { + if (i.isExternal) continue; QString str; - if(i.isString) { + if (i.isString) { // skip whitespace pings - if(i.str.trimmed().isEmpty()) + if (i.str.trimmed().isEmpty()) continue; str = i.str; - } - else + } else str = d->client.elementToString(i.elem); - if(i.isSent) + if (i.isSent) emit outgoingXml(str); else emit incomingXml(str); @@ -1017,9 +902,9 @@ void ClientStream::processNext() #ifdef XMPP_DEBUG qDebug("\tNOTIFY: %d\n", d->client.notify); #endif - if (d->client.notify & CoreProtocol::NTimeout ) { + if (d->client.notify & CoreProtocol::NTimeout) { #ifdef XMPP_DEBUG - qDebug() << "Time = "<< d->client.timeout_sec; + qDebug() << "Time = " << d->client.timeout_sec; #endif setTimer(d->client.timeout_sec); #ifdef XMPP_DEBUG @@ -1027,175 +912,176 @@ void ClientStream::processNext() #endif } - if(!ok) { + if (!ok) { bool cont = handleNeed(); // now we can announce stanzas - //if(!d->in_rrsig && !d->in.isEmpty()) { - if(!d->in.isEmpty()) { - //d->in_rrsig = true; - //fprintf(stderr, "\tClientStream::processNext() QTimer::singleShot\n"); - //fflush(stderr); + // if(!d->in_rrsig && !d->in.isEmpty()) { + if (!d->in.isEmpty()) { + // d->in_rrsig = true; + // fprintf(stderr, "\tClientStream::processNext() QTimer::singleShot\n"); + // fflush(stderr); QTimer::singleShot(0, this, SLOT(doReadyRead())); } - if(cont) + if (cont) continue; return; } int event = d->client.event; d->notify = 0; - switch(event) { - case CoreProtocol::EError: { + switch (event) { + case CoreProtocol::EError: { #ifdef XMPP_DEBUG - qDebug("Error! Code=%d\n", d->client.errorCode); + qDebug("Error! Code=%d\n", d->client.errorCode); #endif - handleError(); - return; - } - case CoreProtocol::ESend: { - while (true) { - QByteArray a = d->client.takeOutgoingData(); - if (a.isEmpty()) - break; + handleError(); + return; + } + case CoreProtocol::ESend: { + while (true) { + QByteArray a = d->client.takeOutgoingData(); + if (a.isEmpty()) + break; #ifdef XMPP_DEBUG - qDebug("Need Send: {%s}\n", a.data()); + qDebug("Need Send: {%s}\n", a.data()); #endif - d->ss->write(a); - } - break; + d->ss->write(a); } - case CoreProtocol::ERecvOpen: { + break; + } + case CoreProtocol::ERecvOpen: { #ifdef XMPP_DEBUG - qDebug("Break (RecvOpen)\n"); + qDebug("Break (RecvOpen)\n"); #endif #ifdef XMPP_TEST - QString s = QString("handshake success (lang=[%1]").arg(d->client.lang); - if(!d->client.from.isEmpty()) - s += QString(", from=[%1]").arg(d->client.from); - s += ')'; - TD::msg(s); + QString s = QString("handshake success (lang=[%1]").arg(d->client.lang); + if (!d->client.from.isEmpty()) + s += QString(", from=[%1]").arg(d->client.from); + s += ')'; + TD::msg(s); #endif - if(d->client.old) { - d->state = WaitVersion; - warning(WarnOldVersion); - return; - } - break; + if (d->client.old) { + d->state = WaitVersion; + emit warning(WarnOldVersion); + return; } - case CoreProtocol::EFeatures: { + break; + } + case CoreProtocol::EFeatures: { #ifdef XMPP_DEBUG - qDebug("Break (Features)\n"); + qDebug("Break (Features)\n"); #endif - if (d->client.unhandledFeatures.count()) { - emit haveUnhandledFeatures(); - } - if(!d->tls_warned && !d->using_tls && !d->client.features.tls_supported) { - d->tls_warned = true; - d->state = WaitTLS; - warning(WarnNoTLS); - return; - } - break; + if (d->client.unhandledFeatures.count()) { + emit haveUnhandledFeatures(); } - case CoreProtocol::ESASLSuccess: { -#ifdef XMPP_DEBUG - qDebug("Break SASL Success\n"); -#endif - break; + if (!d->tls_warned && !d->using_tls && !d->client.features.tls_supported) { + d->tls_warned = true; + d->state = WaitTLS; + emit warning(WarnNoTLS); + return; } - case CoreProtocol::EReady: { + break; + } + case CoreProtocol::ESASLSuccess: { #ifdef XMPP_DEBUG - qDebug("Done!\n"); + qDebug("Break SASL Success\n"); #endif - // grab the JID, in case it changed - d->jid = d->client.jid(); - d->state = Active; - setNoopTime(d->noop_time); - if (!d->quiet_reconnection) - authenticated(); - if(!self) - return; - break; - } - case CoreProtocol::EPeerClosed: { + break; + } + case CoreProtocol::EReady: { #ifdef XMPP_DEBUG - qDebug("DocumentClosed\n"); + qDebug("Done!\n"); #endif - reset(); - connectionClosed(); + // grab the JID, in case it changed + d->jid = d->client.jid(); + d->state = Active; + setNoopTime(d->noop_time); + if (!d->quiet_reconnection) + emit authenticated(); + if (!self) return; - } - case CoreProtocol::EStanzaReady: { + break; + } + case CoreProtocol::EPeerClosed: { #ifdef XMPP_DEBUG - qDebug("StanzaReady\n"); + qDebug("DocumentClosed\n"); #endif - // store the stanza for now, announce after processing all events - // TODO: add a method to the stanza to mark them handled. - Stanza s = createStanza(d->client.recvStanza()); - if(s.isNull()) - break; - if (d->client.sm.isActive()) - d->client.sm.markStanzaHandled(); - d->in.append(new Stanza(s)); - break; - } - case CoreProtocol::EStanzaSent: { + reset(); + emit connectionClosed(); + return; + } + case CoreProtocol::EStanzaReady: { #ifdef XMPP_DEBUG - qDebug("StanzasSent\n"); + qDebug("StanzaReady\n"); #endif - stanzaWritten(); - if(!self) - return; + // store the stanza for now, announce after processing all events + // TODO: add a method to the stanza to mark them handled. + Stanza s = createStanza(d->client.recvStanza()); + if (s.isNull()) break; - } - case CoreProtocol::EClosed: { + if (d->client.sm.isActive()) + d->client.sm.markStanzaHandled(); + d->in.append(new Stanza(s)); + break; + } + case CoreProtocol::EStanzaSent: { #ifdef XMPP_DEBUG - qDebug("Closed\n"); + qDebug("StanzasSent\n"); #endif - reset(); - delayedCloseFinished(); + emit stanzaWritten(); + if (!self) return; - } - case CoreProtocol::EAck: { - int ack_cnt = d->client.sm.takeAckedCount(); + break; + } + case CoreProtocol::EClosed: { #ifdef XMPP_DEBUG - qDebug() << "Stream Management: [INF] Received ack amount: " << ack_cnt; + qDebug("Closed\n"); #endif - emit stanzasAcked(ack_cnt); - break; - } - case CoreProtocol::ESMConnTimeout: { + reset(); + emit delayedCloseFinished(); + return; + } + case CoreProtocol::EAck: { + int ack_cnt = d->client.sm.takeAckedCount(); #ifdef XMPP_DEBUG - qDebug() << "Stream Management: [INF] Connection timeout"; + qDebug() << "Stream Management: [INF] Received ack amount: " << ack_cnt; #endif - reset(); - if (d->client.sm.state().isResumption()) { - d->state = Connecting; - emit warning(WarnSMReconnection); - d->quiet_reconnection = true; - if (d->client.sm.state().isLocationValid()) - d->conn->setOptHostPort(d->client.sm.state().resumption_location.host , d->client.sm.state().resumption_location.port); - d->conn->connectToServer(d->server); - } else { - d->quiet_reconnection = false; - emit connectionClosed(); - } - return; - } - case CoreProtocol::ESMResumeFailed: { + emit stanzasAcked(ack_cnt); + break; + } + case CoreProtocol::ESMConnTimeout: { #ifdef XMPP_DEBUG - qDebug() << "Stream Management: [INF] Resuming session failed"; + qDebug() << "Stream Management: [INF] Connection timeout"; #endif - reset(); + reset(); + if (d->client.sm.state().isResumption()) { + d->state = Connecting; + emit warning(WarnSMReconnection); + d->quiet_reconnection = true; + if (d->client.sm.state().isLocationValid()) + d->conn->setOptHostPort(d->client.sm.state().resumption_location.host, + d->client.sm.state().resumption_location.port); + d->conn->connectToServer(d->server); + } else { d->quiet_reconnection = false; - emit error(ErrSmResume); - return; + emit connectionClosed(); } + return; + } + case CoreProtocol::ESMResumeFailed: { +#ifdef XMPP_DEBUG + qDebug() << "Stream Management: [INF] Resuming session failed"; +#endif + reset(); + d->quiet_reconnection = false; + emit error(ErrSmResume); + return; + } } } } @@ -1203,146 +1089,146 @@ void ClientStream::processNext() bool ClientStream::handleNeed() { int need = d->client.need; - if(need == CoreProtocol::NNotify) { + if (need == CoreProtocol::NNotify) { d->notify = d->client.notify; #ifdef XMPP_DEBUG - if(d->notify & CoreProtocol::NSend) + if (d->notify & CoreProtocol::NSend) qDebug("More data needs to be written to process next step\n"); - if(d->notify & CoreProtocol::NRecv) + if (d->notify & CoreProtocol::NRecv) qDebug("More data is needed to process next step\n"); #endif return false; } d->notify = 0; - switch(need) { - case CoreProtocol::NStartTLS: { + switch (need) { + case CoreProtocol::NStartTLS: { #ifdef XMPP_DEBUG - qDebug("Need StartTLS\n"); + qDebug("Need StartTLS\n"); #endif - d->using_tls = true; - d->ss->startTLSClient(d->tlsHandler, d->server, d->client.spare); - return false; - } - case CoreProtocol::NCompress: { + d->using_tls = true; + d->ss->startTLSClient(d->tlsHandler, d->server, d->client.spare); + return false; + } + case CoreProtocol::NCompress: { #ifdef XMPP_DEBUG - qDebug("Need compress\n"); + qDebug("Need compress\n"); #endif - d->ss->setLayerCompress(d->client.spare); - return true; - } - case CoreProtocol::NSASLFirst: { + d->ss->setLayerCompress(d->client.spare); + return true; + } + case CoreProtocol::NSASLFirst: { #ifdef XMPP_DEBUG - qDebug("Need SASL First Step\n"); + qDebug("Need SASL First Step\n"); #endif - // ensure simplesasl provider is installed - bool found = false; - foreach(QCA::Provider *p, QCA::providers()) { - if(p->name() == "simplesasl") { - found = true; - break; - } - } - if(!found) { - // install with low-priority - QCA::insertProvider(createProviderSimpleSASL()); - QCA::setProviderPriority("simplesasl", 10); + // ensure simplesasl provider is installed + bool found = false; + const auto &providers = QCA::providers(); + for (QCA::Provider *p : providers) { + if (p->name() == "simplesasl") { + found = true; + break; } + } + if (!found) { + // install with low-priority + QCA::insertProvider(createProviderSimpleSASL()); + QCA::setProviderPriority("simplesasl", 10); + } + + static QStringList preference { "GSSAPI", "SCRAM-SHA-512-PLUS", "SCRAM-SHA-512", "SCRAM-SHA-384-PLUS", + "SCRAM-SHA-384", "SCRAM-SHA-256-PLUS", "SCRAM-SHA-256", "SCRAM-SHA-1-PLUS", + "SCRAM-SHA-1", "DIGEST-MD5", "PLAIN" }; + // TODO qca should maintain the list of preferred - static QStringList preference{ "GSSAPI", "SCRAM-SHA-512-PLUS", "SCRAM-SHA-512", - "SCRAM-SHA-384-PLUS", "SCRAM-SHA-384", - "SCRAM-SHA-256-PLUS", "SCRAM-SHA-256", - "SCRAM-SHA-1-PLUS", "SCRAM-SHA-1", - "DIGEST-MD5", "PLAIN" }; - // TODO qca should maintain the list of preferred - - QStringList ml; - if(!d->sasl_mech.isEmpty()) - ml += d->sasl_mech; - else { - QMap prefOrdered; - QStringList unpreferred; - for (auto const &m : d->client.features.sasl_mechs) { - int i = preference.indexOf(m); - if (i != -1) { - prefOrdered.insert(i, m); - } else { - unpreferred.append(m); - } + QStringList ml; + if (!d->sasl_mech.isEmpty()) + ml += d->sasl_mech; + else { + QMap prefOrdered; + QStringList unpreferred; + for (auto const &m : std::as_const(d->client.features.sasl_mechs)) { + int i = preference.indexOf(m); + if (i != -1) { + prefOrdered.insert(i, m); + } else { + unpreferred.append(m); } - ml = prefOrdered.values() + unpreferred; } + ml = prefOrdered.values() + unpreferred; + } - QString saslProvider; - foreach (const QString &mech, d->mechProviders.keys()) { - if (ml.contains(mech)) { - saslProvider = d->mechProviders[mech]; - break; - } + QString saslProvider; + const auto &meches = d->mechProviders.keys(); + for (const QString &mech : meches) { + if (ml.contains(mech)) { + saslProvider = d->mechProviders[mech]; + break; } + } - d->sasl = new QCA::SASL(0, saslProvider); - connect(d->sasl, SIGNAL(clientStarted(bool,QByteArray)), SLOT(sasl_clientFirstStep(bool,QByteArray))); - connect(d->sasl, SIGNAL(nextStep(QByteArray)), SLOT(sasl_nextStep(QByteArray))); - connect(d->sasl, SIGNAL(needParams(QCA::SASL::Params)), SLOT(sasl_needParams(QCA::SASL::Params))); - connect(d->sasl, SIGNAL(authenticated()), SLOT(sasl_authenticated())); - connect(d->sasl, SIGNAL(error()), SLOT(sasl_error())); - - if(d->haveLocalAddr) - d->sasl->setLocalAddress(d->localAddr.toString(), d->localPort); - if(d->conn->havePeerAddress()) - d->sasl->setRemoteAddress(d->conn->peerAddress().toString(), d->conn->peerPort()); - - //d->sasl_mech = "ANONYMOUS"; - //d->sasl->setRequirePassCredentials(true); - //d->sasl->setExternalAuthID("localhost"); - //d->sasl->setExternalSSF(64); - //d->sasl_mech = "EXTERNAL"; - - QCA::SASL::AuthFlags auth_flags = (QCA::SASL::AuthFlags) 0; - if (d->allowPlain == AllowPlain || (d->allowPlain == AllowPlainOverTLS && d->using_tls)) - auth_flags = (QCA::SASL::AuthFlags) (auth_flags | QCA::SASL::AllowPlain); - if (d->mutualAuth) - auth_flags = (QCA::SASL::AuthFlags) (auth_flags | QCA::SASL::RequireMutualAuth); - d->sasl->setConstraints(auth_flags,d->minimumSSF,d->maximumSSF); + d->sasl = new QCA::SASL(nullptr, saslProvider); + connect(d->sasl, SIGNAL(clientStarted(bool, QByteArray)), SLOT(sasl_clientFirstStep(bool, QByteArray))); + connect(d->sasl, SIGNAL(nextStep(QByteArray)), SLOT(sasl_nextStep(QByteArray))); + connect(d->sasl, SIGNAL(needParams(QCA::SASL::Params)), SLOT(sasl_needParams(QCA::SASL::Params))); + connect(d->sasl, SIGNAL(authenticated()), SLOT(sasl_authenticated())); + connect(d->sasl, SIGNAL(error()), SLOT(sasl_error())); + + if (d->haveLocalAddr) + d->sasl->setLocalAddress(d->localAddr.toString(), d->localPort); + if (d->conn->havePeerAddress()) + d->sasl->setRemoteAddress(d->conn->peerAddress().toString(), d->conn->peerPort()); + + // d->sasl_mech = "ANONYMOUS"; + // d->sasl->setRequirePassCredentials(true); + // d->sasl->setExternalAuthID("localhost"); + // d->sasl->setExternalSSF(64); + // d->sasl_mech = "EXTERNAL"; + + QCA::SASL::AuthFlags auth_flags = (QCA::SASL::AuthFlags)0; + if (d->allowPlain == AllowPlain || (d->allowPlain == AllowPlainOverTLS && d->using_tls)) + auth_flags = (QCA::SASL::AuthFlags)(auth_flags | QCA::SASL::AllowPlain); + if (d->mutualAuth) + auth_flags = (QCA::SASL::AuthFlags)(auth_flags | QCA::SASL::RequireMutualAuth); + d->sasl->setConstraints(auth_flags, d->minimumSSF, d->maximumSSF); #ifdef IRIS_SASLCONNECTHOST - d->sasl->startClient("xmpp", QUrl::toAce(d->connectHost), ml, QCA::SASL::AllowClientSendFirst); + d->sasl->startClient("xmpp", QUrl::toAce(d->connectHost), ml, QCA::SASL::AllowClientSendFirst); #else - d->sasl->startClient("xmpp", QUrl::toAce(d->server), ml, QCA::SASL::AllowClientSendFirst); + d->sasl->startClient("xmpp", QUrl::toAce(d->server), ml, QCA::SASL::AllowClientSendFirst); #endif - return false; - } - case CoreProtocol::NSASLNext: { + return false; + } + case CoreProtocol::NSASLNext: { #ifdef XMPP_DEBUG - qDebug("Need SASL Next Step\n"); + qDebug("Need SASL Next Step\n"); #endif - QByteArray a = d->client.saslStep(); - d->sasl->putStep(a); - return false; - } - case CoreProtocol::NSASLLayer: { - // SecureStream will handle the errors from this point - disconnect(d->sasl, SIGNAL(error()), this, SLOT(sasl_error())); - d->ss->setLayerSASL(d->sasl, d->client.spare); - if(d->sasl_ssf > 0) { - QPointer self = this; - if (!d->quiet_reconnection) - securityLayerActivated(LayerSASL); - if(!self) - return false; - } - break; + QByteArray a = d->client.saslStep(); + d->sasl->putStep(a); + return false; + } + case CoreProtocol::NSASLLayer: { + // SecureStream will handle the errors from this point + disconnect(d->sasl, SIGNAL(error()), this, SLOT(sasl_error())); + d->ss->setLayerSASL(d->sasl, d->client.spare); + if (d->sasl_ssf > 0) { + QPointer self = this; + if (!d->quiet_reconnection) + emit securityLayerActivated(LayerSASL); + if (!self) + return false; } - case CoreProtocol::NPassword: { + break; + } + case CoreProtocol::NPassword: { #ifdef XMPP_DEBUG - qDebug("Need Password\n"); + qDebug("Need Password\n"); #endif - d->state = NeedParams; - needAuthParams(false, true, false); - return false; - } + d->state = NeedParams; + emit needAuthParams(false, true, false); + return false; + } } return true; @@ -1351,13 +1237,13 @@ bool ClientStream::handleNeed() int ClientStream::convertedSASLCond() const { int x = d->sasl->authCondition(); - if(x == QCA::SASL::NoMechanism) + if (x == QCA::SASL::NoMechanism) return NoMech; - else if(x == QCA::SASL::BadProtocol) + else if (x == QCA::SASL::BadProtocol) return MalformedRequest; - else if(x == QCA::SASL::BadServer) + else if (x == QCA::SASL::BadServer) return BadServ; - else if(x == QCA::SASL::TooWeak) + else if (x == QCA::SASL::TooWeak) return MechTooWeak; else return GenericAuthError; @@ -1372,8 +1258,7 @@ void ClientStream::sm_timeout() int elapsed = d->client.sm.lastAckElapsed(); if (elapsed < d->client.timeout_sec) { setTimer(d->client.timeout_sec - elapsed); - } - else { + } else { d->client.timeout_sec = 0; processNext(); } @@ -1381,7 +1266,7 @@ void ClientStream::sm_timeout() void ClientStream::doNoop() { - if(d->state == Active) { + if (d->state == Active) { #ifdef XMPP_DEBUG qDebug("doPing\n"); #endif @@ -1392,7 +1277,7 @@ void ClientStream::doNoop() void ClientStream::writeDirect(const QString &s) { - if(d->state == Active) { + if (d->state == Active) { #ifdef XMPP_DEBUG qDebug("writeDirect\n"); #endif @@ -1404,209 +1289,275 @@ void ClientStream::writeDirect(const QString &s) void ClientStream::handleError() { int c = d->client.errorCode; - if(c == CoreProtocol::ErrParse) { + if (c == CoreProtocol::ErrParse) { reset(); - error(ErrParse); - } - else if(c == CoreProtocol::ErrProtocol) { + emit error(ErrParse); + } else if (c == CoreProtocol::ErrProtocol) { reset(); - error(ErrProtocol); - } - else if(c == CoreProtocol::ErrStream) { - int x = d->client.errCond; - QString text = d->client.errText; - auto langText = d->client.errLangText; - QDomElement appSpec = d->client.errAppSpec; + emit error(ErrProtocol); + } else if (c == CoreProtocol::ErrStream) { + int x = d->client.errCond; + QString text = d->client.errText; + auto langText = d->client.errLangText; + QDomElement appSpec = d->client.errAppSpec; int connErr = -1; - int strErr = -1; - - switch(x) { - case CoreProtocol::BadFormat: { break; } // should NOT happen (we send the right format) - case CoreProtocol::BadNamespacePrefix: { break; } // should NOT happen (we send prefixes) - case CoreProtocol::Conflict: { strErr = Conflict; break; } - case CoreProtocol::ConnectionTimeout: { strErr = ConnectionTimeout; break; } - case CoreProtocol::HostGone: { connErr = HostGone; break; } - case CoreProtocol::HostUnknown: { connErr = HostUnknown; break; } - case CoreProtocol::ImproperAddressing: { break; } // should NOT happen (we aren't a server) - case CoreProtocol::InternalServerError: { strErr = InternalServerError; break; } - case CoreProtocol::InvalidFrom: { strErr = InvalidFrom; break; } - case CoreProtocol::InvalidNamespace: { break; } // should NOT happen (we set the right ns) - case CoreProtocol::InvalidXml: { strErr = InvalidXml; break; } // shouldn't happen either, but just in case ... - case CoreProtocol::StreamNotAuthorized: { break; } // should NOT happen (we're not stupid) - case CoreProtocol::PolicyViolation: { strErr = PolicyViolation; break; } - case CoreProtocol::RemoteConnectionFailed: { connErr = RemoteConnectionFailed; break; } - case CoreProtocol::StreamReset: { strErr = StreamReset; break; } - case CoreProtocol::ResourceConstraint: { strErr = ResourceConstraint; break; } - case CoreProtocol::RestrictedXml: { strErr = InvalidXml; break; } // group with this one - case CoreProtocol::SeeOtherHost: { connErr = SeeOtherHost; break; } - case CoreProtocol::SystemShutdown: { strErr = SystemShutdown; break; } - case CoreProtocol::UndefinedCondition: { break; } // leave as null error - case CoreProtocol::UnsupportedEncoding: { break; } // should NOT happen (we send good encoding) - case CoreProtocol::UnsupportedStanzaType: { break; } // should NOT happen (we're not stupid) - case CoreProtocol::UnsupportedVersion: { connErr = UnsupportedVersion; break; } - case CoreProtocol::NotWellFormed: { strErr = InvalidXml; break; } // group with this one - default: { break; } + int strErr = -1; + + switch (x) { + case CoreProtocol::BadFormat: { + break; + } // should NOT happen (we send the right format) + case CoreProtocol::BadNamespacePrefix: { + break; + } // should NOT happen (we send prefixes) + case CoreProtocol::Conflict: { + strErr = Conflict; + break; + } + case CoreProtocol::ConnectionTimeout: { + strErr = ConnectionTimeout; + break; + } + case CoreProtocol::HostGone: { + connErr = HostGone; + break; + } + case CoreProtocol::HostUnknown: { + connErr = HostUnknown; + break; + } + case CoreProtocol::ImproperAddressing: { + break; + } // should NOT happen (we aren't a server) + case CoreProtocol::InternalServerError: { + strErr = InternalServerError; + break; + } + case CoreProtocol::InvalidFrom: { + strErr = InvalidFrom; + break; + } + case CoreProtocol::InvalidNamespace: { + break; + } // should NOT happen (we set the right ns) + case CoreProtocol::InvalidXml: { + strErr = InvalidXml; + break; + } // shouldn't happen either, but just in case ... + case CoreProtocol::StreamNotAuthorized: { + break; + } // should NOT happen (we're not stupid) + case CoreProtocol::PolicyViolation: { + strErr = PolicyViolation; + break; + } + case CoreProtocol::RemoteConnectionFailed: { + connErr = RemoteConnectionFailed; + break; + } + case CoreProtocol::StreamReset: { + strErr = StreamReset; + break; + } + case CoreProtocol::ResourceConstraint: { + strErr = ResourceConstraint; + break; + } + case CoreProtocol::RestrictedXml: { + strErr = InvalidXml; + break; + } // group with this one + case CoreProtocol::SeeOtherHost: { + connErr = SeeOtherHost; + break; + } + case CoreProtocol::SystemShutdown: { + strErr = SystemShutdown; + break; + } + case CoreProtocol::UndefinedCondition: { + break; + } // leave as null error + case CoreProtocol::UnsupportedEncoding: { + break; + } // should NOT happen (we send good encoding) + case CoreProtocol::UnsupportedStanzaType: { + break; + } // should NOT happen (we're not stupid) + case CoreProtocol::UnsupportedVersion: { + connErr = UnsupportedVersion; + break; + } + case CoreProtocol::NotWellFormed: { + strErr = InvalidXml; + break; + } // group with this one + default: { + break; + } } reset(); - d->errText = text; + d->errText = text; d->errLangText = langText; - d->errAppSpec = appSpec; - if(connErr != -1) { + d->errAppSpec = appSpec; + if (connErr != -1) { d->errCond = connErr; - error(ErrNeg); - } - else { - if(strErr != -1) + emit error(ErrNeg); + } else { + if (strErr != -1) d->errCond = strErr; else d->errCond = GenericStreamError; - error(ErrStream); + emit error(ErrStream); } - } - else if(c == CoreProtocol::ErrStartTLS) { + } else if (c == CoreProtocol::ErrStartTLS) { reset(); d->errCond = TLSStart; - error(ErrTLS); - } - else if(c == CoreProtocol::ErrAuth) { + emit error(ErrTLS); + } else if (c == CoreProtocol::ErrAuth) { int x = d->client.errCond; int r = GenericAuthError; - if(d->client.old) { - if(x == 401) // not authorized + if (d->client.old) { + if (x == 401) // not authorized r = NotAuthorized; - else if(x == 409) // conflict + else if (x == 409) // conflict r = GenericAuthError; - else if(x == 406) // not acceptable (this should NOT happen) + else if (x == 406) // not acceptable (this should NOT happen) r = GenericAuthError; - } - else { - switch(x) { - case CoreProtocol::Aborted: { r = GenericAuthError; break; } // should NOT happen (we never send ) - case CoreProtocol::AccountDisabled: { r = AccountDisabled; break; } // account temporrily disabled - case CoreProtocol::CredentialsExpired: { r = CredentialsExpired; break; } // credential expired - case CoreProtocol::EncryptionRequired: { r = EncryptionRequired; break; } // can't use mech without TLS - case CoreProtocol::IncorrectEncoding: { r = GenericAuthError; break; } // should NOT happen - case CoreProtocol::InvalidAuthzid: { r = InvalidAuthzid; break; } - case CoreProtocol::InvalidMech: { r = InvalidMech; break; } - case CoreProtocol::MalformedRequest: { r = MalformedRequest; break; } - case CoreProtocol::MechTooWeak: { r = MechTooWeak; break; } - case CoreProtocol::NotAuthorized: { r = NotAuthorized; break; } - case CoreProtocol::TemporaryAuthFailure: { r = TemporaryAuthFailure; break; } + } else { + switch (x) { + case CoreProtocol::Aborted: { + r = GenericAuthError; + break; + } // should NOT happen (we never send ) + case CoreProtocol::AccountDisabled: { + r = AccountDisabled; + break; + } // account temporrily disabled + case CoreProtocol::CredentialsExpired: { + r = CredentialsExpired; + break; + } // credential expired + case CoreProtocol::EncryptionRequired: { + r = EncryptionRequired; + break; + } // can't use mech without TLS + case CoreProtocol::IncorrectEncoding: { + r = GenericAuthError; + break; + } // should NOT happen + case CoreProtocol::InvalidAuthzid: { + r = InvalidAuthzid; + break; + } + case CoreProtocol::InvalidMech: { + r = InvalidMech; + break; + } + case CoreProtocol::MalformedRequest: { + r = MalformedRequest; + break; + } + case CoreProtocol::MechTooWeak: { + r = MechTooWeak; + break; + } + case CoreProtocol::NotAuthorized: { + r = NotAuthorized; + break; + } + case CoreProtocol::TemporaryAuthFailure: { + r = TemporaryAuthFailure; + break; + } } } reset(); - d->errCond = r; + d->errCond = r; d->errLangText = d->client.errLangText; - error(ErrAuth); - } - else if(c == CoreProtocol::ErrPlain) { + emit error(ErrAuth); + } else if (c == CoreProtocol::ErrPlain) { reset(); d->errCond = NoMech; - error(ErrAuth); - } - else if(c == CoreProtocol::ErrBind) { + emit error(ErrAuth); + } else if (c == CoreProtocol::ErrBind) { int r = -1; - if(d->client.errCond == CoreProtocol::BindBadRequest) { + if (d->client.errCond == CoreProtocol::BindBadRequest) { // should NOT happen - } - else if(d->client.errCond == CoreProtocol::BindNotAllowed) { + } else if (d->client.errCond == CoreProtocol::BindNotAllowed) { r = BindNotAllowed; - } - else if(d->client.errCond == CoreProtocol::BindConflict) { + } else if (d->client.errCond == CoreProtocol::BindConflict) { r = BindConflict; } - if(r != -1) { + if (r != -1) { reset(); d->errCond = r; - error(ErrBind); - } - else { + emit error(ErrBind); + } else { reset(); - error(ErrProtocol); + emit error(ErrProtocol); } } } -bool ClientStream::isResumed() const -{ - return d->client.sm.isResumed(); -} +bool ClientStream::isResumed() const { return d->client.sm.isResumed(); } -void ClientStream::setSMEnabled(bool e) -{ - d->client.sm.state().setEnabled(e); -} +void ClientStream::setSMEnabled(bool e) { d->client.sm.state().setEnabled(e); } void ClientStream::setTimer(int secs) { d->timeout_timer.setSingleShot(true); d->timeout_timer.start(secs * 1000); - d->client.notify &= ~ CoreProtocol::NTimeout; -} - -QStringList ClientStream::hosts() const -{ - return d->client.hosts; + d->client.notify &= ~CoreProtocol::NTimeout; } -const StreamFeatures &ClientStream::streamFeatures() const -{ - return d->client.features; -} +QStringList ClientStream::hosts() const { return d->client.hosts; } -QList ClientStream::unhandledFeatures() const -{ - return d->client.unhandledFeatures; -} +const StreamFeatures &ClientStream::streamFeatures() const { return d->client.features; } +QList ClientStream::unhandledFeatures() const { return d->client.unhandledFeatures; } //---------------------------------------------------------------------------- // Debug //---------------------------------------------------------------------------- -Debug::~Debug() -{ -} +Debug::~Debug() { } #ifdef XMPP_TEST -TD::TD() -{ -} +TD::TD() { } -TD::~TD() -{ -} +TD::~TD() { } void TD::msg(const QString &s) { - if(debug_ptr) + if (debug_ptr) debug_ptr->msg(s); } void TD::outgoingTag(const QString &s) { - if(debug_ptr) + if (debug_ptr) debug_ptr->outgoingTag(s); } void TD::incomingTag(const QString &s) { - if(debug_ptr) + if (debug_ptr) debug_ptr->incomingTag(s); } void TD::outgoingXml(const QDomElement &e) { - if(debug_ptr) + if (debug_ptr) debug_ptr->outgoingXml(e); } void TD::incomingXml(const QDomElement &e) { - if(debug_ptr) + if (debug_ptr) debug_ptr->incomingXml(e); } #endif diff --git a/src/xmpp/xmpp-core/td.h b/src/xmpp/xmpp-core/td.h index f20dbe6c..be3c54e0 100644 --- a/src/xmpp/xmpp-core/td.h +++ b/src/xmpp/xmpp-core/td.h @@ -3,8 +3,7 @@ #include -class TD -{ +class TD { public: TD(); ~TD(); @@ -16,5 +15,4 @@ class TD static void incomingXml(const QDomElement &); }; -#endif - +#endif // TESTDEBUG_H diff --git a/src/xmpp/xmpp-core/tlshandler.cpp b/src/xmpp/xmpp-core/tlshandler.cpp index e129e1fa..fdb48c2d 100644 --- a/src/xmpp/xmpp-core/tlshandler.cpp +++ b/src/xmpp/xmpp-core/tlshandler.cpp @@ -17,81 +17,71 @@ * */ +#include "qca.h" #include "xmpp.h" -#include -#include "qca.h" +#include +#include +#include using namespace XMPP; - // FIXME: remove this code once qca cert host checking works ... using namespace QCA; -#include // ip address string to binary (msb), adapted from jdns (adapted from qt) // return: size 4 = ipv4, size 16 = ipv6, size 0 = error static QByteArray ipaddr_str2bin(const QString &str) { // ipv6 - if(str.contains(':')) - { + if (str.contains(':')) { +#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) + QStringList parts = str.split(':', Qt::KeepEmptyParts); +#else QStringList parts = str.split(':', QString::KeepEmptyParts); - if(parts.count() < 3 || parts.count() > 8) +#endif + if (parts.count() < 3 || parts.count() > 8) return QByteArray(); QByteArray ipv6(16, 0); - int at = 16; - int fill = 9 - parts.count(); - for(int n = parts.count() - 1; n >= 0; --n) - { - if(at <= 0) + int at = 16; + int fill = 9 - parts.count(); + for (int n = parts.count() - 1; n >= 0; --n) { + if (at <= 0) return QByteArray(); - if(parts[n].isEmpty()) - { - if(n == parts.count() - 1) - { - if(!parts[n - 1].isEmpty()) + if (parts[n].isEmpty()) { + if (n == parts.count() - 1) { + if (!parts[n - 1].isEmpty()) return QByteArray(); ipv6[--at] = 0; ipv6[--at] = 0; - } - else if(n == 0) - { - if(!parts[n + 1].isEmpty()) + } else if (n == 0) { + if (!parts[n + 1].isEmpty()) return QByteArray(); ipv6[--at] = 0; ipv6[--at] = 0; - } - else - { - for(int i = 0; i < fill; ++i) - { - if(at <= 0) + } else { + for (int i = 0; i < fill; ++i) { + if (at <= 0) return QByteArray(); ipv6[--at] = 0; ipv6[--at] = 0; } } - } - else - { - if(parts[n].indexOf('.') == -1) - { + } else { + if (parts[n].indexOf('.') == -1) { bool ok; - int x = parts[n].toInt(&ok, 16); - if(!ok || x < 0 || x > 0xffff) + int x = parts[n].toInt(&ok, 16); + if (!ok || x < 0 || x > 0xffff) return QByteArray(); ipv6[--at] = x & 0xff; ipv6[--at] = (x >> 8) & 0xff; - } - else - { - if(n != parts.count() - 1) + } else { + if (n != parts.count() - 1) return QByteArray(); QByteArray buf = ipaddr_str2bin(parts[n]); - if(buf.isEmpty()) + if (buf.isEmpty()) return QByteArray(); ipv6[--at] = buf[3]; @@ -104,25 +94,25 @@ static QByteArray ipaddr_str2bin(const QString &str) } return ipv6; - } - else if(str.contains('.')) - { + } else if (str.contains('.')) { +#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) + QStringList parts = str.split('.', Qt::KeepEmptyParts); +#else QStringList parts = str.split('.', QString::KeepEmptyParts); - if(parts.count() != 4) +#endif + if (parts.count() != 4) return QByteArray(); QByteArray out(4, 0); - for(int n = 0; n < 4; ++n) - { + for (int n = 0; n < 4; ++n) { bool ok; - int x = parts[n].toInt(&ok); - if(!ok || x < 0 || x > 0xff) + int x = parts[n].toInt(&ok); + if (!ok || x < 0 || x > 0xff) return QByteArray(); out[n] = (unsigned char)x; } return out; - } - else + } else return QByteArray(); } @@ -135,47 +125,52 @@ static bool cert_match_domain(const QString &certname, const QString &acedomain) // KSSL strips trailing dot, even though the dot is probably not // legal anyway. (compat) - if(name.length() > 0 && name[name.length()-1] == '.') - name.truncate(name.length()-1); + if (name.length() > 0 && name[name.length() - 1] == '.') + name.truncate(name.length() - 1); // after our compatibility modifications, make sure the name isn't // empty. - if(name.isEmpty()) + if (name.isEmpty()) return false; // lowercase, for later performing case insensitive matching name = name.toLower(); // ensure the cert field contains valid characters only - if(QRegExp("[^a-z0-9\\.\\*\\-]").indexIn(name) >= 0) + if (QRegularExpression("[^a-z0-9\\.\\*\\-]").match(name).hasMatch()) return false; - // hack into parts, and require at least 1 part + // hack into parts, and require at least 1 part +#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) + QStringList parts_name = name.split('.', Qt::KeepEmptyParts); +#else QStringList parts_name = name.split('.', QString::KeepEmptyParts); - if(parts_name.isEmpty()) +#endif + if (parts_name.isEmpty()) return false; // KSSL checks to make sure the last two parts don't contain // wildcards. I don't know where it is written that this // should be done, but for compat sake we'll do it. - if(parts_name[parts_name.count()-1].contains('*')) + if (parts_name[parts_name.count() - 1].contains('*')) return false; - if(parts_name.count() >= 2 && parts_name[parts_name.count()-2].contains('*')) + if (parts_name.count() >= 2 && parts_name[parts_name.count() - 2].contains('*')) return false; - +#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) + QStringList parts_compare = acedomain.split('.', Qt::KeepEmptyParts); +#else QStringList parts_compare = acedomain.split('.', QString::KeepEmptyParts); - if(parts_compare.isEmpty()) +#endif + if (parts_compare.isEmpty()) return false; // don't allow empty parts - foreach(const QString &s, parts_name) - { - if(s.isEmpty()) + for (const QString &s : std::as_const(parts_name)) { + if (s.isEmpty()) return false; } - foreach(const QString &s, parts_compare) - { - if(s.isEmpty()) + for (const QString &s : parts_compare) { + if (s.isEmpty()) return false; } @@ -190,16 +185,19 @@ static bool cert_match_domain(const QString &certname, const QString &acedomain) // they reside in. // // First, make sure the number of parts is equal. - if(parts_name.count() != parts_compare.count()) + if (parts_name.count() != parts_compare.count()) return false; // Now compare each part - for(int n = 0; n < parts_name.count(); ++n) - { + for (int n = 0; n < parts_name.count(); ++n) { const QString &p1 = parts_name[n]; const QString &p2 = parts_compare[n]; - - if(!QRegExp(p1, Qt::CaseSensitive, QRegExp::Wildcard).exactMatch(p2)) +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) + if (!QRegExp(p1, Qt::CaseSensitive, QRegExp::Wildcard).exactMatch(p2)) +#else + // note, wildcards are by default anchored. so exact match + if (!QRegularExpression::fromWildcard(p1, Qt::CaseSensitive).match(p2).hasMatch()) +#endif return false; } @@ -216,53 +214,49 @@ static bool cert_match_ipaddress(const QString &certname, const QByteArray &ipad // KSSL accepts IPv6 in brackets, which is usually done for URIs, but // IMO sounds very strange for a certificate. We'll follow this // behavior anyway. (compat) - if(name.length() >= 2 && name[0] == '[' && name[name.length()-1] == ']') + if (name.length() >= 2 && name[0] == '[' && name[name.length() - 1] == ']') name = name.mid(1, name.length() - 2); // chop off brackets // after our compatibility modifications, make sure the name isn't // empty. - if(name.isEmpty()) + if (name.isEmpty()) return false; // convert to binary form QByteArray addr = ipaddr_str2bin(name); - if(addr.isEmpty()) + if (addr.isEmpty()) return false; // not the same? - if(addr != ipaddress) - return false; - - return true; + return addr == ipaddress; } static bool matchesHostName(const QCA::Certificate &cert, const QString &host) { QByteArray ipaddr = ipaddr_str2bin(host); - if(!ipaddr.isEmpty()) // ip address + if (!ipaddr.isEmpty()) // ip address { // check iPAddress - foreach(const QString &s, cert.subjectInfo().values(IPAddress)) - { - if(cert_match_ipaddress(s, ipaddr)) + const auto ipAddresses = cert.subjectInfo().values(IPAddress); + for (const QString &s : ipAddresses) { + if (cert_match_ipaddress(s, ipaddr)) return true; } // check dNSName - foreach(const QString &s, cert.subjectInfo().values(DNS)) - { - if(cert_match_ipaddress(s, ipaddr)) + const auto dnsNames = cert.subjectInfo().values(DNS); + for (const QString &s : dnsNames) { + if (cert_match_ipaddress(s, ipaddr)) return true; } // check commonName - foreach(const QString &s, cert.subjectInfo().values(CommonName)) - { - if(cert_match_ipaddress(s, ipaddr)) + const auto commonNames = cert.subjectInfo().values(CommonName); + for (const QString &s : commonNames) { + if (cert_match_ipaddress(s, ipaddr)) return true; } - } - else // domain + } else // domain { // lowercase QString name = host.toLower(); @@ -271,28 +265,28 @@ static bool matchesHostName(const QCA::Certificate &cert, const QString &host) name = QString::fromLatin1(QUrl::toAce(name)); // don't allow wildcards in the comparison host - if(name.contains('*')) + if (name.contains('*')) return false; // strip out trailing dot - if(name.length() > 0 && name[name.length()-1] == '.') - name.truncate(name.length()-1); + if (name.length() > 0 && name[name.length() - 1] == '.') + name.truncate(name.length() - 1); // make sure the name is not empty after our modifications - if(name.isEmpty()) + if (name.isEmpty()) return false; // check dNSName - foreach(const QString &s, cert.subjectInfo().values(DNS)) - { - if(cert_match_domain(s, name)) + const auto dnsNames = cert.subjectInfo().values(DNS); + for (const QString &s : dnsNames) { + if (cert_match_domain(s, name)) return true; } // check commonName - foreach(const QString &s, cert.subjectInfo().values(CommonName)) - { - if(cert_match_domain(s, name)) + const auto commonNames = cert.subjectInfo().values(CommonName); + for (const QString &s : commonNames) { + if (cert_match_domain(s, name)) return true; } } @@ -303,59 +297,43 @@ static bool matchesHostName(const QCA::Certificate &cert, const QString &host) //---------------------------------------------------------------------------- // TLSHandler //---------------------------------------------------------------------------- -TLSHandler::TLSHandler(QObject *parent) -:QObject(parent) -{ -} - -TLSHandler::~TLSHandler() -{ -} +TLSHandler::TLSHandler(QObject *parent) : QObject(parent) { } +TLSHandler::~TLSHandler() { } //---------------------------------------------------------------------------- // QCATLSHandler //---------------------------------------------------------------------------- -class QCATLSHandler::Private -{ +class QCATLSHandler::Private { public: QCA::TLS *tls; - int state, err; - QString host; - bool internalHostMatch; + int state, err; + QString host; + bool internalHostMatch; }; -QCATLSHandler::QCATLSHandler(QCA::TLS *parent) -:TLSHandler(parent) +QCATLSHandler::QCATLSHandler(QCA::TLS *parent) : TLSHandler(parent) { - d = new Private; + d = new Private; d->tls = parent; connect(d->tls, SIGNAL(handshaken()), SLOT(tls_handshaken())); connect(d->tls, SIGNAL(readyRead()), SLOT(tls_readyRead())); connect(d->tls, SIGNAL(readyReadOutgoing()), SLOT(tls_readyReadOutgoing())); connect(d->tls, SIGNAL(closed()), SLOT(tls_closed())); connect(d->tls, SIGNAL(error()), SLOT(tls_error())); - d->state = 0; - d->err = -1; + d->state = 0; + d->err = -1; d->internalHostMatch = false; } -QCATLSHandler::~QCATLSHandler() -{ - delete d; -} +QCATLSHandler::~QCATLSHandler() { delete d; } -void QCATLSHandler::setXMPPCertCheck(bool enable) -{ - d->internalHostMatch = enable; -} -bool QCATLSHandler::XMPPCertCheck() -{ - return d->internalHostMatch; -} +void QCATLSHandler::setXMPPCertCheck(bool enable) { d->internalHostMatch = enable; } +bool QCATLSHandler::XMPPCertCheck() { return d->internalHostMatch; } bool QCATLSHandler::certMatchesHostname() { - if (!d->internalHostMatch) return false; + if (!d->internalHostMatch) + return false; QCA::CertificateChain peerCert = d->tls->peerCertificateChain(); if (matchesHostName(peerCert.primary(), d->host)) @@ -363,7 +341,8 @@ bool QCATLSHandler::certMatchesHostname() Jid host(d->host); - foreach( const QString &idOnXmppAddr, peerCert.primary().subjectInfo().values(QCA::XMPP) ) { + const auto hosts = peerCert.primary().subjectInfo().values(QCA::XMPP); + for (const QString &idOnXmppAddr : hosts) { if (host.compare(Jid(idOnXmppAddr))) return true; } @@ -371,16 +350,9 @@ bool QCATLSHandler::certMatchesHostname() return false; } +QCA::TLS *QCATLSHandler::tls() const { return d->tls; } -QCA::TLS *QCATLSHandler::tls() const -{ - return d->tls; -} - -int QCATLSHandler::tlsError() const -{ - return d->err; -} +int QCATLSHandler::tlsError() const { return d->err; } void QCATLSHandler::reset() { @@ -391,26 +363,21 @@ void QCATLSHandler::reset() void QCATLSHandler::startClient(const QString &host) { d->state = 0; - d->err = -1; - if (d->internalHostMatch) d->host = host; + d->err = -1; + if (d->internalHostMatch) + d->host = host; d->tls->startClient(d->internalHostMatch ? QString() : host); } -void QCATLSHandler::write(const QByteArray &a) -{ - d->tls->write(a); -} +void QCATLSHandler::write(const QByteArray &a) { d->tls->write(a); } -void QCATLSHandler::writeIncoming(const QByteArray &a) -{ - d->tls->writeIncoming(a); -} +void QCATLSHandler::writeIncoming(const QByteArray &a) { d->tls->writeIncoming(a); } void QCATLSHandler::continueAfterHandshake() { - if(d->state == 2) { + if (d->state == 2) { d->tls->continueAfterStep(); - success(); + emit success(); d->state = 3; } } @@ -418,29 +385,23 @@ void QCATLSHandler::continueAfterHandshake() void QCATLSHandler::tls_handshaken() { d->state = 2; - tlsHandshaken(); + emit tlsHandshaken(); } -void QCATLSHandler::tls_readyRead() -{ - readyRead(d->tls->read()); -} +void QCATLSHandler::tls_readyRead() { emit readyRead(d->tls->read()); } void QCATLSHandler::tls_readyReadOutgoing() { - int plainBytes; + int plainBytes; QByteArray buf = d->tls->readOutgoing(&plainBytes); - readyReadOutgoing(buf, plainBytes); + emit readyReadOutgoing(buf, plainBytes); } -void QCATLSHandler::tls_closed() -{ - closed(); -} +void QCATLSHandler::tls_closed() { emit closed(); } void QCATLSHandler::tls_error() { - d->err = d->tls->errorCode(); + d->err = d->tls->errorCode(); d->state = 0; - fail(); + emit fail(); } diff --git a/src/xmpp/xmpp-core/xmlprotocol.cpp b/src/xmpp/xmpp-core/xmlprotocol.cpp index e3774c23..ab89fed4 100644 --- a/src/xmpp/xmpp-core/xmlprotocol.cpp +++ b/src/xmpp/xmpp-core/xmlprotocol.cpp @@ -17,13 +17,14 @@ * */ -#include -#include -#include - #include "xmlprotocol.h" + #include "bytestream.h" +#include +#include +#include + using namespace XMPP; // stripExtraNS @@ -36,33 +37,33 @@ static QDomElement stripExtraNS(const QDomElement &e) { // find closest parent with a namespace QDomNode par = e.parentNode(); - while(!par.isNull() && par.namespaceURI().isNull()) + while (!par.isNull() && par.namespaceURI().isNull()) par = par.parentNode(); bool noShowNS = false; - if(!par.isNull() && par.namespaceURI() == e.namespaceURI()) + if (!par.isNull() && par.namespaceURI() == e.namespaceURI()) noShowNS = true; // build qName (prefix:localName) QString qName; - if(!e.prefix().isEmpty()) + if (!e.prefix().isEmpty()) qName = e.prefix() + ':' + e.localName(); else qName = e.tagName(); QDomElement i; - int x; - if(noShowNS) + int x; + if (noShowNS) i = e.ownerDocument().createElement(qName); else i = e.ownerDocument().createElementNS(e.namespaceURI(), qName); // copy attributes QDomNamedNodeMap al = e.attributes(); - for(x = 0; x < al.count(); ++x) { + for (x = 0; x < al.count(); ++x) { QDomAttr a = al.item(x).cloneNode().toAttr(); // don't show xml namespace - if(a.namespaceURI() == NS_XML) + if (a.namespaceURI() == NS_XML) i.setAttribute(QString("xml:") + a.name(), a.value()); else i.setAttributeNodeNS(a); @@ -70,9 +71,9 @@ static QDomElement stripExtraNS(const QDomElement &e) // copy children QDomNodeList nl = e.childNodes(); - for(x = 0; x < nl.count(); ++x) { + for (x = 0; x < nl.count(); ++x) { QDomNode n = nl.item(x); - if(n.isElement()) + if (n.isElement()) i.appendChild(stripExtraNS(n.toElement())); else i.appendChild(n.cloneNode()); @@ -96,20 +97,12 @@ static QString xmlToString(const QDomElement &e, const QString &fakeNS, const QS QString out; { QTextStream ts(&out, QIODevice::WriteOnly); - // NOTE: Workaround for bug in QtXML https://bugreports.qt.io/browse/QTBUG-25291 (Qt4 only): - // Qt by default convert low surrogate to XML notation &#x....; and let high in binary! - // - // Qt is calling encode function per UTF-16 codepoint, which means that high and low - // surrogate pairs are encoded separately. So all encoding except UTF-16 will leads - // to damaged Unicode characters above 0xFFFF. Internal QString encoding is UTF-16 - // so this should be safe as QString still contains valid Unicode characters. - ts.setCodec("UTF-16"); fake.firstChild().save(ts, 0); } // 'clip' means to remove any unwanted (and unneeded) characters, such as a trailing newline - if(clip) { + if (clip) { int n = out.lastIndexOf('>'); - out.truncate(n+1); + out.truncate(n + 1); } return out; } @@ -138,14 +131,14 @@ static void createRootXmlTags(const QDomElement &root, QString *xmlHeader, QStri } // parse the tags out - int n = str.indexOf('<'); + int n = str.indexOf('<'); int n2 = str.indexOf('>', n); ++n2; - *tagOpen = str.mid(n, n2-n); - n2 = str.lastIndexOf('>'); - n = str.lastIndexOf('<'); + *tagOpen = str.mid(n, n2 - n); + n2 = str.lastIndexOf('>'); + n = str.lastIndexOf('<'); ++n2; - *tagClose = str.mid(n, n2-n); + *tagClose = str.mid(n, n2 - n); // generate a nice xml processing header *xmlHeader = ""; @@ -155,22 +148,13 @@ static void createRootXmlTags(const QDomElement &root, QString *xmlHeader, QStri // [2] Char ::= #x9 | #xA | #xD | [#x20-#xD7FF] | [#xE000-#xFFFD] | [#x10000-#x10FFFF] static inline bool validChar(const quint32 ch) { - return ch == 0x9 || ch == 0xA || ch == 0xD - || (ch >= 0x20 && ch <= 0xD7FF) - || (ch >= 0xE000 && ch <= 0xFFFD) - || (ch >= 0x10000 && ch <= 0x10FFFF); + return ch == 0x9 || ch == 0xA || ch == 0xD || (ch >= 0x20 && ch <= 0xD7FF) || (ch >= 0xE000 && ch <= 0xFFFD) + || (ch >= 0x10000 && ch <= 0x10FFFF); } -static inline bool lowSurrogate(const quint32 ch) -{ - return ch >= 0xDC00 && ch <= 0xDFFF; -} - -static inline bool highSurrogate(const quint32 ch) -{ - return ch >= 0xD800 && ch <= 0xDBFF; -} +static inline bool lowSurrogate(const quint32 ch) { return ch >= 0xDC00 && ch <= 0xDFFF; } +static inline bool highSurrogate(const quint32 ch) { return ch >= 0xD800 && ch <= 0xDBFF; } // force encoding of '>'. this function is needed for XMPP-Core, which // requires the '>' character to be encoded as ">" even though this is @@ -180,106 +164,82 @@ static inline bool highSurrogate(const quint32 ch) static QString sanitizeForStream(const QString &in) { QString out; - bool intag = false; - bool inquote = false; - QChar quotechar; - int inlength = in.length(); - for(int n = 0; n < inlength; ++n) - { - QChar c = in[n]; - bool escape = false; - if(c == '<') - { + bool intag = false; + bool inquote = false; + QChar quotechar; + int inlength = in.length(); + for (int n = 0; n < inlength; ++n) { + QChar c = in[n]; + bool escape = false; + if (c == '<') { intag = true; - } - else if(c == '>') - { - if(inquote) { + } else if (c == '>') { + if (inquote) { escape = true; - } else if(!intag) { + } else if (!intag) { escape = true; } else { intag = false; } - } - else if(c == '\'' || c == '\"') - { - if(intag) - { - if(!inquote) - { - inquote = true; + } else if (c == '\'' || c == '\"') { + if (intag) { + if (!inquote) { + inquote = true; quotechar = c; - } - else - { - if(quotechar == c) { + } else { + if (quotechar == c) { inquote = false; } } } } - if(escape) { + if (escape) { out += ">"; - } else { + } else { // don't silently drop invalid chars in element or attribute names, // because that's something that should not happen. if (intag && (!inquote)) { out += c; - } else if (validChar(c.unicode())) { + } else if (validChar(c.unicode())) { out += c; - } else if (highSurrogate(c.unicode()) && (n+1 < inlength) && lowSurrogate(in[n+1].unicode())) { - //uint unicode = (c.unicode() & 0x3FF) << 10 | in[n+1].unicode() & 0x3FF + 0x10000; + } else if (highSurrogate(c.unicode()) && (n + 1 < inlength) && lowSurrogate(in[n + 1].unicode())) { + // uint unicode = (c.unicode() & 0x3FF) << 10 | in[n+1].unicode() & 0x3FF + 0x10000; // we don't need to recheck this, because 0x10000 <= unicode <= 0x100000 is always true out += c; - out += in[n+1]; + out += in[n + 1]; ++n; } else { - qDebug("Dropping invalid XML char U+%04x",c.unicode()); + qDebug("Dropping invalid XML char U+%04x", c.unicode()); } } } return out; } - //---------------------------------------------------------------------------- // Protocol //---------------------------------------------------------------------------- -XmlProtocol::TransferItem::TransferItem() -{ -} +XmlProtocol::TransferItem::TransferItem() { } -XmlProtocol::TransferItem::TransferItem(const QString &_str, bool sent, bool external) - : isSent(sent) - , isString(true) - , isExternal(external) - , str(_str) +XmlProtocol::TransferItem::TransferItem(const QString &_str, bool sent, bool external) : + isSent(sent), isString(true), isExternal(external), str(_str) { } -XmlProtocol::TransferItem::TransferItem(const QDomElement &_elem, bool sent, bool external) - : isSent(sent) - , isString(false) - , isExternal(external) - , elem(_elem) +XmlProtocol::TransferItem::TransferItem(const QDomElement &_elem, bool sent, bool external) : + isSent(sent), isString(false), isExternal(external), elem(_elem) { } -XmlProtocol::XmlProtocol() -{ - init(); -} +XmlProtocol::XmlProtocol() { init(); } -XmlProtocol::~XmlProtocol() -{ -} +XmlProtocol::~XmlProtocol() { } void XmlProtocol::init() { - incoming = false; - peerClosed = false; + incoming = false; + peerClosed = false; closeWritten = false; } @@ -287,9 +247,9 @@ void XmlProtocol::reset() { init(); - elem = QDomElement(); - elemDoc = QDomDocument(); - tagOpen = QString(); + elem = QDomElement(); + elemDoc = QDomDocument(); + tagOpen = QString(); tagClose = QString(); xml.reset(); outDataNormal.resize(0); @@ -299,10 +259,7 @@ void XmlProtocol::reset() transferItemList.clear(); } -void XmlProtocol::addIncomingData(const QByteArray &a) -{ - xml.appendData(a); -} +void XmlProtocol::addIncomingData(const QByteArray &a) { xml.appendData(a); } QByteArray XmlProtocol::takeOutgoingData() { @@ -323,66 +280,69 @@ void XmlProtocol::outgoingDataWritten(int bytes) processTrackQueue(trackQueueNormal, b); } +void XmlProtocol::clearSendQueue() +{ + outDataUrgent.clear(); + outDataNormal.clear(); +} + bool XmlProtocol::processStep() { Parser::Event pe; notify = 0; transferItemList.clear(); - if(state != Closing && (state == RecvOpen || stepAdvancesParser())) { + if (state != Closing && (state == RecvOpen || stepAdvancesParser())) { // if we get here, then it's because we're in some step that advances the parser pe = xml.readNext(); - if(!pe.isNull()) { + if (!pe.isNull()) { // note: error/close events should be handled for ALL steps, so do them here - switch(pe.type()) { - case Parser::Event::DocumentOpen: { - transferItemList += TransferItem(pe.actualString(), false); + switch (pe.type()) { + case Parser::Event::DocumentOpen: { + transferItemList += TransferItem(pe.actualString(), false); - //stringRecv(pe.actualString()); - break; - } - case Parser::Event::DocumentClose: { - transferItemList += TransferItem(pe.actualString(), false); - - //stringRecv(pe.actualString()); - if(incoming) { - sendTagClose(); - event = ESend; - peerClosed = true; - state = Closing; - } - else { - event = EPeerClosed; - } - return true; + // stringRecv(pe.actualString()); + break; + } + case Parser::Event::DocumentClose: { + transferItemList += TransferItem(pe.actualString(), false); + + // stringRecv(pe.actualString()); + if (incoming) { + sendTagClose(); + event = ESend; + peerClosed = true; + state = Closing; + } else { + event = EPeerClosed; } - case Parser::Event::Element: { - QDomElement e = elemDoc.importNode(pe.element(),true).toElement(); - transferItemList += TransferItem(e, false); + return true; + } + case Parser::Event::Element: { + QDomElement e = elemDoc.importNode(pe.element(), true).toElement(); + transferItemList += TransferItem(e, false); - //elementRecv(pe.element()); - break; - } - case Parser::Event::Error: { - if(incoming) { - // If we get a parse error during the initial element exchange, - // flip immediately into 'open' mode so that we can report an error. - if(state == RecvOpen) { - sendTagOpen(); - state = Open; - } - return handleError(); - } - else { - event = EError; - errorCode = ErrParse; - return true; + // elementRecv(pe.element()); + break; + } + case Parser::Event::Error: { + if (incoming) { + // If we get a parse error during the initial element exchange, + // flip immediately into 'open' mode so that we can report an error. + if (state == RecvOpen) { + sendTagOpen(); + state = Open; } + return handleError(); + } else { + event = EError; + errorCode = ErrParse; + return true; } } - } - else { - if(state == RecvOpen || stepRequiresElement()) { + } + } else { + if (state == RecvOpen || stepRequiresElement()) { need = NNotify; notify |= NRecv; return false; @@ -393,14 +353,11 @@ bool XmlProtocol::processStep() return baseStep(pe); } -QString XmlProtocol::xmlEncoding() const -{ - return xml.encoding(); -} +QString XmlProtocol::xmlEncoding() const { return xml.encoding().toString(); } QString XmlProtocol::elementToString(const QDomElement &e, bool clip) { - if(elem.isNull()) + if (elem.isNull()) elem = elemDoc.importNode(docElement(), true).toElement(); // Determine the appropriate 'fakeNS' to use @@ -408,29 +365,28 @@ QString XmlProtocol::elementToString(const QDomElement &e, bool clip) // first, check root namespace QString pre = e.prefix(); - if(pre.isNull()) + if (pre.isNull()) pre = ""; - if(pre == elem.prefix()) { + if (pre == elem.prefix()) { ns = elem.namespaceURI(); - } - else { + } else { // scan the root attributes for 'xmlns' (oh joyous hacks) QDomNamedNodeMap al = elem.attributes(); - int n; - for(n = 0; n < al.count(); ++n) { + int n; + for (n = 0; n < al.count(); ++n) { QDomAttr a = al.item(n).toAttr(); - QString s = a.name(); - int x = s.indexOf(':'); - if(x != -1) - s = s.mid(x+1); + QString s = a.name(); + int x = s.indexOf(':'); + if (x != -1) + s = s.mid(x + 1); else s = ""; - if(pre == s) { + if (pre == s) { ns = a.value(); break; } } - if(n >= al.count()) { + if (n >= al.count()) { // if we get here, then no appropriate ns was found. use root then.. ns = elem.namespaceURI(); } @@ -438,7 +394,7 @@ QString XmlProtocol::elementToString(const QDomElement &e, bool clip) // build qName QString qn; - if(!elem.prefix().isEmpty()) + if (!elem.prefix().isEmpty()) qn = elem.prefix() + ':'; qn += elem.localName(); @@ -480,13 +436,13 @@ void XmlProtocol::elementRecv(const QDomElement &) void XmlProtocol::startConnect() { incoming = false; - state = SendOpen; + state = SendOpen; } void XmlProtocol::startAccept() { incoming = true; - state = RecvOpen; + state = RecvOpen; } bool XmlProtocol::close() @@ -505,11 +461,11 @@ int XmlProtocol::writeString(const QString &s, int id, bool external) int XmlProtocol::writeElement(const QDomElement &e, int id, bool external, bool clip, bool urgent) { - if(e.isNull()) + if (e.isNull()) return 0; transferItemList += TransferItem(e, true, external); - //elementSend(e); + // elementSend(e); QString out = sanitizeForStream(elementToString(e, clip)); return internalWriteString(out, TrackItem::Custom, id, urgent); } @@ -517,7 +473,7 @@ int XmlProtocol::writeElement(const QDomElement &e, int id, bool external, bool QByteArray XmlProtocol::resetStream() { // reset the state - if(incoming) + if (incoming) state = RecvOpen; else state = SendOpen; @@ -532,14 +488,13 @@ int XmlProtocol::internalWriteData(const QByteArray &a, TrackItem::Type t, int i { TrackItem i; i.type = t; - i.id = id; + i.id = id; i.size = a.size(); if (urgent) { trackQueueUrgent += i; outDataUrgent += a; - } - else { + } else { trackQueueNormal += i; outDataNormal += a; } @@ -548,34 +503,31 @@ int XmlProtocol::internalWriteData(const QByteArray &a, TrackItem::Type t, int i int XmlProtocol::internalWriteString(const QString &s, TrackItem::Type t, int id, bool urgent) { - QString out=sanitizeForStream(s); return internalWriteData(s.toUtf8(), t, id, urgent); } int XmlProtocol::processTrackQueue(QList &queue, int bytes) { - for(QList::Iterator it = queue.begin(); it != queue.end();) { + for (QList::Iterator it = queue.begin(); it != queue.end();) { TrackItem &i = *it; // enough bytes? - if(bytes < i.size) { + if (bytes < i.size) { i.size -= bytes; bytes = 0; break; } int type = i.type; - int id = i.id; + int id = i.id; int size = i.size; bytes -= i.size; it = queue.erase(it); - if(type == TrackItem::Raw) { + if (type == TrackItem::Raw) { // do nothing - } - else if(type == TrackItem::Close) { + } else if (type == TrackItem::Close) { closeWritten = true; - } - else if(type == TrackItem::Custom) { + } else if (type == TrackItem::Custom) { itemWritten(id, size); } if (bytes == 0) @@ -586,7 +538,7 @@ int XmlProtocol::processTrackQueue(QList &queue, int bytes) void XmlProtocol::sendTagOpen() { - if(elem.isNull()) + if (elem.isNull()) elem = elemDoc.importNode(docElement(), true).toElement(); QString xmlHeader; @@ -599,8 +551,8 @@ void XmlProtocol::sendTagOpen() transferItemList += TransferItem(xmlHeader, true); transferItemList += TransferItem(tagOpen, true); - //stringSend(xmlHeader); - //stringSend(tagOpen); + // stringSend(xmlHeader); + // stringSend(tagOpen); internalWriteString(s, TrackItem::Raw); } @@ -608,24 +560,23 @@ void XmlProtocol::sendTagClose() { transferItemList += TransferItem(tagClose, true); - //stringSend(tagClose); + // stringSend(tagClose); internalWriteString(tagClose, TrackItem::Close); } bool XmlProtocol::baseStep(const Parser::Event &pe) { // Basic - if(state == SendOpen) { + if (state == SendOpen) { sendTagOpen(); event = ESend; - if(incoming) + if (incoming) state = Open; else state = RecvOpen; return true; - } - else if(state == RecvOpen) { - if(incoming) + } else if (state == RecvOpen) { + if (incoming) state = SendOpen; else state = Open; @@ -634,25 +585,23 @@ bool XmlProtocol::baseStep(const Parser::Event &pe) handleDocOpen(pe); event = ERecvOpen; return true; - } - else if(state == Open) { + } else if (state == Open) { QDomElement e; - if(pe.type() == Parser::Event::Element) + if (pe.type() == Parser::Event::Element) e = pe.element(); return doStep(e); } // Closing else { - if(closeWritten) { - if(peerClosed) { + if (closeWritten) { + if (peerClosed) { event = EPeerClosed; return true; - } - else + } else return handleCloseFinished(); } - need = NNotify; + need = NNotify; notify = NSend; return false; } @@ -660,11 +609,10 @@ bool XmlProtocol::baseStep(const Parser::Event &pe) void XmlProtocol::setIncomingAsExternal() { - for(QList::Iterator it = transferItemList.begin(); it != transferItemList.end(); ++it) { + for (QList::Iterator it = transferItemList.begin(); it != transferItemList.end(); ++it) { TransferItem &i = *it; // look for elements received - if(!i.isString && !i.isSent) + if (!i.isString && !i.isSent) i.isExternal = true; } } - diff --git a/src/xmpp/xmpp-core/xmlprotocol.h b/src/xmpp/xmpp-core/xmlprotocol.h index cc63e707..75be1d57 100644 --- a/src/xmpp/xmpp-core/xmlprotocol.h +++ b/src/xmpp/xmpp-core/xmlprotocol.h @@ -20,133 +20,131 @@ #ifndef XMLPROTOCOL_H #define XMLPROTOCOL_H -#include +#include "parser.h" + #include #include -#include "parser.h" +#include #define NS_XML "http://www.w3.org/XML/1998/namespace" -namespace XMPP -{ - class XmlProtocol : public QObject - { +namespace XMPP { +class XmlProtocol : public QObject { +public: + enum Need { + NNotify, // need a data send and/or recv update + NCustom = 10 + }; + enum Event { + EError, // unrecoverable error, see errorCode for details + ESend, // data needs to be sent, use takeOutgoingData() + ERecvOpen, // breakpoint after root element open tag is received + EPeerClosed, // root element close tag received + EClosed, // finished closing + ESMConnTimeout, // absence of responses to query + ESMResumeFailed, // failed to resume sm session + ECustom = 10 + }; + enum Error { + ErrParse, // there was an error parsing the xml + ErrCustom = 10 + }; + enum Notify { + NSend = 0x01, // need to know if data has been written + NRecv = 0x02, // need incoming data + NTimeout = 0x04 // need to know when time passed + }; + + XmlProtocol(); + virtual ~XmlProtocol(); + + virtual void reset(); + + // byte I/O for the stream + void addIncomingData(const QByteArray &); + QByteArray takeOutgoingData(); + void outgoingDataWritten(int); + void clearSendQueue(); + + // advance the state machine + bool processStep(); + + // set these before returning from a step + int need = 0, event = 0, errorCode = 0, notify = 0, timeout_sec = 0; + + inline bool isIncoming() const { return incoming; } + QString xmlEncoding() const; + QString elementToString(const QDomElement &e, bool clip = false); + + class TransferItem { public: - enum Need { - NNotify, // need a data send and/or recv update - NCustom = 10 - }; - enum Event { - EError, // unrecoverable error, see errorCode for details - ESend, // data needs to be sent, use takeOutgoingData() - ERecvOpen, // breakpoint after root element open tag is received - EPeerClosed, // root element close tag received - EClosed, // finished closing - ESMConnTimeout, // absence of responses to query - ESMResumeFailed, // failed to resume sm session - ECustom = 10 - }; - enum Error { - ErrParse, // there was an error parsing the xml - ErrCustom = 10 - }; - enum Notify { - NSend = 0x01, // need to know if data has been written - NRecv = 0x02, // need incoming data - NTimeout = 0x04 // need to know when time passed - }; - - XmlProtocol(); - virtual ~XmlProtocol(); - - virtual void reset(); - - // byte I/O for the stream - void addIncomingData(const QByteArray &); - QByteArray takeOutgoingData(); - void outgoingDataWritten(int); - - // advance the state machine - bool processStep(); - - // set these before returning from a step - int need = 0, event = 0, errorCode = 0, notify = 0, timeout_sec = 0; - - inline bool isIncoming() const { return incoming; } - QString xmlEncoding() const; - QString elementToString(const QDomElement &e, bool clip=false); - - class TransferItem - { - public: - TransferItem(); - TransferItem(const QString &str, bool sent, bool external=false); - TransferItem(const QDomElement &elem, bool sent, bool external=false); - - bool isSent; // else, received - bool isString; // else, is element - bool isExternal; // not owned by protocol - QString str; - QDomElement elem; - }; - QList transferItemList; - void setIncomingAsExternal(); - - protected: - virtual QDomElement docElement()=0; - virtual void handleDocOpen(const Parser::Event &pe)=0; - virtual bool handleError()=0; - virtual bool handleCloseFinished()=0; - virtual bool stepAdvancesParser() const=0; - virtual bool stepRequiresElement() const; - virtual bool doStep(const QDomElement &e)=0; - virtual void itemWritten(int id, int size); - - // 'debug' - virtual void stringSend(const QString &s); - virtual void stringRecv(const QString &s); - virtual void elementSend(const QDomElement &e); - virtual void elementRecv(const QDomElement &e); - - void startConnect(); - void startAccept(); - bool close(); - int writeString(const QString &s, int id, bool external); - int writeElement(const QDomElement &e, int id, bool external, bool clip=false, bool urgent = false); - QByteArray resetStream(); - - private: - enum { SendOpen, RecvOpen, Open, Closing }; - class TrackItem - { - public: - enum Type { Raw, Close, Custom }; - int type, id, size; - }; - - bool incoming; - QDomDocument elemDoc; + TransferItem(); + TransferItem(const QString &str, bool sent, bool external = false); + TransferItem(const QDomElement &elem, bool sent, bool external = false); + + bool isSent; // else, received + bool isString; // else, is element + bool isExternal; // not owned by protocol + QString str; QDomElement elem; - QString tagOpen; - QString tagClose; - int state = 0; - bool peerClosed; - bool closeWritten; - - Parser xml; - QByteArray outDataNormal; - QByteArray outDataUrgent; - QList trackQueueNormal; - QList trackQueueUrgent; - - void init(); - int internalWriteData(const QByteArray &a, TrackItem::Type t, int id=-1, bool urgent = false); - int internalWriteString(const QString &s, TrackItem::Type t, int id=-1, bool urgent = false); - int processTrackQueue(QList &queue, int bytes); - void sendTagOpen(); - void sendTagClose(); - bool baseStep(const Parser::Event &pe); }; -} + QList transferItemList; + void setIncomingAsExternal(); + +protected: + virtual QDomElement docElement() = 0; + virtual void handleDocOpen(const Parser::Event &pe) = 0; + virtual bool handleError() = 0; + virtual bool handleCloseFinished() = 0; + virtual bool stepAdvancesParser() const = 0; + virtual bool stepRequiresElement() const; + virtual bool doStep(const QDomElement &e) = 0; + virtual void itemWritten(int id, int size); + + // 'debug' + virtual void stringSend(const QString &s); + virtual void stringRecv(const QString &s); + virtual void elementSend(const QDomElement &e); + virtual void elementRecv(const QDomElement &e); + + void startConnect(); + void startAccept(); + bool close(); + int writeString(const QString &s, int id, bool external); + int writeElement(const QDomElement &e, int id, bool external, bool clip = false, bool urgent = false); + QByteArray resetStream(); + +private: + enum { SendOpen, RecvOpen, Open, Closing }; + class TrackItem { + public: + enum Type { Raw, Close, Custom }; + int type, id, size; + }; -#endif + bool incoming; + QDomDocument elemDoc; + QDomElement elem; + QString tagOpen; + QString tagClose; + int state = 0; + bool peerClosed; + bool closeWritten; + + Parser xml; + QByteArray outDataNormal; + QByteArray outDataUrgent; + QList trackQueueNormal; + QList trackQueueUrgent; + + void init(); + int internalWriteData(const QByteArray &a, TrackItem::Type t, int id = -1, bool urgent = false); + int internalWriteString(const QString &s, TrackItem::Type t, int id = -1, bool urgent = false); + int processTrackQueue(QList &queue, int bytes); + void sendTagOpen(); + void sendTagClose(); + bool baseStep(const Parser::Event &pe); +}; +} // namespace XMPP + +#endif // XMLPROTOCOL_H diff --git a/src/xmpp/xmpp-core/xmpp.h b/src/xmpp/xmpp-core/xmpp.h index 61617a02..70e058fb 100644 --- a/src/xmpp/xmpp-core/xmpp.h +++ b/src/xmpp/xmpp-core/xmpp.h @@ -20,218 +20,207 @@ #ifndef XMPP_H #define XMPP_H -#include -#include -#include -#include -#include -#include -#include - -#include "addressresolver.h" -#include "xmpp/jid/jid.h" -#include "xmpp_stanza.h" -#include "xmpp_stream.h" +#include "iris/addressresolver.h" #include "xmpp_clientstream.h" -namespace QCA -{ - class TLS; -}; +#include +#include +#include +#include +#include +#include +#include // For QCA::SASL::Params #ifndef CS_XMPP class ByteStream; #endif -#include // For QCA::SASL::Params +namespace QCA { +class TLS; +}; -namespace XMPP -{ - // CS_IMPORT_BEGIN cutestuff/bytestream.h +namespace XMPP { +// CS_IMPORT_BEGIN cutestuff/bytestream.h #ifdef CS_XMPP - class ByteStream; +class ByteStream; #endif - // CS_IMPORT_END +// CS_IMPORT_END - class Debug - { - public: - virtual ~Debug(); +class Debug { +public: + virtual ~Debug(); - virtual void msg(const QString &)=0; - virtual void outgoingTag(const QString &)=0; - virtual void incomingTag(const QString &)=0; - virtual void outgoingXml(const QDomElement &)=0; - virtual void incomingXml(const QDomElement &)=0; - }; + virtual void msg(const QString &) = 0; + virtual void outgoingTag(const QString &) = 0; + virtual void incomingTag(const QString &) = 0; + virtual void outgoingXml(const QDomElement &) = 0; + virtual void incomingXml(const QDomElement &) = 0; +}; - void setDebug(Debug *); +void setDebug(Debug *); - class Connector : public QObject - { - Q_OBJECT - public: - Connector(QObject *parent=0); - virtual ~Connector(); +class Connector : public QObject { + Q_OBJECT +public: + Connector(QObject *parent = nullptr); + virtual ~Connector(); - virtual void setOptHostPort(const QString &host, quint16 port)=0; - virtual void connectToServer(const QString &server)=0; - virtual ByteStream *stream() const=0; - virtual void done()=0; + virtual void setOptHostPort(const QString &host, quint16 port) = 0; + virtual void connectToServer(const QString &server) = 0; + virtual ByteStream *stream() const = 0; + virtual void done() = 0; - bool useSSL() const; - bool havePeerAddress() const; - QHostAddress peerAddress() const; - quint16 peerPort() const; + bool useSSL() const; + bool havePeerAddress() const; + QHostAddress peerAddress() const; + quint16 peerPort() const; - virtual QString host() const; + virtual QString host() const; - signals: - void connected(); - void error(); +signals: + void connected(); + void error(); - protected: - void setUseSSL(bool b); - void setPeerAddressNone(); - void setPeerAddress(const QHostAddress &addr, quint16 port); +protected: + void setUseSSL(bool b); + void setPeerAddressNone(); + void setPeerAddress(const QHostAddress &addr, quint16 port); - private: - bool ssl; // a flag to start ssl handshake immediately - bool haveaddr; - QHostAddress addr; - quint16 port; - }; +private: + bool ssl; // a flag to start ssl handshake immediately + bool haveaddr; + QHostAddress addr; + quint16 port; +}; - class AdvancedConnector : public Connector - { - Q_OBJECT +class AdvancedConnector : public Connector { + Q_OBJECT +public: + enum Error { ErrConnectionRefused, ErrHostNotFound, ErrProxyConnect, ErrProxyNeg, ErrProxyAuth, ErrStream }; + AdvancedConnector(QObject *parent = nullptr); + virtual ~AdvancedConnector(); + + class Proxy { public: - enum Error { ErrConnectionRefused, ErrHostNotFound, ErrProxyConnect, ErrProxyNeg, ErrProxyAuth, ErrStream }; - AdvancedConnector(QObject *parent=0); - virtual ~AdvancedConnector(); - - class Proxy - { - public: - enum { None, HttpConnect, HttpPoll, Socks }; - Proxy() = default; - ~Proxy() {} - - int type() const; - QString host() const; - quint16 port() const; - QUrl url() const; - QString user() const; - QString pass() const; - int pollInterval() const; - - void setHttpConnect(const QString &host, quint16 port); - void setHttpPoll(const QString &host, quint16 port, const QUrl &url); - void setSocks(const QString &host, quint16 port); - void setUserPass(const QString &user, const QString &pass); - void setPollInterval(int secs); - - private: - int t = None; - QUrl v_url; - QString v_host; - quint16 v_port = 0; - QString v_user; - QString v_pass; - int v_poll = 30; - }; - - void setProxy(const Proxy &proxy); - void setOptProbe(bool); - void setOptSSL(bool); - - void changePollInterval(int secs); - - void setOptHostPort(const QString &host, quint16 port); - void connectToServer(const QString &server); - ByteStream *stream() const; - void done(); - - int errorCode() const; - - virtual QString host() const; - - signals: - void srvLookup(const QString &server); - void srvResult(bool success); - void httpSyncStarted(); - void httpSyncFinished(); - - private slots: - void bs_connected(); - void bs_error(int); - void http_syncStarted(); - void http_syncFinished(); - void t_timeout(); + enum { None, HttpConnect, HttpPoll, Socks }; + Proxy() = default; + ~Proxy() { } + + int type() const; + QString host() const; + quint16 port() const; + QUrl url() const; + QString user() const; + QString pass() const; + int pollInterval() const; + + void setHttpConnect(const QString &host, quint16 port); + void setHttpPoll(const QString &host, quint16 port, const QUrl &url); + void setSocks(const QString &host, quint16 port); + void setUserPass(const QString &user, const QString &pass); + void setPollInterval(int secs); + + operator QNetworkProxy(); private: - class Private; - Private *d; - - void cleanup(); + int t = None; + QUrl v_url; + QString v_host; + quint16 v_port = 0; + QString v_user; + QString v_pass; + int v_poll = 30; }; - class TLSHandler : public QObject - { - Q_OBJECT - public: - TLSHandler(QObject *parent=0); - virtual ~TLSHandler(); - - virtual void reset()=0; - virtual void startClient(const QString &host)=0; - virtual void write(const QByteArray &a)=0; - virtual void writeIncoming(const QByteArray &a)=0; - - signals: - void success(); - void fail(); - void closed(); - void readyRead(const QByteArray &a); - void readyReadOutgoing(const QByteArray &a, int plainBytes); - }; + void setProxy(const Proxy &proxy); + void setOptProbe(bool); + void setOptSSL(bool); - class QCATLSHandler : public TLSHandler - { - Q_OBJECT - public: - QCATLSHandler(QCA::TLS *parent); - ~QCATLSHandler(); + void changePollInterval(int secs); - QCA::TLS *tls() const; - int tlsError() const; + void setOptHostPort(const QString &host, quint16 port); + void connectToServer(const QString &server); + ByteStream *stream() const; + void done(); - void setXMPPCertCheck(bool enable); - bool XMPPCertCheck(); - bool certMatchesHostname(); + int errorCode() const; - void reset(); - void startClient(const QString &host); - void write(const QByteArray &a); - void writeIncoming(const QByteArray &a); + virtual QString host() const; - signals: - void tlsHandshaken(); +signals: + void srvLookup(const QString &server); + void srvResult(bool success); + void httpSyncStarted(); + void httpSyncFinished(); - public slots: - void continueAfterHandshake(); +private slots: + void bs_connected(); + void bs_error(int); + void http_syncStarted(); + void http_syncFinished(); + void t_timeout(); - private slots: - void tls_handshaken(); - void tls_readyRead(); - void tls_readyReadOutgoing(); - void tls_closed(); - void tls_error(); +private: + class Private; + Private *d; - private: - class Private; - Private *d; - }; + void cleanup(); }; -#endif +class TLSHandler : public QObject { + Q_OBJECT +public: + TLSHandler(QObject *parent = nullptr); + virtual ~TLSHandler(); + + virtual void reset() = 0; + virtual void startClient(const QString &host) = 0; + virtual void write(const QByteArray &a) = 0; + virtual void writeIncoming(const QByteArray &a) = 0; + +signals: + void success(); + void fail(); + void closed(); + void readyRead(const QByteArray &a); + void readyReadOutgoing(const QByteArray &a, int plainBytes); +}; + +class QCATLSHandler : public TLSHandler { + Q_OBJECT +public: + QCATLSHandler(QCA::TLS *parent); + ~QCATLSHandler(); + + QCA::TLS *tls() const; + int tlsError() const; + + void setXMPPCertCheck(bool enable); + bool XMPPCertCheck(); + bool certMatchesHostname(); + + void reset(); + void startClient(const QString &host); + void write(const QByteArray &a); + void writeIncoming(const QByteArray &a); + +signals: + void tlsHandshaken(); + +public slots: + void continueAfterHandshake(); + +private slots: + void tls_handshaken(); + void tls_readyRead(); + void tls_readyReadOutgoing(); + void tls_closed(); + void tls_error(); + +private: + class Private; + Private *d; +}; +}; // namespace XMPP + +#endif // XMPP_H diff --git a/src/xmpp/xmpp-core/xmpp_clientstream.h b/src/xmpp/xmpp-core/xmpp_clientstream.h index 4bc5b6a4..20551943 100644 --- a/src/xmpp/xmpp-core/xmpp_clientstream.h +++ b/src/xmpp/xmpp-core/xmpp_clientstream.h @@ -19,213 +19,205 @@ #ifndef XMPP_CLIENTSTREAM_H #define XMPP_CLIENTSTREAM_H -#include - #include "xmpp_stream.h" +#include + +class ByteStream; class QByteArray; -class QString; class QDomDocument; class QDomElement; -class QObject; -class ByteStream; class QHostAddress; +class QObject; +class QString; -namespace XMPP -{ - class TLSHandler; - class Connector; - class StreamFeatures; - - class ClientStream : public Stream - { - Q_OBJECT - public: - enum Error { - ErrConnection = ErrCustom, // Connection error, ask Connector-subclass what's up - ErrNeg, // Negotiation error, see condition - ErrTLS, // TLS error, see condition - ErrAuth, // Auth error, see condition - ErrSecurityLayer, // broken SASL security layer - ErrSmResume, // SM resume error - ErrBind // Resource binding error - }; - enum Warning { - WarnOldVersion, // server uses older XMPP/Jabber "0.9" protocol - WarnNoTLS, // there is no chance for TLS at this point - WarnSMReconnection // SM started a quiet stream reconnection - }; - enum NegCond { - HostGone, // host no longer hosted - HostUnknown, // unknown host - RemoteConnectionFailed, // unable to connect to a required remote resource - SeeOtherHost, // a 'redirect', see errorText() for other host - UnsupportedVersion // unsupported XMPP version - }; - enum TLSCond { - TLSStart, // server rejected STARTTLS - TLSFail // TLS failed, ask TLSHandler-subclass what's up - }; - enum SecurityLayer { - LayerTLS, - LayerSASL - }; - enum AuthCond { - GenericAuthError, // all-purpose "can't login" error (includes: IncorrectEncoding, ) - - // standard xmpp auth errors (not all. some of the converted to GenericAuthError): - Aborted, // server confirms auth abort - AccountDisabled, // account temporrily disabled - CredentialsExpired, // credential expired - EncryptionRequired, // can't use mech without TLS - InvalidAuthzid, // bad input JID - InvalidMech, // bad mechanism - MalformedRequest, // malformded request - MechTooWeak, // can't use mech with this authzid - NotAuthorized, // bad user, bad password, bad creditials - TemporaryAuthFailure, // please try again later! - - // non-xmpp - NoMech, // No appropriate auth mech available - BadServ, // Server failed mutual auth - }; - enum BindCond { - BindNotAllowed, // not allowed to bind a resource - BindConflict // resource in-use - }; - enum AllowPlainType { - NoAllowPlain, - AllowPlain, - AllowPlainOverTLS - }; - - ClientStream(Connector *conn, TLSHandler *tlsHandler=0, QObject *parent=0); - ClientStream(const QString &host, const QString &defRealm, ByteStream *bs, QCA::TLS *tls=0, QObject *parent=0); // server - ~ClientStream(); - - Jid jid() const; - void connectToServer(const Jid &jid, bool auth=true); - void accept(); // server - bool isActive() const; - bool isAuthenticated() const; - - // login params - void setUsername(const QString &s); - void setPassword(const QString &s); - void setRealm(const QString &s); - void setAuthzid(const QString &s); - void continueAfterParams(); - void abortAuth(); - void setSaslMechanismProvider(const QString &m, const QString &p); - QString saslMechanismProvider(const QString &m) const; - QCA::Provider::Context *currentSASLContext() const; - - void setSCRAMStoredSaltedHash(const QString &s); - const QString getSCRAMStoredSaltedHash(); - - // SASL information - QString saslMechanism() const; - int saslSSF() const; - - // binding - void setResourceBinding(bool); - - // Language - void setLang(const QString&); - - // security options (old protocol only uses the first !) - void setAllowPlain(AllowPlainType); - void setRequireMutualAuth(bool); - void setSSFRange(int low, int high); - void setOldOnly(bool); - void setSASLMechanism(const QString &s); - void setLocalAddr(const QHostAddress &addr, quint16 port); - - // Compression - void setCompress(bool); - - // reimplemented - QDomDocument & doc() const; - QString baseNS() const; - bool old() const; - - void close(); - bool stanzaAvailable() const; - Stanza read(); - void write(const Stanza &s); - void clearSendQueue(); - - int errorCondition() const; - QString errorText() const; - QHash errorLangText() const; - QDomElement errorAppSpec() const; - - // extra - void writeDirect(const QString &s); - void setNoopTime(int mills); - - // Stream management - bool isResumed() const; - void setSMEnabled(bool enable); - - // barracuda extension - QStringList hosts() const; - - const StreamFeatures &streamFeatures() const; - QList unhandledFeatures() const; - - signals: - void connected(); - void securityLayerActivated(int); - void needAuthParams(bool user, bool pass, bool realm); - void authenticated(); - void warning(int); - void haveUnhandledFeatures(); - void incomingXml(const QString &s); - void outgoingXml(const QString &s); - void stanzasAcked(int); - - public slots: - void continueAfterWarning(); - - private slots: - void cr_connected(); - void cr_error(); - - void bs_connectionClosed(); - void bs_delayedCloseFinished(); - void bs_error(int); // server only - - void ss_readyRead(); - void ss_bytesWritten(qint64); - void ss_tlsHandshaken(); - void ss_tlsClosed(); - void ss_error(int); - - void sasl_clientFirstStep(bool, const QByteArray&); - void sasl_nextStep(const QByteArray &stepData); - void sasl_needParams(const QCA::SASL::Params&); - void sasl_authCheck(const QString &user, const QString &authzid); - void sasl_authenticated(); - void sasl_error(); - - void sm_timeout(); - - void doNoop(); - void doReadyRead(); - - private: - class Private; - Private *d; - - void reset(bool all=false); - void processNext(); - int convertedSASLCond() const; - bool handleNeed(); - void handleError(); - void srvProcessNext(); - void setTimer(int secs); +namespace XMPP { +class Connector; +class StreamFeatures; +class TLSHandler; + +class ClientStream : public Stream { + Q_OBJECT +public: + enum Error { + ErrConnection = ErrCustom, // Connection error, ask Connector-subclass what's up + ErrNeg, // Negotiation error, see condition + ErrTLS, // TLS error, see condition + ErrAuth, // Auth error, see condition + ErrSecurityLayer, // broken SASL security layer + ErrSmResume, // SM resume error + ErrBind // Resource binding error }; -} - -#endif + enum Warning { + WarnOldVersion, // server uses older XMPP/Jabber "0.9" protocol + WarnNoTLS, // there is no chance for TLS at this point + WarnSMReconnection // SM started a quiet stream reconnection + }; + enum NegCond { + HostGone, // host no longer hosted + HostUnknown, // unknown host + RemoteConnectionFailed, // unable to connect to a required remote resource + SeeOtherHost, // a 'redirect', see errorText() for other host + UnsupportedVersion // unsupported XMPP version + }; + enum TLSCond { + TLSStart, // server rejected STARTTLS + TLSFail // TLS failed, ask TLSHandler-subclass what's up + }; + enum SecurityLayer { LayerTLS, LayerSASL }; + enum AuthCond { + GenericAuthError, // all-purpose "can't login" error (includes: IncorrectEncoding, ) + + // standard xmpp auth errors (not all. some of the converted to GenericAuthError): + Aborted, // server confirms auth abort + AccountDisabled, // account temporrily disabled + CredentialsExpired, // credential expired + EncryptionRequired, // can't use mech without TLS + InvalidAuthzid, // bad input JID + InvalidMech, // bad mechanism + MalformedRequest, // malformded request + MechTooWeak, // can't use mech with this authzid + NotAuthorized, // bad user, bad password, bad creditials + TemporaryAuthFailure, // please try again later! + + // non-xmpp + NoMech, // No appropriate auth mech available + BadServ, // Server failed mutual auth + }; + enum BindCond { + BindNotAllowed, // not allowed to bind a resource + BindConflict // resource in-use + }; + enum AllowPlainType { NoAllowPlain, AllowPlain, AllowPlainOverTLS }; + + ClientStream(Connector *conn, TLSHandler *tlsHandler = nullptr, QObject *parent = nullptr); + ClientStream(const QString &host, const QString &defRealm, ByteStream *bs, QCA::TLS *tls = nullptr, + QObject *parent = nullptr); // server + ~ClientStream(); + + Jid jid() const; + void connectToServer(const Jid &jid, bool auth = true); + void accept(); // server + bool isActive() const; + bool isAuthenticated() const; + + // login params + void setUsername(const QString &s); + void setPassword(const QString &s); + void setRealm(const QString &s); + void setAuthzid(const QString &s); + void continueAfterParams(); + void abortAuth(); + void setSaslMechanismProvider(const QString &m, const QString &p); + QString saslMechanismProvider(const QString &m) const; + QCA::Provider::Context *currentSASLContext() const; + + void setSCRAMStoredSaltedHash(const QString &s); + const QString getSCRAMStoredSaltedHash(); + + // SASL information + QString saslMechanism() const; + int saslSSF() const; + + // binding + void setResourceBinding(bool); + + // Language + void setLang(const QString &); + + // security options (old protocol only uses the first !) + void setAllowPlain(AllowPlainType); + void setRequireMutualAuth(bool); + void setSSFRange(int low, int high); + void setOldOnly(bool); + void setSASLMechanism(const QString &s); + void setLocalAddr(const QHostAddress &addr, quint16 port); + + // Compression + void setCompress(bool); + + // reimplemented + QDomDocument &doc() const; + QString baseNS() const; + bool old() const; + + void close(); + bool stanzaAvailable() const; + Stanza read(); + void write(const Stanza &s); + void clearSendQueue(); + + int errorCondition() const; + QString errorText() const; + QHash errorLangText() const; + QDomElement errorAppSpec() const; + + // extra + void writeDirect(const QString &s); + void setNoopTime(int mills); + + // Stream management + bool isResumed() const; + void setSMEnabled(bool enable); + + // barracuda extension + QStringList hosts() const; + + const StreamFeatures &streamFeatures() const; + QList unhandledFeatures() const; + +signals: + void connected(); + void securityLayerActivated(int); + void needAuthParams(bool user, bool pass, bool realm); + void authenticated(); + void warning(int); + void haveUnhandledFeatures(); + void incomingXml(const QString &s); + void outgoingXml(const QString &s); + void stanzasAcked(int); + +public slots: + void continueAfterWarning(); + +private slots: + void cr_connected(); + void cr_error(); + + void bs_connectionClosed(); + void bs_delayedCloseFinished(); + void bs_error(int); // server only + + void ss_readyRead(); + void ss_bytesWritten(qint64); + void ss_tlsHandshaken(); + void ss_tlsClosed(); + void ss_error(int); + + void sasl_clientFirstStep(bool, const QByteArray &); + void sasl_nextStep(const QByteArray &stepData); + void sasl_needParams(const QCA::SASL::Params &); + void sasl_authCheck(const QString &user, const QString &authzid); + void sasl_authenticated(); + void sasl_error(); + + void sm_timeout(); + + void doNoop(); + void doReadyRead(); + +private: + class Private; + Private *d; + + void reset(bool all = false); + void processNext(); + int convertedSASLCond() const; + bool handleNeed(); + void handleError(); + void srvProcessNext(); + void setTimer(int secs); +}; +} // namespace XMPP + +#endif // XMPP_CLIENTSTREAM_H diff --git a/src/xmpp/xmpp-core/xmpp_stanza.cpp b/src/xmpp/xmpp-core/xmpp_stanza.cpp index fb6e74d6..b3779539 100644 --- a/src/xmpp/xmpp-core/xmpp_stanza.cpp +++ b/src/xmpp/xmpp-core/xmpp_stanza.cpp @@ -18,16 +18,16 @@ #include "xmpp_stanza.h" -#include #include "xmpp/jid/jid.h" -#include "xmpp_stream.h" #include "xmpp_clientstream.h" +#include "xmpp_stream.h" +#include #include using namespace XMPP; -#define NS_STANZAS "urn:ietf:params:xml:ns:xmpp-stanzas" +#define NS_STANZAS "urn:ietf:params:xml:ns:xmpp-stanzas" #define NS_XML "http://www.w3.org/XML/1998/namespace" //---------------------------------------------------------------------------- @@ -55,7 +55,7 @@ using namespace XMPP; both type/condition and code. Error text in output XML is always presented in XMPP-style only. - All functions will always try to guess missing information based on mappings defined in the JEP. + All functions will always try to guess missing information based on mappings defined in the XEP. */ /** @@ -74,52 +74,55 @@ using namespace XMPP; */ Stanza::Error::Error(int _type, int _condition, const QString &_text, const QDomElement &_appSpec) { - type = _type; - condition = _condition; - text = _text; - appSpec = _appSpec; + type = _type; + condition = _condition; + text = _text; + appSpec = _appSpec; originalCode = 0; } - -class Stanza::Error::Private +void Stanza::Error::reset() { + type = 0; + condition = UndefinedCondition; + text.clear(); + by.clear(); + appSpec = QDomElement(); + originalCode = 0; +} + +class Stanza::Error::Private { public: - struct ErrorTypeEntry - { + struct ErrorTypeEntry { const char *str; - int type; + int type; }; static ErrorTypeEntry errorTypeTable[]; - struct ErrorCondEntry - { + struct ErrorCondEntry { const char *str; - int cond; + int cond; }; static ErrorCondEntry errorCondTable[]; - struct ErrorCodeEntry - { + struct ErrorCodeEntry { int cond; int type; int code; }; static ErrorCodeEntry errorCodeTable[]; - struct ErrorDescEntry - { - int cond; + struct ErrorDescEntry { + int cond; const char *name; const char *str; }; static ErrorDescEntry errorDescriptions[]; - static int stringToErrorType(const QString &s) { - for(int n = 0; errorTypeTable[n].str; ++n) { - if(s == errorTypeTable[n].str) + for (int n = 0; errorTypeTable[n].str; ++n) { + if (s == errorTypeTable[n].str) return errorTypeTable[n].type; } return -1; @@ -127,8 +130,8 @@ class Stanza::Error::Private static QString errorTypeToString(int x) { - for(int n = 0; errorTypeTable[n].str; ++n) { - if(x == errorTypeTable[n].type) + for (int n = 0; errorTypeTable[n].str; ++n) { + if (x == errorTypeTable[n].type) return errorTypeTable[n].str; } return QString(); @@ -136,8 +139,8 @@ class Stanza::Error::Private static int stringToErrorCond(const QString &s) { - for(int n = 0; errorCondTable[n].str; ++n) { - if(s == errorCondTable[n].str) + for (int n = 0; errorCondTable[n].str; ++n) { + if (s == errorCondTable[n].str) return errorCondTable[n].cond; } return -1; @@ -145,8 +148,8 @@ class Stanza::Error::Private static QString errorCondToString(int x) { - for(int n = 0; errorCondTable[n].str; ++n) { - if(x == errorCondTable[n].cond) + for (int n = 0; errorCondTable[n].str; ++n) { + if (x == errorCondTable[n].cond) return errorCondTable[n].str; } return QString(); @@ -155,8 +158,8 @@ class Stanza::Error::Private static int errorTypeCondToCode(int t, int c) { Q_UNUSED(t); - for(int n = 0; errorCodeTable[n].cond; ++n) { - if(c == errorCodeTable[n].cond) + for (int n = 0; errorCodeTable[n].cond; ++n) { + if (c == errorCodeTable[n].cond) return errorCodeTable[n].code; } return 0; @@ -164,109 +167,135 @@ class Stanza::Error::Private static QPair errorCodeToTypeCond(int x) { - for(int n = 0; errorCodeTable[n].cond; ++n) { - if(x == errorCodeTable[n].code) + for (int n = 0; errorCodeTable[n].cond; ++n) { + if (x == errorCodeTable[n].code) return QPair(errorCodeTable[n].type, errorCodeTable[n].cond); } return QPair(-1, -1); } - static QPair errorCondToDesc(int x) + static QPair errorCondToDesc(int x) { - for(int n = 0; errorDescriptions[n].str; ++n) { - if(x == errorDescriptions[n].cond) - return QPair(QCoreApplication::translate("Stanza::Error::Private", errorDescriptions[n].name), - QCoreApplication::translate("Stanza::Error::Private", errorDescriptions[n].str)); + for (int n = 0; errorDescriptions[n].str; ++n) { + if (x == errorDescriptions[n].cond) + return QPair( + QCoreApplication::translate("Stanza::Error::Private", errorDescriptions[n].name), + QCoreApplication::translate("Stanza::Error::Private", errorDescriptions[n].str)); } - return QPair(); + return QPair(); } }; -Stanza::Error::Private::ErrorTypeEntry Stanza::Error::Private::errorTypeTable[] = -{ - { "cancel", Cancel }, - { "continue", Continue }, - { "modify", Modify }, - { "auth", Auth }, - { "wait", Wait }, - { 0, 0 }, +Stanza::Error::Private::ErrorTypeEntry Stanza::Error::Private::errorTypeTable[] = { + { "cancel", Cancel }, { "continue", Continue }, { "modify", Modify }, + { "auth", Auth }, { "wait", Wait }, { nullptr, 0 }, }; -Stanza::Error::Private::ErrorCondEntry Stanza::Error::Private::errorCondTable[] = -{ - { "bad-request", BadRequest }, - { "conflict", Conflict }, +Stanza::Error::Private::ErrorCondEntry Stanza::Error::Private::errorCondTable[] = { + { "bad-request", BadRequest }, + { "conflict", Conflict }, { "feature-not-implemented", FeatureNotImplemented }, - { "forbidden", Forbidden }, - { "gone", Gone }, - { "internal-server-error", InternalServerError }, - { "item-not-found", ItemNotFound }, - { "jid-malformed", JidMalformed }, - { "not-acceptable", NotAcceptable }, - { "not-allowed", NotAllowed }, - { "not-authorized", NotAuthorized }, - { "recipient-unavailable", RecipientUnavailable }, - { "redirect", Redirect }, - { "registration-required", RegistrationRequired }, + { "forbidden", Forbidden }, + { "gone", Gone }, + { "internal-server-error", InternalServerError }, + { "item-not-found", ItemNotFound }, + { "jid-malformed", JidMalformed }, + { "not-acceptable", NotAcceptable }, + { "not-allowed", NotAllowed }, + { "not-authorized", NotAuthorized }, + { "recipient-unavailable", RecipientUnavailable }, + { "redirect", Redirect }, + { "registration-required", RegistrationRequired }, { "remote-server-not-found", RemoteServerNotFound }, - { "remote-server-timeout", RemoteServerTimeout }, - { "resource-constraint", ResourceConstraint }, - { "service-unavailable", ServiceUnavailable }, - { "subscription-required", SubscriptionRequired }, - { "undefined-condition", UndefinedCondition }, - { "unexpected-request", UnexpectedRequest }, - { 0, 0 }, + { "remote-server-timeout", RemoteServerTimeout }, + { "resource-constraint", ResourceConstraint }, + { "service-unavailable", ServiceUnavailable }, + { "subscription-required", SubscriptionRequired }, + { "undefined-condition", UndefinedCondition }, + { "unexpected-request", UnexpectedRequest }, + { nullptr, 0 }, }; -Stanza::Error::Private::ErrorCodeEntry Stanza::Error::Private::errorCodeTable[] = -{ - { BadRequest, Modify, 400 }, - { Conflict, Cancel, 409 }, +Stanza::Error::Private::ErrorCodeEntry Stanza::Error::Private::errorCodeTable[] = { + { BadRequest, Modify, 400 }, + { Conflict, Cancel, 409 }, { FeatureNotImplemented, Cancel, 501 }, - { Forbidden, Auth, 403 }, - { Gone, Modify, 302 }, // permanent - { InternalServerError, Wait, 500 }, - { ItemNotFound, Cancel, 404 }, - { JidMalformed, Modify, 400 }, - { NotAcceptable, Modify, 406 }, - { NotAllowed, Cancel, 405 }, - { NotAuthorized, Auth, 401 }, - { RecipientUnavailable, Wait, 404 }, - { Redirect, Modify, 302 }, // temporary - { RegistrationRequired, Auth, 407 }, - { RemoteServerNotFound, Cancel, 404 }, - { RemoteServerTimeout, Wait, 504 }, - { ResourceConstraint, Wait, 500 }, - { ServiceUnavailable, Cancel, 503 }, - { SubscriptionRequired, Auth, 407 }, - { UndefinedCondition, Wait, 500 }, // Note: any type matches really - { UnexpectedRequest, Wait, 400 }, + { Forbidden, Auth, 403 }, + { Gone, Modify, 302 }, // permanent + { InternalServerError, Wait, 500 }, + { ItemNotFound, Cancel, 404 }, + { JidMalformed, Modify, 400 }, + { NotAcceptable, Modify, 406 }, + { NotAllowed, Cancel, 405 }, + { NotAuthorized, Auth, 401 }, + { RecipientUnavailable, Wait, 404 }, + { Redirect, Modify, 302 }, // temporary + { RegistrationRequired, Auth, 407 }, + { RemoteServerNotFound, Cancel, 404 }, + { RemoteServerTimeout, Wait, 504 }, + { ResourceConstraint, Wait, 500 }, + { ServiceUnavailable, Cancel, 503 }, + { SubscriptionRequired, Auth, 407 }, + { UndefinedCondition, Wait, 500 }, // Note: any type matches really + { UnexpectedRequest, Wait, 400 }, { 0, 0, 0 }, }; -Stanza::Error::Private::ErrorDescEntry Stanza::Error::Private::errorDescriptions[] = -{ - { BadRequest, QT_TR_NOOP("Bad request"), QT_TR_NOOP("The sender has sent XML that is malformed or that cannot be processed.") }, - { Conflict, QT_TR_NOOP("Conflict"), QT_TR_NOOP("Access cannot be granted because an existing resource or session exists with the same name or address.") }, - { FeatureNotImplemented, QT_TR_NOOP("Feature not implemented"), QT_TR_NOOP("The feature requested is not implemented by the recipient or server and therefore cannot be processed.") }, - { Forbidden, QT_TR_NOOP("Forbidden"), QT_TR_NOOP("The requesting entity does not possess the required permissions to perform the action.") }, - { Gone, QT_TR_NOOP("Gone"), QT_TR_NOOP("The recipient or server can no longer be contacted at this address.") }, - { InternalServerError, QT_TR_NOOP("Internal server error"), QT_TR_NOOP("The server could not process the stanza because of a misconfiguration or an otherwise-undefined internal server error.") }, - { ItemNotFound, QT_TR_NOOP("Item not found"), QT_TR_NOOP("The addressed JID or item requested cannot be found.") }, - { JidMalformed, QT_TR_NOOP("JID malformed"), QT_TR_NOOP("The sending entity has provided or communicated an XMPP address (e.g., a value of the 'to' attribute) or aspect thereof (e.g., a resource identifier) that does not adhere to the syntax defined in Addressing Scheme.") }, - { NotAcceptable, QT_TR_NOOP("Not acceptable"), QT_TR_NOOP("The recipient or server understands the request but is refusing to process it because it does not meet criteria defined by the recipient or server (e.g., a local policy regarding acceptable words in messages).") }, - { NotAllowed, QT_TR_NOOP("Not allowed"), QT_TR_NOOP("The recipient or server does not allow any entity to perform the action.") }, - { NotAuthorized, QT_TR_NOOP("Not authorized"), QT_TR_NOOP("The sender must provide proper credentials before being allowed to perform the action, or has provided improper credentials.") }, - { RecipientUnavailable, QT_TR_NOOP("Recipient unavailable"), QT_TR_NOOP("The intended recipient is temporarily unavailable.") }, - { Redirect, QT_TR_NOOP("Redirect"), QT_TR_NOOP("The recipient or server is redirecting requests for this information to another entity, usually temporarily.") }, - { RegistrationRequired, QT_TR_NOOP("Registration required"), QT_TR_NOOP("The requesting entity is not authorized to access the requested service because registration is required.") }, - { RemoteServerNotFound, QT_TR_NOOP("Remote server not found"), QT_TR_NOOP("A remote server or service specified as part or all of the JID of the intended recipient does not exist.") }, - { RemoteServerTimeout, QT_TR_NOOP("Remote server timeout"), QT_TR_NOOP("A remote server or service specified as part or all of the JID of the intended recipient (or required to fulfill a request) could not be contacted within a reasonable amount of time.") }, - { ResourceConstraint, QT_TR_NOOP("Resource constraint"), QT_TR_NOOP("The server or recipient lacks the system resources necessary to service the request.") }, - { ServiceUnavailable, QT_TR_NOOP("Service unavailable"), QT_TR_NOOP("The server or recipient does not currently provide the requested service.") }, - { SubscriptionRequired, QT_TR_NOOP("Subscription required"), QT_TR_NOOP("The requesting entity is not authorized to access the requested service because a subscription is required.") }, - { UndefinedCondition, QT_TR_NOOP("Undefined condition"), QT_TR_NOOP("The error condition is not one of those defined by the other conditions in this list.") }, - { UnexpectedRequest, QT_TR_NOOP("Unexpected request"), QT_TR_NOOP("The recipient or server understood the request but was not expecting it at this time (e.g., the request was out of order).") }, +Stanza::Error::Private::ErrorDescEntry Stanza::Error::Private::errorDescriptions[] = { + { BadRequest, QT_TR_NOOP("Bad request"), + QT_TR_NOOP("The sender has sent XML that is malformed or that cannot be processed.") }, + { Conflict, QT_TR_NOOP("Conflict"), + QT_TR_NOOP( + "Access cannot be granted because an existing resource or session exists with the same name or address.") }, + { FeatureNotImplemented, QT_TR_NOOP("Feature not implemented"), + QT_TR_NOOP( + "The feature requested is not implemented by the recipient or server and therefore cannot be processed.") }, + { Forbidden, QT_TR_NOOP("Forbidden"), + QT_TR_NOOP("The requesting entity does not possess the required permissions to perform the action.") }, + { Gone, QT_TR_NOOP("Gone"), QT_TR_NOOP("The recipient or server can no longer be contacted at this address.") }, + { InternalServerError, QT_TR_NOOP("Internal server error"), + QT_TR_NOOP("The server could not process the stanza because of a misconfiguration or an otherwise-undefined " + "internal server error.") }, + { ItemNotFound, QT_TR_NOOP("Item not found"), QT_TR_NOOP("The addressed JID or item requested cannot be found.") }, + { JidMalformed, QT_TR_NOOP("JID malformed"), + QT_TR_NOOP("The sending entity has provided or communicated an XMPP address (e.g., a value of the 'to' " + "attribute) or aspect thereof (e.g., a resource identifier) that does not adhere to the syntax " + "defined in Addressing Scheme.") }, + { NotAcceptable, QT_TR_NOOP("Not acceptable"), + QT_TR_NOOP("The recipient or server understands the request but is refusing to process it because it does not " + "meet criteria defined by the recipient or server (e.g., a local policy regarding acceptable words in " + "messages).") }, + { NotAllowed, QT_TR_NOOP("Not allowed"), + QT_TR_NOOP("The recipient or server does not allow any entity to perform the action.") }, + { NotAuthorized, QT_TR_NOOP("Not authorized"), + QT_TR_NOOP("The sender must provide proper credentials before being allowed to perform the action, or has " + "provided improper credentials.") }, + { RecipientUnavailable, QT_TR_NOOP("Recipient unavailable"), + QT_TR_NOOP("The intended recipient is temporarily unavailable.") }, + { Redirect, QT_TR_NOOP("Redirect"), + QT_TR_NOOP("The recipient or server is redirecting requests for this information to another entity, usually " + "temporarily.") }, + { RegistrationRequired, QT_TR_NOOP("Registration required"), + QT_TR_NOOP("The requesting entity is not authorized to access the requested service because registration is " + "required.") }, + { RemoteServerNotFound, QT_TR_NOOP("Remote server not found"), + QT_TR_NOOP( + "A remote server or service specified as part or all of the JID of the intended recipient does not exist.") }, + { RemoteServerTimeout, QT_TR_NOOP("Remote server timeout"), + QT_TR_NOOP("A remote server or service specified as part or all of the JID of the intended recipient (or " + "required to fulfill a request) could not be contacted within a reasonable amount of time.") }, + { ResourceConstraint, QT_TR_NOOP("Resource constraint"), + QT_TR_NOOP("The server or recipient lacks the system resources necessary to service the request.") }, + { ServiceUnavailable, QT_TR_NOOP("Service unavailable"), + QT_TR_NOOP("The server or recipient does not currently provide the requested service.") }, + { SubscriptionRequired, QT_TR_NOOP("Subscription required"), + QT_TR_NOOP("The requesting entity is not authorized to access the requested service because a subscription is " + "required.") }, + { UndefinedCondition, QT_TR_NOOP("Undefined condition"), + QT_TR_NOOP("The error condition is not one of those defined by the other conditions in this list.") }, + { UnexpectedRequest, QT_TR_NOOP("Unexpected request"), + QT_TR_NOOP("The recipient or server understood the request but was not expecting it at this time (e.g., the " + "request was out of order).") }, }; /** @@ -277,10 +306,7 @@ Stanza::Error::Private::ErrorDescEntry Stanza::Error::Private::errorDescriptions 0 means unknown code. */ -int Stanza::Error::code() const -{ - return originalCode ? originalCode : Private::errorTypeCondToCode(type, condition); -} +int Stanza::Error::code() const { return originalCode ? originalCode : Private::errorTypeCondToCode(type, condition); } /** \brief Creates a StanzaError from \a code. @@ -291,11 +317,11 @@ int Stanza::Error::code() const bool Stanza::Error::fromCode(int code) { QPair guess = Private::errorCodeToTypeCond(code); - if(guess.first == -1 || guess.second == -1) + if (guess.first == -1 || guess.second == -1) return false; - type = guess.first; - condition = guess.second; + type = guess.first; + condition = guess.second; originalCode = code; return true; @@ -311,36 +337,36 @@ bool Stanza::Error::fromCode(int code) */ bool Stanza::Error::fromXml(const QDomElement &e, const QString &baseNS) { - if(e.tagName() != "error" && e.namespaceURI() != baseNS) + if (e.tagName() != "error" && e.namespaceURI() != baseNS) return false; // type - type = Private::stringToErrorType(e.attribute("type")); - by = e.attribute(QLatin1String("by")); - - // condition - QDomNodeList nl = e.childNodes(); - QDomElement t; + type = Private::stringToErrorType(e.attribute("type")); + by = e.attribute(QLatin1String("by")); condition = -1; - int n; - for(n = 0; n < nl.count(); ++n) { - QDomNode i = nl.item(n); - t = i.toElement(); - if(!t.isNull()) { - // FIX-ME: this shouldn't be needed - if(t.namespaceURI() == NS_STANZAS || t.attribute("xmlns") == NS_STANZAS) { + + QString textTag(QString::fromLatin1("text")); + for (auto t = e.firstChildElement(); !t.isNull(); t = t.nextSiblingElement()) { + if (t.namespaceURI() == NS_STANZAS) { + if (t.tagName() == textTag) { + text = t.text().trimmed(); + } else { condition = Private::stringToErrorCond(t.tagName()); - if (condition != -1) - break; } + } else { + appSpec = t; } + + if (condition != -1 && !appSpec.isNull() && !text.isEmpty()) + break; } // code - originalCode = e.attribute("code").toInt(); // deprecated. rfc6120 has just a little note about it. also see XEP-0086 + originalCode + = e.attribute("code").toInt(); // deprecated. rfc6120 has just a little note about it. also see XEP-0086 // try to guess type/condition - if(type == -1 || condition == -1) { + if (type == -1 || condition == -1) { QPair guess(-1, -1); if (originalCode) guess = Private::errorCodeToTypeCond(originalCode); @@ -351,24 +377,6 @@ bool Stanza::Error::fromXml(const QDomElement &e, const QString &baseNS) condition = guess.second != -1 ? guess.second : UndefinedCondition; } - // text - t = e.elementsByTagNameNS(NS_STANZAS, "text").item(0).toElement(); - if(!t.isNull()) - text = t.text().trimmed(); - else - text = e.text().trimmed(); - - // appspec: find first non-standard namespaced element - appSpec = QDomElement(); - nl = e.childNodes(); - for(n = 0; n < nl.count(); ++n) { - QDomNode i = nl.item(n); - if(i.isElement() && i.namespaceURI() != NS_STANZAS) { - appSpec = i.toElement(); - break; - } - } - return true; } @@ -387,10 +395,10 @@ QDomElement Stanza::Error::toXml(QDomDocument &doc, const QString &baseNS) const // XMPP error QString stype = Private::errorTypeToString(type); - if(stype.isEmpty()) + if (stype.isEmpty()) return errElem; QString scond = Private::errorCondToString(condition); - if(scond.isEmpty()) + if (scond.isEmpty()) return errElem; errElem.setAttribute("type", stype); @@ -398,17 +406,17 @@ QDomElement Stanza::Error::toXml(QDomDocument &doc, const QString &baseNS) const errElem.setAttribute("by", by); } errElem.appendChild(t = doc.createElementNS(NS_STANZAS, scond)); - t.setAttribute("xmlns", NS_STANZAS); // FIX-ME: this shouldn't be needed + // t.setAttribute("xmlns", NS_STANZAS); // FIX-ME: this shouldn't be needed // old code int scode = code(); - if(scode) + if (scode) errElem.setAttribute("code", scode); // text - if(!text.isEmpty()) { + if (!text.isEmpty()) { t = doc.createElementNS(NS_STANZAS, "text"); - t.setAttribute("xmlns", NS_STANZAS); // FIX-ME: this shouldn't be needed + // t.setAttribute("xmlns", NS_STANZAS); // FIX-ME: this shouldn't be needed t.appendChild(doc.createTextNode(text)); errElem.appendChild(t); } @@ -424,10 +432,7 @@ QDomElement Stanza::Error::toXml(QDomDocument &doc, const QString &baseNS) const Returns the error name (e.g. "Not Allowed") and generic description. */ -QPair Stanza::Error::description() const -{ - return Private::errorCondToDesc(condition); -} +QPair Stanza::Error::description() const { return Private::errorCondToDesc(condition); } /** * \brief Returns string human-reabable representation of the error @@ -444,16 +449,15 @@ QString Stanza::Error::toString() const //---------------------------------------------------------------------------- // Stanza //---------------------------------------------------------------------------- -class Stanza::Private -{ +class Stanza::Private { public: static int stringToKind(const QString &s) { - if(s == QLatin1String("message")) + if (s == QLatin1String("message")) return Message; - else if(s == QLatin1String("presence")) + else if (s == QLatin1String("presence")) return Presence; - else if(s == QLatin1String("iq")) + else if (s == QLatin1String("iq")) return IQ; else return -1; @@ -461,23 +465,20 @@ class Stanza::Private static QString kindToString(Kind k) { - if(k == Message) + if (k == Message) return QLatin1String("message"); - else if(k == Presence) + else if (k == Presence) return QLatin1String("presence"); else return QLatin1String("iq"); } - Stream *s; - QDomElement e; + Stream *s; + QDomElement e; QSharedPointer sharedDoc; }; -Stanza::Stanza() -{ - d = 0; -} +Stanza::Stanza() { d = nullptr; } Stanza::Stanza(Stream *s, Kind k, const Jid &to, const QString &type, const QString &id) { @@ -485,83 +486,65 @@ Stanza::Stanza(Stream *s, Kind k, const Jid &to, const QString &type, const QStr d = new Private; Kind kind; - if(k == Message || k == Presence || k == IQ) + if (k == Message || k == Presence || k == IQ) kind = k; else kind = Message; d->s = s; - if(d->s) + if (d->s) d->e = d->s->doc().createElementNS(s->baseNS(), Private::kindToString(kind)); - if(to.isValid()) + if (to.isValid()) setTo(to); - if(!type.isEmpty()) + if (!type.isEmpty()) setType(type); - if(!id.isEmpty()) + if (!id.isEmpty()) setId(id); } Stanza::Stanza(Stream *s, const QDomElement &e) { Q_ASSERT(s); - d = 0; - if(e.namespaceURI() != s->baseNS()) + d = nullptr; + if (e.namespaceURI() != s->baseNS()) return; int x = Private::stringToKind(e.tagName()); - if(x == -1) + if (x == -1) return; - d = new Private; + d = new Private; d->s = s; d->e = e; } Stanza::Stanza(const Stanza &from) { - d = 0; + d = nullptr; *this = from; } -Stanza & Stanza::operator=(const Stanza &from) +Stanza &Stanza::operator=(const Stanza &from) { - if(&from == this) + if (&from == this) return *this; delete d; - d = 0; - if(from.d) + d = nullptr; + if (from.d) d = new Private(*from.d); return *this; } -Stanza::~Stanza() -{ - delete d; -} +Stanza::~Stanza() { delete d; } -bool Stanza::isNull() const -{ - return (d ? false: true); -} +bool Stanza::isNull() const { return d == nullptr; } -QDomElement Stanza::element() const -{ - return d->e; -} +QDomElement Stanza::element() const { return d->e; } -QString Stanza::toString() const -{ - return Stream::xmlToString(d->e); -} +QString Stanza::toString() const { return Stream::xmlToString(d->e); } -QDomDocument & Stanza::doc() const -{ - return d->s->doc(); -} +QDomDocument &Stanza::doc() const { return d->s->doc(); } -QString Stanza::baseNS() const -{ - return d->s->baseNS(); -} +QString Stanza::baseNS() const { return d->s->baseNS(); } QDomElement Stanza::createElement(const QString &ns, const QString &tagName) { @@ -575,81 +558,39 @@ QDomElement Stanza::createTextElement(const QString &ns, const QString &tagName, return e; } -void Stanza::appendChild(const QDomElement &e) -{ - d->e.appendChild(e); -} +void Stanza::appendChild(const QDomElement &e) { d->e.appendChild(e); } -Stanza::Kind Stanza::kind() const -{ - return (Kind)Private::stringToKind(d->e.tagName()); -} +Stanza::Kind Stanza::kind() const { return (Kind)Private::stringToKind(d->e.tagName()); } -Stanza::Kind Stanza::kind(const QString &tagName) -{ - return (Kind)Private::stringToKind(tagName); -} +Stanza::Kind Stanza::kind(const QString &tagName) { return (Kind)Private::stringToKind(tagName); } -void Stanza::setKind(Kind k) -{ - d->e.setTagName(Private::kindToString(k)); -} +void Stanza::setKind(Kind k) { d->e.setTagName(Private::kindToString(k)); } -Jid Stanza::to() const -{ - return Jid(d->e.attribute("to")); -} +Jid Stanza::to() const { return Jid(d->e.attribute("to")); } -Jid Stanza::from() const -{ - return Jid(d->e.attribute("from")); -} +Jid Stanza::from() const { return Jid(d->e.attribute("from")); } -QString Stanza::id() const -{ - return d->e.attribute("id"); -} +QString Stanza::id() const { return d->e.attribute("id"); } -QString Stanza::type() const -{ - return d->e.attribute("type"); -} +QString Stanza::type() const { return d->e.attribute("type"); } -QString Stanza::lang() const -{ - return d->e.attributeNS(NS_XML, "lang", QString()); -} +QString Stanza::lang() const { return d->e.attributeNS(NS_XML, "lang", QString()); } -void Stanza::setTo(const Jid &j) -{ - d->e.setAttribute("to", j.full()); -} +void Stanza::setTo(const Jid &j) { d->e.setAttribute("to", j.full()); } -void Stanza::setFrom(const Jid &j) -{ - d->e.setAttribute("from", j.full()); -} +void Stanza::setFrom(const Jid &j) { d->e.setAttribute("from", j.full()); } -void Stanza::setId(const QString &id) -{ - d->e.setAttribute("id", id); -} +void Stanza::setId(const QString &id) { d->e.setAttribute("id", id); } -void Stanza::setType(const QString &type) -{ - d->e.setAttribute("type", type); -} +void Stanza::setType(const QString &type) { d->e.setAttribute("type", type); } -void Stanza::setLang(const QString &lang) -{ - d->e.setAttribute("xml:lang", lang); -} +void Stanza::setLang(const QString &lang) { d->e.setAttribute("xml:lang", lang); } Stanza::Error Stanza::error() const { - Error err; + Error err; QDomElement e = d->e.elementsByTagNameNS(d->s->baseNS(), "error").item(0).toElement(); - if(!e.isNull()) + if (!e.isNull()) err.fromXml(e, d->s->baseNS()); return err; @@ -657,14 +598,13 @@ Stanza::Error Stanza::error() const void Stanza::setError(const Error &err) { - QDomDocument doc = d->e.ownerDocument(); - QDomElement errElem = err.toXml(doc, d->s->baseNS()); + QDomDocument doc = d->e.ownerDocument(); + QDomElement errElem = err.toXml(doc, d->s->baseNS()); QDomElement oldElem = d->e.elementsByTagNameNS(d->s->baseNS(), "error").item(0).toElement(); - if(oldElem.isNull()) { + if (oldElem.isNull()) { d->e.appendChild(errElem); - } - else { + } else { d->e.replaceChild(errElem, oldElem); } } @@ -672,7 +612,7 @@ void Stanza::setError(const Error &err) void Stanza::clearError() { QDomElement errElem = d->e.elementsByTagNameNS(d->s->baseNS(), "error").item(0).toElement(); - if(!errElem.isNull()) + if (!errElem.isNull()) d->e.removeChild(errElem); } @@ -681,8 +621,7 @@ QSharedPointer Stanza::unboundDocument(QSharedPointer(new QDomDocument); } - d->e = sd->importNode(d->e, true).toElement(); + d->e = sd->importNode(d->e, true).toElement(); d->sharedDoc = sd; return d->sharedDoc; } - diff --git a/src/xmpp/xmpp-core/xmpp_stanza.h b/src/xmpp/xmpp-core/xmpp_stanza.h index 34558ea0..c562f58c 100644 --- a/src/xmpp/xmpp-core/xmpp_stanza.h +++ b/src/xmpp/xmpp-core/xmpp_stanza.h @@ -19,124 +19,123 @@ #ifndef XMPP_STANZA_H #define XMPP_STANZA_H -#include -#include #include +#include #include +#include class QDomDocument; -namespace XMPP -{ - class Jid; - class Stream; +namespace XMPP { +class Jid; +class Stream; - class Stanza - { - public: - enum Kind { Message, Presence, IQ }; - - Stanza(); - Stanza(const Stanza &from); - Stanza & operator=(const Stanza &from); - virtual ~Stanza(); - - class Error - { - public: - enum ErrorType { Cancel = 1, Continue, Modify, Auth, Wait }; - enum ErrorCond - { - BadRequest = 1, - Conflict, - FeatureNotImplemented, - Forbidden, - Gone, - InternalServerError, - ItemNotFound, - JidMalformed, - NotAcceptable, - NotAllowed, - NotAuthorized, - PaymentRequired, - RecipientUnavailable, - Redirect, - RegistrationRequired, - RemoteServerNotFound, - RemoteServerTimeout, - ResourceConstraint, - ServiceUnavailable, - SubscriptionRequired, - UndefinedCondition, - UnexpectedRequest - }; - - Error(int type=Cancel, int condition=UndefinedCondition, const QString &text=QString(), const QDomElement &appSpec=QDomElement()); - - int type; - int condition; - QString text; - QString by; - QDomElement appSpec; - - int code() const; - bool fromCode(int code); - - QPair description() const; - QString toString() const; - - QDomElement toXml(QDomDocument &doc, const QString &baseNS) const; - bool fromXml(const QDomElement &e, const QString &baseNS); - private: - class Private; - int originalCode; +class Stanza { +public: + enum Kind { Message, Presence, IQ }; + Stanza(); + Stanza(const Stanza &from); + Stanza &operator=(const Stanza &from); + virtual ~Stanza(); + + class Error { + public: + enum ErrorType { Cancel = 1, Continue, Modify, Auth, Wait }; + enum ErrorCond { + BadRequest = 1, + Conflict, + FeatureNotImplemented, + Forbidden, + Gone, + InternalServerError, + ItemNotFound, + JidMalformed, + NotAcceptable, + NotAllowed, + NotAuthorized, + PaymentRequired, + RecipientUnavailable, + Redirect, + RegistrationRequired, + RemoteServerNotFound, + RemoteServerTimeout, + ResourceConstraint, + ServiceUnavailable, + SubscriptionRequired, + UndefinedCondition, + UnexpectedRequest }; - bool isNull() const; + Error(int type = Cancel, int condition = UndefinedCondition, const QString &text = QString(), + const QDomElement &appSpec = QDomElement()); + + int type; + int condition; + QString text; + QString by; + QDomElement appSpec; + + void reset(); + + int code() const; + bool fromCode(int code); - QDomElement element() const; - QString toString() const; + QPair description() const; + QString toString() const; - QDomDocument & doc() const; - QString baseNS() const; - QDomElement createElement(const QString &ns, const QString &tagName); - QDomElement createTextElement(const QString &ns, const QString &tagName, const QString &text); - void appendChild(const QDomElement &e); + QDomElement toXml(QDomDocument &doc, const QString &baseNS) const; + bool fromXml(const QDomElement &e, const QString &baseNS); - Kind kind() const; - static Kind kind(const QString &tagName); - void setKind(Kind k); + private: + class Private; + int originalCode; + }; - Jid to() const; - Jid from() const; - QString id() const; - QString type() const; - QString lang() const; + bool isNull() const; - void setTo(const Jid &j); - void setFrom(const Jid &j); - void setId(const QString &id); - void setType(const QString &type); - void setLang(const QString &lang); + QDomElement element() const; + QString toString() const; - Error error() const; - void setError(const Error &err); - void clearError(); + QDomDocument &doc() const; + QString baseNS() const; + QDomElement createElement(const QString &ns, const QString &tagName); + QDomElement createTextElement(const QString &ns, const QString &tagName, const QString &text); + void appendChild(const QDomElement &e); - void markHandled(); - void setSMId(unsigned long id); + Kind kind() const; + static Kind kind(const QString &tagName); + void setKind(Kind k); - QSharedPointer unboundDocument(QSharedPointer); + Jid to() const; + Jid from() const; + QString id() const; + QString type() const; + QString lang() const; - private: - friend class Stream; - Stanza(Stream *s, Kind k, const Jid &to, const QString &type, const QString &id); - Stanza(Stream *s, const QDomElement &e); + void setTo(const Jid &j); + void setFrom(const Jid &j); + void setId(const QString &id); + void setType(const QString &type); + void setLang(const QString &lang); - class Private; - Private *d; - }; -} + Error error() const; + void setError(const Error &err); + void clearError(); + + void markHandled(); + void setSMId(unsigned long id); + + QSharedPointer unboundDocument(QSharedPointer); + +private: + friend class Stream; + Stanza(Stream *s, Kind k, const Jid &to, const QString &type, const QString &id); + Stanza(Stream *s, const QDomElement &e); + + class Private; + Private *d; +}; +} // namespace XMPP -#endif +#endif // XMPP_STANZA_H diff --git a/src/xmpp/xmpp-core/xmpp_stream.h b/src/xmpp/xmpp-core/xmpp_stream.h index 916a9807..bf1a3fee 100644 --- a/src/xmpp/xmpp-core/xmpp_stream.h +++ b/src/xmpp/xmpp-core/xmpp_stream.h @@ -20,65 +20,63 @@ #ifndef XMPP_STREAM_H #define XMPP_STREAM_H +#include "xmpp/jid/jid.h" +#include "xmpp_stanza.h" + #include #include -#include "xmpp_stanza.h" -#include "xmpp/jid/jid.h" - class QDomDocument; -namespace XMPP -{ - class Stream : public QObject - { - Q_OBJECT - public: - enum Error { ErrParse, ErrProtocol, ErrStream, ErrCustom = 10 }; - enum StreamCond { - GenericStreamError, - Conflict, - ConnectionTimeout, - InternalServerError, - InvalidFrom, - InvalidXml, - PolicyViolation, - ResourceConstraint, - SystemShutdown, - StreamReset - }; +namespace XMPP { +class Stream : public QObject { + Q_OBJECT +public: + enum Error { ErrParse, ErrProtocol, ErrStream, ErrCustom = 10 }; + enum StreamCond { + GenericStreamError, + Conflict, + ConnectionTimeout, + InternalServerError, + InvalidFrom, + InvalidXml, + PolicyViolation, + ResourceConstraint, + SystemShutdown, + StreamReset + }; - Stream(QObject *parent=0); - virtual ~Stream(); + Stream(QObject *parent = nullptr); + virtual ~Stream(); - virtual QDomDocument & doc() const=0; - virtual QString baseNS() const=0; - virtual bool old() const=0; + virtual QDomDocument &doc() const = 0; + virtual QString baseNS() const = 0; + virtual bool old() const = 0; - virtual void close()=0; - virtual bool stanzaAvailable() const=0; - virtual Stanza read()=0; - virtual void write(const Stanza &s)=0; + virtual void close() = 0; + virtual bool stanzaAvailable() const = 0; + virtual Stanza read() = 0; + virtual void write(const Stanza &s) = 0; - virtual int errorCondition() const=0; - virtual QString errorText() const=0; - virtual QHash errorLangText() const=0; // localized error descriptions - virtual QDomElement errorAppSpec() const=0; + virtual int errorCondition() const = 0; + virtual QString errorText() const = 0; + virtual QHash errorLangText() const = 0; // localized error descriptions + virtual QDomElement errorAppSpec() const = 0; - Stanza createStanza(Stanza::Kind k, const Jid &to="", const QString &type="", const QString &id=""); - Stanza createStanza(const QDomElement &e); + Stanza createStanza(Stanza::Kind k, const Jid &to = "", const QString &type = "", const QString &id = ""); + Stanza createStanza(const QDomElement &e); - static QString xmlToString(const QDomElement &e, bool clip=false); + static QString xmlToString(const QDomElement &e, bool clip = false); - static void cleanup(); + static void cleanup(); - signals: - void connectionClosed(); - void delayedCloseFinished(); - void readyRead(); - void stanzaWritten(); - void error(int); - }; -} +signals: + void connectionClosed(); + void delayedCloseFinished(); + void readyRead(); + void stanzaWritten(); + void error(int); +}; +} // namespace XMPP -#endif +#endif // XMPP_STREAM_H diff --git a/src/xmpp/xmpp-im/client.cpp b/src/xmpp/xmpp-im/client.cpp index ad55ea17..2761ff91 100644 --- a/src/xmpp/xmpp-im/client.cpp +++ b/src/xmpp/xmpp-im/client.cpp @@ -65,96 +65,101 @@ //! } //! \endcode -#include -#include -#include -#include -#include - +#include "filetransfer.h" +#include "httpfileupload.h" #include "im.h" -#include "xmpp_tasks.h" -#include "xmpp_xmlcommon.h" +#include "jingle-ft.h" +#include "jingle-ibb.h" +#include "jingle-ice.h" +#include "jingle-s5b.h" +#include "jingle.h" #include "s5b.h" -#include "xmpp_ibb.h" +#include "stundisco.h" +#include "tcpportreserver.h" +#include "xmpp/xmpp-core/protocol.h" #include "xmpp_bitsofbinary.h" -#include "filetransfer.h" #include "xmpp_caps.h" #include "xmpp_carbons.h" +#include "xmpp_externalservicediscovery.h" #include "xmpp_hash.h" +#include "xmpp_ibb.h" #include "xmpp_serverinfomanager.h" -#include "protocol.h" -#include "xmpp_serverinfomanager.h" -#include "httpfileupload.h" -#include "jingle.h" -#include "jingle-ft.h" -#include "jingle-s5b.h" +#include "xmpp_tasks.h" +#include "xmpp_xmlcommon.h" + +#include +#include +#include +#include +#include #ifdef Q_OS_WIN #define vsnprintf _vsnprintf #endif -namespace XMPP -{ +#define GROUPS_DELIMITER_TIMEOUT 10 +namespace XMPP { //---------------------------------------------------------------------------- // Client //---------------------------------------------------------------------------- -class Client::GroupChat -{ +class Client::GroupChat { public: enum { Connecting, Connected, Closing }; - GroupChat() = default; + GroupChat() = default; - Jid j; - int status = 0; + Jid j; + int status = 0; QString password; }; -class Client::ClientPrivate -{ +class Client::ClientPrivate { public: - ClientPrivate() {} - - QPointer stream; - QDomDocument doc; - int id_seed = 0xaaaa; - Task *root = nullptr; - QNetworkAccessManager *qnam = nullptr; - QString host, user, pass, resource; - QString osName, osVersion, tzname, clientName, clientVersion; - CapsSpec caps, serverCaps; - DiscoItem::Identity identity; - Features features; - QMap extension_features; - int tzoffset = 0; - bool useTzoffset = false; // manual tzoffset is old way of doing utc<->local translations - bool active = false; - - LiveRoster roster; - ResourceList resourceList; - CapsManager *capsman = nullptr; - CarbonsManager *carbonsman = nullptr; - S5BManager *s5bman = nullptr; - Jingle::S5B::Manager *jingleS5BManager = nullptr; - IBBManager *ibbman = nullptr; - BoBManager *bobman = nullptr; - FileTransferManager *ftman = nullptr; - ServerInfoManager *serverInfoManager = nullptr; - HttpFileUploadManager *httpFileUploadManager = nullptr; - Jingle::Manager *jingleManager = nullptr; - QList groupChatList; - EncryptionHandler *encryptionHandler = nullptr; - JT_PushMessage *pushMessage = nullptr; + ClientPrivate() { } + + QPointer stream; + QDomDocument doc; + Task *root = nullptr; + QNetworkAccessManager *qnam = nullptr; + QString host, user, pass, resource; + QString osName, osVersion, tzname, clientName, clientVersion; + CapsSpec caps, serverCaps; + DiscoItem::Identity identity; + Features features; + QMap extension_features; + int tzoffset = 0; + bool useTzoffset = false; // manual tzoffset is old way of doing utc<->local translations + bool active = false; + bool capsOptimization = false; // don't send caps every time + + LiveRoster roster; + ResourceList resourceList; + CapsManager *capsman = nullptr; + CarbonsManager *carbonsman = nullptr; + TcpPortReserver *tcpPortReserver = nullptr; + S5BManager *s5bman = nullptr; + Jingle::S5B::Manager *jingleS5BManager = nullptr; + Jingle::IBB::Manager *jingleIBBManager = nullptr; + Jingle::ICE::Manager *jingleICEManager = nullptr; + IBBManager *ibbman = nullptr; + BoBManager *bobman = nullptr; + FileTransferManager *ftman = nullptr; + ServerInfoManager *serverInfoManager = nullptr; + ExternalServiceDiscovery *externalServiceDiscovery = nullptr; + StunDiscoManager *stunDiscoManager = nullptr; + HttpFileUploadManager *httpFileUploadManager = nullptr; + Jingle::Manager *jingleManager = nullptr; + QList groupChatList; + EncryptionHandler *encryptionHandler = nullptr; + JT_PushMessage *pushMessage = nullptr; }; - -Client::Client(QObject *par) -:QObject(par) +Client::Client(QObject *par) : QObject(par) { - d = new ClientPrivate; - d->active = false; - d->osName = "N/A"; - d->clientName = "N/A"; + d = new ClientPrivate; + d->active = false; + d->osName = "N/A"; + d->clientName = "N/A"; d->clientVersion = "0.0"; d->root = new Task(this, true); @@ -171,40 +176,47 @@ Client::Client(QObject *par) d->capsman = new CapsManager(this); - d->serverInfoManager = new ServerInfoManager(this); - d->httpFileUploadManager = new HttpFileUploadManager(this); + d->serverInfoManager = new ServerInfoManager(this); + d->externalServiceDiscovery = new ExternalServiceDiscovery(this); + d->stunDiscoManager = new StunDiscoManager(this); + d->httpFileUploadManager = new HttpFileUploadManager(this); d->jingleManager = new Jingle::Manager(this); - auto ft = new Jingle::FileTransfer::Manager(this); - d->jingleManager->registerApp(Jingle::FileTransfer::NS, ft); + auto ft = new Jingle::FileTransfer::Manager(this); + d->jingleManager->registerApplication(ft); d->jingleS5BManager = new Jingle::S5B::Manager(d->jingleManager); - d->jingleManager->registerTransport(Jingle::S5B::NS, d->jingleS5BManager); + d->jingleIBBManager = new Jingle::IBB::Manager(d->jingleManager); + d->jingleICEManager = new Jingle::ICE::Manager(d->jingleManager); + d->jingleManager->registerTransport(d->jingleS5BManager); + d->jingleManager->registerTransport(d->jingleIBBManager); + d->jingleManager->registerTransport(d->jingleICEManager); } Client::~Client() { - //fprintf(stderr, "\tClient::~Client\n"); - //fflush(stderr); + // fprintf(stderr, "\tClient::~Client\n"); + // fflush(stderr); close(true); delete d->ftman; delete d->ibbman; delete d->s5bman; + delete d->jingleManager; delete d->root; delete d; - //fprintf(stderr, "\tClient::~Client\n"); + // fprintf(stderr, "\tClient::~Client\n"); } void Client::connectToServer(ClientStream *s, const Jid &j, bool auth) { d->stream = s; - //connect(d->stream, SIGNAL(connected()), SLOT(streamConnected())); - //connect(d->stream, SIGNAL(handshaken()), SLOT(streamHandshaken())); + // connect(d->stream, SIGNAL(connected()), SLOT(streamConnected())); + // connect(d->stream, SIGNAL(handshaken()), SLOT(streamHandshaken())); connect(d->stream, SIGNAL(error(int)), SLOT(streamError(int))); - //connect(d->stream, SIGNAL(sslCertificateReady(QSSLCert)), SLOT(streamSSLCertificateReady(QSSLCert))); + // connect(d->stream, SIGNAL(sslCertificateReady(QSSLCert)), SLOT(streamSSLCertificateReady(QSSLCert))); connect(d->stream, SIGNAL(readyRead()), SLOT(streamReadyRead())); - //connect(d->stream, SIGNAL(closeFinished()), SLOT(streamCloseFinished())); + // connect(d->stream, SIGNAL(closeFinished()), SLOT(streamCloseFinished())); connect(d->stream, SIGNAL(incomingXml(QString)), SLOT(streamIncomingXml(QString))); connect(d->stream, SIGNAL(outgoingXml(QString)), SLOT(streamOutgoingXml(QString))); connect(d->stream, SIGNAL(haveUnhandledFeatures()), SLOT(parseUnhandledStreamFeatures())); @@ -215,9 +227,9 @@ void Client::connectToServer(ClientStream *s, const Jid &j, bool auth) void Client::start(const QString &host, const QString &user, const QString &pass, const QString &_resource) { // TODO - d->host = host; - d->user = user; - d->pass = pass; + d->host = host; + d->user = user; + d->pass = pass; d->resource = _resource; Status stat; @@ -225,8 +237,8 @@ void Client::start(const QString &host, const QString &user, const QString &pass d->resourceList += Resource(resource(), stat); JT_PushPresence *pp = new JT_PushPresence(rootTask()); - connect(pp, SIGNAL(subscription(Jid,QString,QString)), SLOT(ppSubscription(Jid,QString,QString))); - connect(pp, SIGNAL(presence(Jid,Status)), SLOT(ppPresence(Jid,Status))); + connect(pp, SIGNAL(subscription(Jid, QString, QString)), SLOT(ppSubscription(Jid, QString, QString))); + connect(pp, SIGNAL(presence(Jid, Status)), SLOT(ppPresence(Jid, Status))); d->pushMessage = new JT_PushMessage(rootTask(), d->encryptionHandler); connect(d->pushMessage, SIGNAL(message(Message)), SLOT(pmMessage(Message))); @@ -241,85 +253,71 @@ void Client::start(const QString &host, const QString &user, const QString &pass d->active = true; } +void Client::setTcpPortReserver(TcpPortReserver *portReserver) { d->tcpPortReserver = portReserver; } + +TcpPortReserver *Client::tcpPortReserver() const { return d->tcpPortReserver; } + void Client::setFileTransferEnabled(bool b) { - if(b) { - if(!d->ftman) + if (b) { + if (!d->ftman) d->ftman = new FileTransferManager(this); - } - else { - if(d->ftman) { + } else { + if (d->ftman) { delete d->ftman; - d->ftman = 0; + d->ftman = nullptr; } } } -FileTransferManager *Client::fileTransferManager() const -{ - return d->ftman; -} +FileTransferManager *Client::fileTransferManager() const { return d->ftman; } -S5BManager *Client::s5bManager() const -{ - return d->s5bman; -} +S5BManager *Client::s5bManager() const { return d->s5bman; } -Jingle::S5B::Manager *Client::jingleS5BManager() const -{ - return d->jingleS5BManager; -} +Jingle::S5B::Manager *Client::jingleS5BManager() const { return d->jingleS5BManager; } -IBBManager *Client::ibbManager() const -{ - return d->ibbman; -} +Jingle::IBB::Manager *Client::jingleIBBManager() const { return d->jingleIBBManager; } -BoBManager *Client::bobManager() const -{ - return d->bobman; -} +Jingle::ICE::Manager *Client::jingleICEManager() const { return d->jingleICEManager; } -CapsManager *Client::capsManager() const -{ - return d->capsman; -} +IBBManager *Client::ibbManager() const { return d->ibbman; } -ServerInfoManager *Client::serverInfoManager() const -{ - return d->serverInfoManager; -} +BoBManager *Client::bobManager() const { return d->bobman; } -CarbonsManager *Client::carbonsManager() const -{ - return d->carbonsman; -} +CapsManager *Client::capsManager() const { return d->capsman; } -JT_PushMessage *Client::pushMessage() const -{ - return d->pushMessage; -} +void Client::setCapsOptimizationAllowed(bool allowed) { d->capsOptimization = allowed; } -HttpFileUploadManager *Client::httpFileUploadManager() const +bool Client::capsOptimizationAllowed() const { - return d->httpFileUploadManager; + if (d->capsOptimization && d->active && d->serverInfoManager->features().hasCapsOptimize()) { + auto it = d->resourceList.find(d->resource); + return it != d->resourceList.end() && it->status().isAvailable(); + } + return false; } -Jingle::Manager *Client::jingleManager() const -{ - return d->jingleManager; -} +ServerInfoManager *Client::serverInfoManager() const { return d->serverInfoManager; } -bool Client::isActive() const -{ - return d->active; -} +ExternalServiceDiscovery *Client::externalServiceDiscovery() const { return d->externalServiceDiscovery; } + +StunDiscoManager *Client::stunDiscoManager() const { return d->stunDiscoManager; } + +HttpFileUploadManager *Client::httpFileUploadManager() const { return d->httpFileUploadManager; } -QString Client::groupChatPassword(const QString& host, const QString& room) const +Jingle::Manager *Client::jingleManager() const { return d->jingleManager; } + +bool Client::isActive() const { return d->active; } + +CarbonsManager *Client::carbonsManager() const { return d->carbonsman; } + +JT_PushMessage *Client::pushMessage() const { return d->pushMessage; } + +QString Client::groupChatPassword(const QString &host, const QString &room) const { Jid jid(room + "@" + host); - foreach(const GroupChat &i, d->groupChatList) { - if(i.j.compare(jid, false)) { + for (const GroupChat &i : std::as_const(d->groupChatList)) { + if (i.j.compare(jid, false)) { return i.password; } } @@ -329,9 +327,9 @@ QString Client::groupChatPassword(const QString& host, const QString& room) cons void Client::groupChatChangeNick(const QString &host, const QString &room, const QString &nick, const Status &_s) { Jid jid(room + "@" + host + "/" + nick); - for(QList::Iterator it = d->groupChatList.begin(); it != d->groupChatList.end(); it++) { + for (QList::Iterator it = d->groupChatList.begin(); it != d->groupChatList.end(); it++) { GroupChat &i = *it; - if(i.j.compare(jid, false)) { + if (i.j.compare(jid, false)) { i.j = jid; Status s = _s; @@ -346,37 +344,37 @@ void Client::groupChatChangeNick(const QString &host, const QString &room, const } } -bool Client::groupChatJoin(const QString &host, const QString &room, const QString &nick, const QString& password, int maxchars, int maxstanzas, int seconds, const QDateTime &since, const Status& _s) +bool Client::groupChatJoin(const QString &host, const QString &room, const QString &nick, const QString &password, + int maxchars, int maxstanzas, int seconds, const QDateTime &since, const Status &_s) { Jid jid(room + "@" + host + "/" + nick); - for(QList::Iterator it = d->groupChatList.begin(); it != d->groupChatList.end();) { + for (QList::Iterator it = d->groupChatList.begin(); it != d->groupChatList.end();) { GroupChat &i = *it; - if(i.j.compare(jid, false)) { + if (i.j.compare(jid, false)) { // if this room is shutting down, then free it up - if(i.status == GroupChat::Closing) + if (i.status == GroupChat::Closing) it = d->groupChatList.erase(it); else return false; - } - else + } else ++it; } debug(QString("Client: Joined: [%1]\n").arg(jid.full())); GroupChat i; - i.j = jid; - i.status = GroupChat::Connecting; + i.j = jid; + i.status = GroupChat::Connecting; i.password = password; d->groupChatList += i; JT_Presence *j = new JT_Presence(rootTask()); - Status s = _s; + Status s = _s; s.setMUC(); s.setMUCHistory(maxchars, maxstanzas, seconds, since); if (!password.isEmpty()) { s.setMUCPassword(password); } - j->pres(jid,s); + j->pres(jid, s); j->go(true); return true; @@ -384,16 +382,16 @@ bool Client::groupChatJoin(const QString &host, const QString &room, const QStri void Client::groupChatSetStatus(const QString &host, const QString &room, const Status &_s) { - Jid jid(room + "@" + host); + Jid jid(room + "@" + host); bool found = false; - foreach (const GroupChat &i, d->groupChatList) { - if(i.j.compare(jid, false)) { + for (const GroupChat &i : std::as_const(d->groupChatList)) { + if (i.j.compare(jid, false)) { found = true; - jid = i.j; + jid = i.j; break; } } - if(!found) + if (!found) return; Status s = _s; @@ -407,17 +405,17 @@ void Client::groupChatSetStatus(const QString &host, const QString &room, const void Client::groupChatLeave(const QString &host, const QString &room, const QString &statusStr) { Jid jid(room + "@" + host); - for(QList::Iterator it = d->groupChatList.begin(); it != d->groupChatList.end(); it++) { + for (QList::Iterator it = d->groupChatList.begin(); it != d->groupChatList.end(); it++) { GroupChat &i = *it; - if(!i.j.compare(jid, false)) + if (!i.j.compare(jid, false)) continue; i.status = GroupChat::Closing; debug(QString("Client: Leaving: [%1]\n").arg(i.j.full())); JT_Presence *j = new JT_Presence(rootTask()); - Status s; + Status s; s.setIsAvailable(false); s.setStatus(statusStr); j->pres(i.j, s); @@ -428,12 +426,12 @@ void Client::groupChatLeave(const QString &host, const QString &room, const QStr void Client::groupChatLeaveAll(const QString &statusStr) { if (d->stream && d->active) { - for(QList::Iterator it = d->groupChatList.begin(); it != d->groupChatList.end(); it++) { + for (QList::Iterator it = d->groupChatList.begin(); it != d->groupChatList.end(); it++) { GroupChat &i = *it; - i.status = GroupChat::Closing; + i.status = GroupChat::Closing; JT_Presence *j = new JT_Presence(rootTask()); - Status s; + Status s; s.setIsAvailable(false); s.setStatus(statusStr); j->pres(i.j, s); @@ -445,7 +443,7 @@ void Client::groupChatLeaveAll(const QString &statusStr) QString Client::groupChatNick(const QString &host, const QString &room) const { Jid jid(room + "@" + host); - foreach (const GroupChat &gc, d->groupChatList) { + for (const GroupChat &gc : std::as_const(d->groupChatList)) { if (gc.j.compare(jid, false)) { return gc.j.resource(); } @@ -468,22 +466,22 @@ QString Client::groupChatNick(const QString &host, const QString &room) const // TODO: fast close void Client::close(bool) { - //fprintf(stderr, "\tClient::close\n"); - //fflush(stderr); + // fprintf(stderr, "\tClient::close\n"); + // fflush(stderr); - if(d->stream) { + if (d->stream) { d->stream->disconnect(this); d->stream->close(); - d->stream = 0; + d->stream = nullptr; } - disconnected(); - cleanup(); + emit disconnected(); + cleanup(); // TODO wait till stream writes all data to the socket } void Client::cleanup() { d->active = false; - //d->authed = false; + // d->authed = false; d->groupChatList.clear(); } @@ -504,14 +502,14 @@ void Client::streamHandshaken() void Client::streamError(int) { - //StreamError e = err; - //error(e); + // StreamError e = err; + // error(e); - //if(!e.isWarning()) { - disconnected(); - cleanup(); + // if(!e.isWarning()) { + emit disconnected(); + cleanup(); //} -} +} // namespace XMPP /*void Client::streamSSLCertificateReady(const QSSLCert &cert) { @@ -523,56 +521,19 @@ void Client::streamCloseFinished() closeFinished(); }*/ -static QDomElement oldStyleNS(const QDomElement &e) -{ - // find closest parent with a namespace - QDomNode par = e.parentNode(); - while(!par.isNull() && par.namespaceURI().isNull()) - par = par.parentNode(); - bool noShowNS = false; - if(!par.isNull() && par.namespaceURI() == e.namespaceURI()) - noShowNS = true; - - QDomElement i; - int x; - //if(noShowNS) - i = e.ownerDocument().createElement(e.tagName()); - //else - // i = e.ownerDocument().createElementNS(e.namespaceURI(), e.tagName()); - - // copy attributes - QDomNamedNodeMap al = e.attributes(); - for(x = 0; x < al.count(); ++x) - i.setAttributeNode(al.item(x).cloneNode().toAttr()); - - if(!noShowNS) - i.setAttribute("xmlns", e.namespaceURI()); - - // copy children - QDomNodeList nl = e.childNodes(); - for(x = 0; x < nl.count(); ++x) { - QDomNode n = nl.item(x); - if(n.isElement()) - i.appendChild(oldStyleNS(n.toElement())); - else - i.appendChild(n.cloneNode()); - } - return i; -} - void Client::streamReadyRead() { - //fprintf(stderr, "\tClientStream::streamReadyRead\n"); - //fflush(stderr); + // fprintf(stderr, "\tClientStream::streamReadyRead\n"); + // fflush(stderr); - while(d->stream && d->stream->stanzaAvailable()) { + while (d->stream && d->stream->stanzaAvailable()) { Stanza s = d->stream->read(); QString out = s.toString(); debug(QString("Client: incoming: [\n%1]\n").arg(out)); emit xmlIncoming(out); - QDomElement x = oldStyleNS(s.element()); + QDomElement x = s.element(); // oldStyleNS(s.element()); distribute(x); } } @@ -580,7 +541,7 @@ void Client::streamReadyRead() void Client::streamIncomingXml(const QString &s) { QString str = s; - if(str.at(str.length()-1) != '\n') + if (str.at(str.length() - 1) != '\n') str += '\n'; emit xmlIncoming(str); } @@ -588,7 +549,7 @@ void Client::streamIncomingXml(const QString &s) void Client::streamOutgoingXml(const QString &s) { QString str = s; - if(str.at(str.length()-1) != '\n') + if (str.at(str.length() - 1) != '\n') str += '\n'; emit xmlOutgoing(str); } @@ -596,7 +557,7 @@ void Client::streamOutgoingXml(const QString &s) void Client::parseUnhandledStreamFeatures() { QList nl = d->stream->unhandledFeatures(); - foreach (const QDomElement &e, nl) { + for (const QDomElement &e : nl) { if (e.localName() == "c" && e.namespaceURI() == NS_CAPS) { d->serverCaps = CapsSpec::fromXml(e); if (d->capsman->isEnabled()) { @@ -606,41 +567,34 @@ void Client::parseUnhandledStreamFeatures() } } -void Client::debug(const QString &str) -{ - emit debugText(str); -} +void Client::debug(const QString &str) { emit debugText(str); } QString Client::genUniqueId() { - QString s; - s.sprintf("a%x", d->id_seed); - d->id_seed += 0x10; - return s; +#if QT_VERSION >= QT_VERSION_CHECK(5, 11, 0) + return QUuid::createUuid().toString(QUuid::WithoutBraces); +#else + auto s = QUuid::createUuid().toString(); + return s.mid(1, s.size() - 2); +#endif } -Task *Client::rootTask() -{ - return d->root; -} +Task *Client::rootTask() { return d->root; } -QDomDocument *Client::doc() const -{ - return &d->doc; -} +QDomDocument *Client::doc() const { return &d->doc; } void Client::distribute(const QDomElement &x) { static QString fromAttr(QStringLiteral("from")); - if(x.hasAttribute(fromAttr)) { + if (x.hasAttribute(fromAttr)) { Jid j(x.attribute(fromAttr)); - if(!j.isValid()) { + if (!j.isValid()) { debug("Client: bad 'from' JID\n"); return; } } - if(!rootTask()->take(x) && (x.attribute("type") == "get" || x.attribute("type") == "set") ) { + if (!rootTask()->take(x) && (x.attribute("type") == "get" || x.attribute("type") == "set")) { debug("Client: Unrecognized IQ.\n"); // Create reply element @@ -653,11 +607,11 @@ void Client::distribute(const QDomElement &x) // Add error QDomElement error = doc()->createElement("error"); - error.setAttribute("type","cancel"); + error.setAttribute("type", "cancel"); reply.appendChild(error); - QDomElement error_type = doc()->createElement("feature-not-implemented"); - error_type.setAttribute("xmlns","urn:ietf:params:xml:ns:xmpp-stanzas"); + QDomElement error_type = doc()->createElementNS(QLatin1String("urn:ietf:params:xml:ns:xmpp-stanzas"), + QLatin1String("feature-not-implemented")); error.appendChild(error_type); send(reply); @@ -666,109 +620,84 @@ void Client::distribute(const QDomElement &x) void Client::send(const QDomElement &x) { - if(!d->stream) + if (!d->stream) return; - //QString out; - //QTextStream ts(&out, IO_WriteOnly); - //x.save(ts, 0); + // QString out; + // QTextStream ts(&out, IO_WriteOnly); + // x.save(ts, 0); - //QString out = Stream::xmlToString(x); - //debug(QString("Client: outgoing: [\n%1]\n").arg(out)); - //xmlOutgoing(out); + // QString out = Stream::xmlToString(x); + // debug(QString("Client: outgoing: [\n%1]\n").arg(out)); + // xmlOutgoing(out); QDomElement e = addCorrectNS(x); - Stanza s = d->stream->createStanza(e); - if(s.isNull()) { // e's namespace is not "jabber:client" or e.tagName is not in (message,presence,iq) - //printf("bad stanza??\n"); + Stanza s = d->stream->createStanza(e); + if (s.isNull()) { // e's namespace is not "jabber:client" or e.tagName is not in (message,presence,iq) + // printf("bad stanza??\n"); return; } emit stanzaElementOutgoing(e); // signal handler may change the node (TODO weird design?) - if (e.isNull()) { // so it was changed by signal above + if (e.isNull()) { // so it was changed by signal above return; } QString out = s.toString(); - //qWarning() << "Out: " << out; + // qWarning() << "Out: " << out; debug(QString("Client: outgoing: [\n%1]\n").arg(out)); emit xmlOutgoing(out); - //printf("x[%s] x2[%s] s[%s]\n", Stream::xmlToString(x).toLatin1(), Stream::xmlToString(e).toLatin1(), s.toString().toLatin1()); + // printf("x[%s] x2[%s] s[%s]\n", Stream::xmlToString(x).toLatin1(), Stream::xmlToString(e).toLatin1(), + // s.toString().toLatin1()); d->stream->write(s); } void Client::send(const QString &str) { - if(!d->stream) + if (!d->stream) return; debug(QString("Client: outgoing: [\n%1]\n").arg(str)); emit xmlOutgoing(str); - static_cast(d->stream)->writeDirect(str); + static_cast(d->stream)->writeDirect(str); } /* drops any pending outgoing xml elements */ void Client::clearSendQueue() { - d->stream->clearSendQueue(); + if (d->stream) + d->stream->clearSendQueue(); } -bool Client::hasStream() const -{ - return !!d->stream; -} +bool Client::hasStream() const { return !!d->stream; } -Stream & Client::stream() -{ - return *(d->stream.data()); -} +Stream &Client::stream() { return *(d->stream.data()); } -QString Client::streamBaseNS() const -{ - return d->stream->baseNS(); -} +QString Client::streamBaseNS() const { return d->stream->baseNS(); } -const LiveRoster & Client::roster() const -{ - return d->roster; -} +const LiveRoster &Client::roster() const { return d->roster; } -const ResourceList & Client::resourceList() const -{ - return d->resourceList; -} +const ResourceList &Client::resourceList() const { return d->resourceList; } bool Client::isSessionRequired() const { return d->stream && !d->stream->old() && d->stream->streamFeatures().session_required; } -QString Client::host() const -{ - return d->host; -} +QString Client::host() const { return d->host; } -QString Client::user() const -{ - return d->user; -} +QString Client::user() const { return d->user; } -QString Client::pass() const -{ - return d->pass; -} +QString Client::pass() const { return d->pass; } -QString Client::resource() const -{ - return d->resource; -} +QString Client::resource() const { return d->resource; } Jid Client::jid() const { QString s; - if(!d->user.isEmpty()) + if (!d->user.isEmpty()) s += d->user + '@'; s += d->host; - if(!d->resource.isEmpty()) { + if (!d->resource.isEmpty()) { s += '/'; s += d->resource; } @@ -776,89 +705,78 @@ Jid Client::jid() const return Jid(s); } -void Client::setNetworkAccessManager(QNetworkAccessManager *qnam) -{ - d->qnam = qnam; -} +void Client::setNetworkAccessManager(QNetworkAccessManager *qnam) { d->qnam = qnam; } -QNetworkAccessManager *Client::networkAccessManager() const -{ - return d->qnam; -} +QNetworkAccessManager *Client::networkAccessManager() const { return d->qnam; } -void Client::ppSubscription(const Jid &j, const QString &s, const QString& n) -{ - emit subscription(j, s, n); -} +void Client::ppSubscription(const Jid &j, const QString &s, const QString &n) { emit subscription(j, s, n); } void Client::ppPresence(const Jid &j, const Status &s) { - if(s.isAvailable()) + if (s.isAvailable()) debug(QString("Client: %1 is available.\n").arg(j.full())); else debug(QString("Client: %1 is unavailable.\n").arg(j.full())); - for(QList::Iterator it = d->groupChatList.begin(); it != d->groupChatList.end(); it++) { + for (QList::Iterator it = d->groupChatList.begin(); it != d->groupChatList.end(); it++) { GroupChat &i = *it; - if(i.j.compare(j, false)) { - bool us = (i.j.resource() == j.resource() || j.resource().isEmpty()) ? true: false; - - debug(QString("for groupchat i=[%1] pres=[%2], [us=%3].\n").arg(i.j.full()).arg(j.full()).arg(us)); - switch(i.status) { - case GroupChat::Connecting: - if(us && s.hasError()) { - Jid j = i.j; - d->groupChatList.erase(it); - emit groupChatError(j, s.errorCode(), s.errorString()); + if (i.j.compare(j, false)) { + bool us = i.j.resource() == j.resource() || j.resource().isEmpty(); + + debug(QString("for groupchat i=[%1] pres=[%2], [us=%3].\n").arg(i.j.full(), j.full()).arg(us)); + switch (i.status) { + case GroupChat::Connecting: + if (us && s.hasError()) { + Jid j = i.j; + d->groupChatList.erase(it); + emit groupChatError(j, s.errorCode(), s.errorString()); + } else { + // don't signal success unless it is a non-error presence + if (!s.hasError()) { + i.status = GroupChat::Connected; + emit groupChatJoined(i.j); } - else { - // don't signal success unless it is a non-error presence - if(!s.hasError()) { - i.status = GroupChat::Connected; - emit groupChatJoined(i.j); - } - emit groupChatPresence(j, s); - } - break; - case GroupChat::Connected: emit groupChatPresence(j, s); - break; - case GroupChat::Closing: - if(us && !s.isAvailable()) { - Jid j = i.j; - d->groupChatList.erase(it); - emit groupChatLeft(j); - } - break; - default: - break; + } + break; + case GroupChat::Connected: + emit groupChatPresence(j, s); + break; + case GroupChat::Closing: + if (us && !s.isAvailable()) { + Jid j = i.j; + d->groupChatList.erase(it); + emit groupChatLeft(j); + } + break; + default: + break; } return; } } - if(s.hasError()) { + if (s.hasError()) { emit presenceError(j, s.errorCode(), s.errorString()); return; } // is it me? - if(j.compare(jid(), false)) { + if (j.compare(jid(), false)) { updateSelfPresence(j, s); - } - else { + } else { // update all relavent roster entries - for(LiveRoster::Iterator it = d->roster.begin(); it != d->roster.end(); ++it) { + for (LiveRoster::Iterator it = d->roster.begin(); it != d->roster.end(); ++it) { LiveRosterItem &i = *it; - if(!i.jid().compare(j, false)) + if (!i.jid().compare(j, false)) continue; // roster item has its own resource? - if(!i.jid().resource().isEmpty()) { - if(i.jid().resource() != j.resource()) + if (!i.jid().resource().isEmpty()) { + if (i.jid().resource() != j.resource()) continue; } @@ -869,12 +787,12 @@ void Client::ppPresence(const Jid &j, const Status &s) void Client::updateSelfPresence(const Jid &j, const Status &s) { - ResourceList::Iterator rit = d->resourceList.find(j.resource()); - bool found = (rit == d->resourceList.end()) ? false: true; + ResourceList::Iterator rit = d->resourceList.find(j.resource()); + bool found = rit != d->resourceList.end(); // unavailable? remove the resource - if(!s.isAvailable()) { - if(found) { + if (!s.isAvailable()) { + if (found) { debug(QString("Client: Removing self resource: name=[%1]\n").arg(j.resource())); (*rit).setStatus(s); emit resourceUnavailable(j, *rit); @@ -884,12 +802,11 @@ void Client::updateSelfPresence(const Jid &j, const Status &s) // available? add/update the resource else { Resource r; - if(!found) { + if (!found) { r = Resource(j.resource(), s); d->resourceList += r; debug(QString("Client: Adding self resource: name=[%1]\n").arg(j.resource())); - } - else { + } else { (*rit).setStatus(s); r = *rit; debug(QString("Client: Updating self resource: name=[%1]\n").arg(j.resource())); @@ -901,19 +818,18 @@ void Client::updateSelfPresence(const Jid &j, const Status &s) void Client::updatePresence(LiveRosterItem *i, const Jid &j, const Status &s) { - ResourceList::Iterator rit = i->resourceList().find(j.resource()); - bool found = (rit == i->resourceList().end()) ? false: true; + ResourceList::Iterator rit = i->resourceList().find(j.resource()); + bool found = rit != i->resourceList().end(); // unavailable? remove the resource - if(!s.isAvailable()) { - if(found) { + if (!s.isAvailable()) { + if (found) { (*rit).setStatus(s); - debug(QString("Client: Removing resource from [%1]: name=[%2]\n").arg(i->jid().full()).arg(j.resource())); + debug(QString("Client: Removing resource from [%1]: name=[%2]\n").arg(i->jid().full(), j.resource())); emit resourceUnavailable(j, *rit); i->resourceList().erase(rit); i->setLastUnavailableStatus(s); - } - else { + } else { // create the resource just for the purpose of emit Resource r = Resource(j.resource(), s); i->resourceList() += r; @@ -926,15 +842,14 @@ void Client::updatePresence(LiveRosterItem *i, const Jid &j, const Status &s) // available? add/update the resource else { Resource r; - if(!found) { + if (!found) { r = Resource(j.resource(), s); i->resourceList() += r; - debug(QString("Client: Adding resource to [%1]: name=[%2]\n").arg(i->jid().full()).arg(j.resource())); - } - else { + debug(QString("Client: Adding resource to [%1]: name=[%2]\n").arg(i->jid().full(), j.resource())); + } else { (*rit).setStatus(s); r = *rit; - debug(QString("Client: Updating resource to [%1]: name=[%2]\n").arg(i->jid().full()).arg(j.resource())); + debug(QString("Client: Updating resource to [%1]: name=[%2]\n").arg(i->jid().full(), j.resource())); } emit resourceAvailable(j, r); @@ -946,7 +861,8 @@ void Client::pmMessage(const Message &m) debug(QString("Client: Message from %1\n").arg(m.from().full())); // bits of binary. we can't do this in Message, since it knows nothing about Client - foreach (const BoBData &b, m.bobDataList()) { + auto const &dataList = m.bobDataList(); + for (const BoBData &b : dataList) { d->bobman->append(b); } @@ -954,35 +870,51 @@ void Client::pmMessage(const Message &m) d->ibbman->takeIncomingData(m.from(), m.id(), m.ibbData(), Stanza::Message); } - if(m.type() == "groupchat") { - for(QList::Iterator it = d->groupChatList.begin(); it != d->groupChatList.end(); it++) { + if (m.type() == "groupchat") { + for (QList::Iterator it = d->groupChatList.begin(); it != d->groupChatList.end(); it++) { const GroupChat &i = *it; - if(!i.j.compare(m.from(), false)) + if (!i.j.compare(m.from(), false)) continue; - if(i.status == GroupChat::Connected) - messageReceived(m); + if (i.status == GroupChat::Connected) + emit messageReceived(m); } - } - else - messageReceived(m); + } else + emit messageReceived(m); } -void Client::prRoster(const Roster &r) -{ - importRoster(r); -} +void Client::prRoster(const Roster &r) { importRoster(r); } -void Client::rosterRequest() +void Client::rosterRequest(bool withGroupsDelimiter) { - if(!d->active) + if (!d->active) return; JT_Roster *r = new JT_Roster(rootTask()); - connect(r, SIGNAL(finished()), SLOT(slotRosterRequestFinished())); - r->get(); - d->roster.flagAllForDelete(); // mod_groups patch + if (withGroupsDelimiter) { + connect(r, &JT_Roster::finished, this, [this, r]() mutable { + if (r->success()) { + d->roster.setGroupsDelimiter(r->groupsDelimiter()); + emit rosterGroupsDelimiterRequestFinished(r->groupsDelimiter()); + } + + r = new JT_Roster(rootTask()); + connect(r, SIGNAL(finished()), SLOT(slotRosterRequestFinished())); + r->get(); + d->roster.flagAllForDelete(); // mod_groups patch + r->go(true); + }); + r->getGroupsDelimiter(); + // WORKAROUND: Some bad servers (Facebook for example) don't respond + // on groups delimiter request. Wait timeout and go ahead. + r->setTimeout(GROUPS_DELIMITER_TIMEOUT); + } else { + connect(r, SIGNAL(finished()), SLOT(slotRosterRequestFinished())); + r->get(); + d->roster.flagAllForDelete(); // mod_groups patch + } + r->go(true); } @@ -990,24 +922,22 @@ void Client::slotRosterRequestFinished() { JT_Roster *r = static_cast(sender()); // on success, let's take it - if(r->success()) { - //d->roster.flagAllForDelete(); // mod_groups patch + if (r->success()) { + // d->roster.flagAllForDelete(); // mod_groups patch importRoster(r->roster()); - for(LiveRoster::Iterator it = d->roster.begin(); it != d->roster.end();) { + for (LiveRoster::Iterator it = d->roster.begin(); it != d->roster.end();) { LiveRosterItem &i = *it; - if(i.flagForDelete()) { + if (i.flagForDelete()) { emit rosterItemRemoved(i); it = d->roster.erase(it); - } - else + } else ++it; } - } - else { + } else { // don't report a disconnect. Client::error() will do that. - if(r->statusCode() == Task::ErrDisc) + if (r->statusCode() == Task::ErrDisc) return; } @@ -1018,8 +948,8 @@ void Client::slotRosterRequestFinished() void Client::importRoster(const Roster &r) { emit beginImportRoster(); - for(Roster::ConstIterator it = r.begin(); it != r.end(); ++it) { - importRosterItem(*it); + for (const auto &item : r) { + importRosterItem(item); } emit endImportRoster(); } @@ -1027,30 +957,34 @@ void Client::importRoster(const Roster &r) void Client::importRosterItem(const RosterItem &item) { QString substr; - switch(item.subscription().type()) { - case Subscription::Both: - substr = "<-->"; break; - case Subscription::From: - substr = " ->"; break; - case Subscription::To: - substr = "<- "; break; - case Subscription::Remove: - substr = "xxxx"; break; - case Subscription::None: - default: - substr = "----"; break; + switch (item.subscription().type()) { + case Subscription::Both: + substr = "<-->"; + break; + case Subscription::From: + substr = " ->"; + break; + case Subscription::To: + substr = "<- "; + break; + case Subscription::Remove: + substr = "xxxx"; + break; + case Subscription::None: + default: + substr = "----"; + break; } - QString dstr, str; - str.sprintf(" %s %-32s", qPrintable(substr), qPrintable(item.jid().full())); - if(!item.name().isEmpty()) + QString dstr, str = QString::asprintf(" %s %-32s", qPrintable(substr), qPrintable(item.jid().full())); + if (!item.name().isEmpty()) str += QString(" [") + item.name() + "]"; str += '\n'; // Remove - if(item.subscription().type() == Subscription::Remove) { + if (item.subscription().type() == Subscription::Remove) { LiveRoster::Iterator it = d->roster.find(item.jid()); - if(it != d->roster.end()) { + if (it != d->roster.end()) { emit rosterItemRemoved(*it); d->roster.erase(it); } @@ -1059,14 +993,13 @@ void Client::importRosterItem(const RosterItem &item) // Add/Update else { LiveRoster::Iterator it = d->roster.find(item.jid()); - if(it != d->roster.end()) { + if (it != d->roster.end()) { LiveRosterItem &i = *it; i.setFlagForDelete(false); i.setRosterItem(item); emit rosterItemUpdated(i); dstr = "Client: (Updated) "; - } - else { + } else { LiveRosterItem i(item); d->roster += i; @@ -1085,7 +1018,7 @@ void Client::sendMessage(Message &m) j->go(true); } -void Client::sendSubscription(const Jid &jid, const QString &type, const QString& nick) +void Client::sendSubscription(const Jid &jid, const QString &type, const QString &nick) { JT_Presence *j = new JT_Presence(rootTask()); j->sub(jid, type, nick); @@ -1106,30 +1039,18 @@ void Client::setPresence(const Status &s) // update our resourceList ppPresence(jid(), s); - //ResourceList::Iterator rit = d->resourceList.find(resource()); - //Resource &r = *rit; - //r.setStatus(s); + // ResourceList::Iterator rit = d->resourceList.find(resource()); + // Resource &r = *rit; + // r.setStatus(s); } -QString Client::OSName() const -{ - return d->osName; -} +QString Client::OSName() const { return d->osName; } -QString Client::OSVersion() const -{ - return d->osVersion; -} +QString Client::OSVersion() const { return d->osVersion; } -QString Client::timeZone() const -{ - return d->tzname; -} +QString Client::timeZone() const { return d->tzname; } -int Client::timeZoneOffset() const -{ - return d->tzoffset; -} +int Client::timeZoneOffset() const { return d->tzoffset; } /** \brief Returns true if Client is using old, manual time zone conversions. @@ -1141,77 +1062,58 @@ int Client::timeZoneOffset() const However, if you call setTimeZone(), Client instance switches to old mode and uses given time zone offset for all calculations. */ -bool Client::manualTimeZoneOffset() const -{ - return d->useTzoffset; -} +bool Client::manualTimeZoneOffset() const { return d->useTzoffset; } -QString Client::clientName() const -{ - return d->clientName; -} +QString Client::clientName() const { return d->clientName; } -QString Client::clientVersion() const -{ - return d->clientVersion; -} +QString Client::clientVersion() const { return d->clientVersion; } -CapsSpec Client::caps() const -{ - return d->caps; -} +CapsSpec Client::caps() const { return d->caps; } -CapsSpec Client::serverCaps() const -{ - return d->serverCaps; -} +CapsSpec Client::serverCaps() const { return d->serverCaps; } void Client::setOSName(const QString &name) { + if (d->osName != name) + d->caps.resetVersion(); d->osName = name; } void Client::setOSVersion(const QString &version) { + if (d->osVersion != version) + d->caps.resetVersion(); d->osVersion = version; } void Client::setTimeZone(const QString &name, int offset) { - d->tzname = name; - d->tzoffset = offset; + d->tzname = name; + d->tzoffset = offset; d->useTzoffset = true; } void Client::setClientName(const QString &s) { + if (d->clientName != s) + d->caps.resetVersion(); d->clientName = s; } void Client::setClientVersion(const QString &s) { + if (d->clientVersion != s) + d->caps.resetVersion(); d->clientVersion = s; } -void Client::setCaps(const CapsSpec &s) -{ - d->caps = s; -} +void Client::setCaps(const CapsSpec &s) { d->caps = s; } -void Client::setEncryptionHandler(EncryptionHandler *encryptionHandler) -{ - d->encryptionHandler = encryptionHandler; -} +void Client::setEncryptionHandler(EncryptionHandler *encryptionHandler) { d->encryptionHandler = encryptionHandler; } -EncryptionHandler *Client::encryptionHandler() const -{ - return d->encryptionHandler; -} +EncryptionHandler *Client::encryptionHandler() const { return d->encryptionHandler; } -DiscoItem::Identity Client::identity() const -{ - return d->identity; -} +DiscoItem::Identity Client::identity() const { return d->identity; } void Client::setIdentity(const DiscoItem::Identity &identity) { @@ -1221,7 +1123,7 @@ void Client::setIdentity(const DiscoItem::Identity &identity) d->identity = identity; } -void Client::setFeatures(const Features& f) +void Client::setFeatures(const Features &f) { if (!(d->features == f)) { d->caps.resetVersion(); @@ -1229,10 +1131,7 @@ void Client::setFeatures(const Features& f) d->features = f; } -const Features& Client::features() const -{ - return d->features; -} +const Features &Client::features() const { return d->features; } DiscoItem Client::makeDiscoResult(const QString &node) const { @@ -1241,7 +1140,7 @@ DiscoItem Client::makeDiscoResult(const QString &node) const DiscoItem::Identity id = identity(); if (id.category.isEmpty() || id.type.isEmpty()) { id.category = "client"; - id.type = "pc"; + id.type = "pc"; } item.setIdentities(id); @@ -1260,19 +1159,22 @@ DiscoItem Client::makeDiscoResult(const QString &node) const features.addFeature("urn:xmpp:time"); features.addFeature("urn:xmpp:message-correct:0"); features.addFeature("urn:xmpp:jingle:1"); - features.addFeature("urn:xmpp:jingle:transports:s5b:1"); - features.addFeature("urn:xmpp:jingle:apps:file-transfer:5"); // TODO: since it depends on UI it needs a way to be disabled + features.addFeature("urn:xmpp:extdisco:2"); + features += d->jingleManager->discoFeatures(); + + // TODO rather do foreach for all registered jingle apps and transports + // TODO: since it depends on UI it needs a way to be disabled + features.addFeature("urn:xmpp:jingle:apps:file-transfer:5"); Hash::populateFeatures(features); + features.addFeature(NS_CAPS); // Client-specific features - foreach (const QString & i, d->features.list()) { - features.addFeature(i); - } + features += d->features; item.setFeatures(features); // xep-0232 Software Information - XData si; + XData si; XData::FieldList si_fields; XData::Field si_type_field; @@ -1313,21 +1215,24 @@ DiscoItem Client::makeDiscoResult(const QString &node) const return item; } -void Client::s5b_incomingReady() -{ - handleIncoming(d->s5bman->takeIncoming()); -} +void Client::s5b_incomingReady() { handleIncoming(d->s5bman->takeIncoming()); } void Client::ibb_incomingReady() { - handleIncoming(d->ibbman->takeIncoming()); + auto c = d->ibbman->takeIncoming(); + if (!c) + return; + + if (d->jingleIBBManager && d->jingleIBBManager->handleIncoming(c)) + return; + handleIncoming(c); } void Client::handleIncoming(BSConnection *c) { - if(!c) + if (!c) return; - if(!d->ftman) { + if (!d->ftman) { c->close(); c->deleteLater(); return; @@ -1335,18 +1240,12 @@ void Client::handleIncoming(BSConnection *c) d->ftman->stream_incomingReady(c); } -void Client::handleSMAckResponse(int h) { - qDebug() << "handleSMAckResponse: h = " << h; -} +void Client::handleSMAckResponse(int h) { qDebug() << "handleSMAckResponse: h = " << h; } //--------------------------------------------------------------------------- // LiveRosterItem //--------------------------------------------------------------------------- -LiveRosterItem::LiveRosterItem(const Jid &jid) -:RosterItem(jid) -{ - setFlagForDelete(false); -} +LiveRosterItem::LiveRosterItem(const Jid &jid) : RosterItem(jid) { setFlagForDelete(false); } LiveRosterItem::LiveRosterItem(const RosterItem &i) { @@ -1354,9 +1253,7 @@ LiveRosterItem::LiveRosterItem(const RosterItem &i) setFlagForDelete(false); } -LiveRosterItem::~LiveRosterItem() -{ -} +LiveRosterItem::~LiveRosterItem() { } void LiveRosterItem::setRosterItem(const RosterItem &i) { @@ -1368,76 +1265,57 @@ void LiveRosterItem::setRosterItem(const RosterItem &i) setIsPush(i.isPush()); } -ResourceList & LiveRosterItem::resourceList() -{ - return v_resourceList; -} +ResourceList &LiveRosterItem::resourceList() { return v_resourceList; } -ResourceList::Iterator LiveRosterItem::priority() -{ - return v_resourceList.priority(); -} +ResourceList::Iterator LiveRosterItem::priority() { return v_resourceList.priority(); } -const ResourceList & LiveRosterItem::resourceList() const -{ - return v_resourceList; -} +const ResourceList &LiveRosterItem::resourceList() const { return v_resourceList; } -ResourceList::ConstIterator LiveRosterItem::priority() const -{ - return v_resourceList.priority(); -} +ResourceList::ConstIterator LiveRosterItem::priority() const { return v_resourceList.priority(); } -bool LiveRosterItem::isAvailable() const -{ - if(v_resourceList.count() > 0) - return true; - return false; -} +bool LiveRosterItem::isAvailable() const { return v_resourceList.count() > 0; } -const Status & LiveRosterItem::lastUnavailableStatus() const -{ - return v_lastUnavailableStatus; -} +const Status &LiveRosterItem::lastUnavailableStatus() const { return v_lastUnavailableStatus; } -bool LiveRosterItem::flagForDelete() const -{ - return v_flagForDelete; -} +bool LiveRosterItem::flagForDelete() const { return v_flagForDelete; } -void LiveRosterItem::setLastUnavailableStatus(const Status &s) -{ - v_lastUnavailableStatus = s; -} +void LiveRosterItem::setLastUnavailableStatus(const Status &s) { v_lastUnavailableStatus = s; } -void LiveRosterItem::setFlagForDelete(bool b) -{ - v_flagForDelete = b; -} +void LiveRosterItem::setFlagForDelete(bool b) { v_flagForDelete = b; } //--------------------------------------------------------------------------- // LiveRoster //--------------------------------------------------------------------------- -LiveRoster::LiveRoster() -:QList() +class LiveRoster::Private { +public: + QString groupsDelimiter; +}; + +LiveRoster::LiveRoster() : QList(), d(new LiveRoster::Private) { } +LiveRoster::LiveRoster(const LiveRoster &other) : QList(other), d(new LiveRoster::Private) { + d->groupsDelimiter = other.d->groupsDelimiter; } -LiveRoster::~LiveRoster() +LiveRoster::~LiveRoster() { delete d; } + +LiveRoster &LiveRoster::operator=(const LiveRoster &other) { + QList::operator=(other); + d->groupsDelimiter = other.d->groupsDelimiter; + return *this; } - void LiveRoster::flagAllForDelete() { - for(Iterator it = begin(); it != end(); ++it) + for (Iterator it = begin(); it != end(); ++it) (*it).setFlagForDelete(true); } LiveRoster::Iterator LiveRoster::find(const Jid &j, bool compareRes) { Iterator it; - for(it = begin(); it != end(); ++it) { - if((*it).jid().compare(j, compareRes)) + for (it = begin(); it != end(); ++it) { + if ((*it).jid().compare(j, compareRes)) break; } return it; @@ -1446,11 +1324,15 @@ LiveRoster::Iterator LiveRoster::find(const Jid &j, bool compareRes) LiveRoster::ConstIterator LiveRoster::find(const Jid &j, bool compareRes) const { ConstIterator it; - for(it = begin(); it != end(); ++it) { - if((*it).jid().compare(j, compareRes)) + for (it = begin(); it != end(); ++it) { + if ((*it).jid().compare(j, compareRes)) break; } return it; } +void LiveRoster::setGroupsDelimiter(const QString &groupsDelimiter) { d->groupsDelimiter = groupsDelimiter; } + +QString LiveRoster::groupsDelimiter() const { return d->groupsDelimiter; } + } diff --git a/src/xmpp/xmpp-im/filetransfer.cpp b/src/xmpp/xmpp-im/filetransfer.cpp index 78d0ae5e..714d6e22 100644 --- a/src/xmpp/xmpp-im/filetransfer.cpp +++ b/src/xmpp/xmpp-im/filetransfer.cpp @@ -19,14 +19,20 @@ #include "filetransfer.h" +#include "s5b.h" +#include "xmpp_client.h" +#include "xmpp_ibb.h" +#include "xmpp_stream.h" +#include "xmpp_xmlcommon.h" + +#include #include -#include #include -#include #include -#include "xmpp_xmlcommon.h" -#include "s5b.h" -#include "xmpp_ibb.h" +#include +#if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0) +#include +#endif #define SENDBUFSIZE 65536 @@ -37,8 +43,8 @@ using namespace XMPP; // Get an element's first child element static QDomElement firstChildElement(const QDomElement &e) { - for(QDomNode n = e.firstChild(); !n.isNull(); n = n.nextSibling()) { - if(n.isElement()) + for (QDomNode n = e.firstChild(); !n.isNull(); n = n.nextSibling()) { + if (n.isElement()) return n.toElement(); } return QDomElement(); @@ -47,46 +53,43 @@ static QDomElement firstChildElement(const QDomElement &e) //---------------------------------------------------------------------------- // FileTransfer //---------------------------------------------------------------------------- -class FileTransfer::Private -{ +class FileTransfer::Private { public: FileTransferManager *m; - JT_FT *ft; - Jid peer; - QString fname; - qlonglong size; - qlonglong sent; - QString desc; - bool rangeSupported; - qlonglong rangeOffset, rangeLength, length; - QString streamType; - Thumbnail thumbnail; - bool needStream; - QString id, iq_id; - BSConnection *c; - Jid proxy; - int state; - bool sender; + JT_FT *ft; + Jid peer; + QString fname; + qlonglong size; + qlonglong sent; + QString desc; + bool rangeSupported; + qlonglong rangeOffset, rangeLength, length; + QString streamType; + Thumbnail thumbnail; + bool needStream; + QString id, iq_id; + BSConnection *c; + Jid proxy; + int state; + bool sender; }; -FileTransfer::FileTransfer(FileTransferManager *m, QObject *parent) -:QObject(parent) +FileTransfer::FileTransfer(FileTransferManager *m, QObject *parent) : QObject(parent) { - d = new Private; - d->m = m; - d->ft = 0; - d->c = 0; + d = new Private; + d->m = m; + d->ft = nullptr; + d->c = nullptr; reset(); } -FileTransfer::FileTransfer(const FileTransfer& other) - : QObject(other.parent()) +FileTransfer::FileTransfer(const FileTransfer &other) : QObject(other.parent()) { - d = new Private; - *d = *other.d; - d->m = other.d->m; - d->ft = 0; - d->c = 0; + d = new Private; + *d = *other.d; + d->m = other.d->m; + d->ft = nullptr; + d->c = 0; reset(); if (d->m->isActive(&other)) @@ -99,46 +102,38 @@ FileTransfer::~FileTransfer() delete d; } -FileTransfer *FileTransfer::copy() const -{ - return new FileTransfer(*this); -} +FileTransfer *FileTransfer::copy() const { return new FileTransfer(*this); } void FileTransfer::reset() { d->m->unlink(this); delete d->ft; - d->ft = 0; + d->ft = nullptr; if (d->c) { d->c->disconnect(this); - d->c->manager()->deleteConnection(d->c, d->state == Active && !d->sender ? - 3000 : 0); - d->c = 0; + d->c->manager()->deleteConnection(d->c, d->state == Active && !d->sender ? 3000 : 0); + d->c = nullptr; } - d->state = Idle; + d->state = Idle; d->needStream = false; - d->sent = 0; - d->sender = false; + d->sent = 0; + d->sender = false; } -void FileTransfer::setProxy(const Jid &proxy) -{ - d->proxy = proxy; -} +void FileTransfer::setProxy(const Jid &proxy) { d->proxy = proxy; } -void FileTransfer::sendFile(const Jid &to, const QString &fname, qlonglong size, - const QString &desc, Thumbnail &thumb) +void FileTransfer::sendFile(const Jid &to, const QString &fname, qlonglong size, const QString &desc, Thumbnail &thumb) { - d->state = Requesting; - d->peer = to; - d->fname = fname; - d->size = size; - d->desc = desc; + d->state = Requesting; + d->peer = to; + d->fname = fname; + d->size = size; + d->desc = desc; d->sender = true; - d->id = d->m->link(this); + d->id = d->m->link(this); d->ft = new JT_FT(d->m->client()->rootTask()); connect(d->ft, SIGNAL(finished()), SLOT(ft_finished())); @@ -148,79 +143,54 @@ void FileTransfer::sendFile(const Jid &to, const QString &fname, qlonglong size, int FileTransfer::dataSizeNeeded() const { - int pending = d->c->bytesToWrite(); - if(pending >= SENDBUFSIZE) + quint64 pending = d->c->bytesToWrite(); + if (pending >= SENDBUFSIZE) return 0; qlonglong left = d->length - (d->sent + pending); - int size = SENDBUFSIZE - pending; - if((qlonglong)size > left) - size = (int)left; + int size = SENDBUFSIZE - pending; + if (qlonglong(size) > left) + size = int(left); return size; } void FileTransfer::writeFileData(const QByteArray &a) { - int pending = d->c->bytesToWrite(); - qlonglong left = d->length - (d->sent + pending); - if(left == 0) + quint64 pending = d->c->bytesToWrite(); + qlonglong left = d->length - (d->sent + pending); + if (left == 0) return; QByteArray block; - if((qlonglong)a.size() > left) { + if (qlonglong(a.size()) > left) { block = a; - block.resize((uint)left); - } - else + block.resize(uint(left)); + } else block = a; d->c->write(block); } -const Thumbnail &FileTransfer::thumbnail() const -{ - return d->thumbnail; -} +const Thumbnail &FileTransfer::thumbnail() const { return d->thumbnail; } -Jid FileTransfer::peer() const -{ - return d->peer; -} +Jid FileTransfer::peer() const { return d->peer; } -QString FileTransfer::fileName() const -{ - return d->fname; -} +QString FileTransfer::fileName() const { return d->fname; } -qlonglong FileTransfer::fileSize() const -{ - return d->size; -} +qlonglong FileTransfer::fileSize() const { return d->size; } -QString FileTransfer::description() const -{ - return d->desc; -} +QString FileTransfer::description() const { return d->desc; } -bool FileTransfer::rangeSupported() const -{ - return d->rangeSupported; -} +bool FileTransfer::rangeSupported() const { return d->rangeSupported; } -qlonglong FileTransfer::offset() const -{ - return d->rangeOffset; -} +qlonglong FileTransfer::offset() const { return d->rangeOffset; } -qlonglong FileTransfer::length() const -{ - return d->length; -} +qlonglong FileTransfer::length() const { return d->length; } void FileTransfer::accept(qlonglong offset, qlonglong length) { - d->state = Connecting; + d->state = Connecting; d->rangeOffset = offset; d->rangeLength = length; - if(length > 0) + if (length > 0) d->length = length; else d->length = d->size; @@ -229,38 +199,35 @@ void FileTransfer::accept(qlonglong offset, qlonglong length) void FileTransfer::close() { - if(d->state == Idle) + if (d->state == Idle) return; - if(d->state == WaitingForAccept) + if (d->state == WaitingForAccept) d->m->con_reject(this); - else if(d->state == Active) + else if (d->state == Active) d->c->close(); reset(); } -BSConnection *FileTransfer::bsConnection() const -{ - return d->c; -} +BSConnection *FileTransfer::bsConnection() const { return d->c; } // file transfer request accepted or error happened void FileTransfer::ft_finished() { JT_FT *ft = d->ft; - d->ft = 0; + d->ft = nullptr; - if(ft->success()) { - d->state = Connecting; + if (ft->success()) { + d->state = Connecting; d->rangeOffset = ft->rangeOffset(); - d->length = ft->rangeLength(); - if(d->length == 0) + d->length = ft->rangeLength(); + if (d->length == 0) d->length = d->size - d->rangeOffset; - d->streamType = ft->streamType(); + d->streamType = ft->streamType(); BytestreamManager *streamManager = d->m->streamManager(d->streamType); if (streamManager) { d->c = streamManager->createConnection(); - if (dynamic_cast(streamManager) && d->proxy.isValid()) { - ((S5BConnection*)(d->c))->setProxy(d->proxy); + if (dynamic_cast(streamManager) && d->proxy.isValid()) { + ((S5BConnection *)(d->c))->setProxy(d->proxy); } connect(d->c, SIGNAL(connected()), SLOT(stream_connected())); connect(d->c, SIGNAL(connectionClosed()), SLOT(stream_connectionClosed())); @@ -268,17 +235,15 @@ void FileTransfer::ft_finished() connect(d->c, SIGNAL(error(int)), SLOT(stream_error(int))); d->c->connectToJid(d->peer, d->id); - accepted(); - } - else { + emit accepted(); + } else { emit error(Err400); reset(); } - } - else { - if(ft->statusCode() == 403) + } else { + if (ft->statusCode() == 403) emit error(ErrReject); - else if(ft->statusCode() == 400) + else if (ft->statusCode() == 400) emit error(Err400); else emit error(ErrNeg); @@ -294,10 +259,10 @@ void FileTransfer::takeConnection(BSConnection *c) connect(d->c, SIGNAL(readyRead()), SLOT(stream_readyRead())); connect(d->c, SIGNAL(error(int)), SLOT(stream_error(int))); - S5BConnection *s5b = dynamic_cast(c); - if(s5b && d->proxy.isValid()) + S5BConnection *s5b = dynamic_cast(c); + if (s5b && d->proxy.isValid()) s5b->setProxy(d->proxy); - accepted(); + emit accepted(); QTimer::singleShot(0, this, SLOT(doAccept())); } @@ -317,20 +282,20 @@ void FileTransfer::stream_connectionClosed() void FileTransfer::stream_readyRead() { - QByteArray a = d->c->readAll(); - qlonglong need = d->length - d->sent; - if((qlonglong)a.size() > need) + QByteArray a = d->c->readAll(); + qlonglong need = d->length - d->sent; + if ((qlonglong)a.size() > need) a.resize((uint)need); d->sent += a.size(); -// if(d->sent == d->length) // we close it in stream_connectionClosed. at least for ibb -// reset(); // in other words we wait for another party to close the connection - readyRead(a); + // if(d->sent == d->length) // we close it in stream_connectionClosed. at least for ibb + // reset(); // in other words we wait for another party to close the connection + emit readyRead(a); } void FileTransfer::stream_bytesWritten(qint64 x) { d->sent += x; - if(d->sent == d->length) + if (d->sent == d->length) reset(); emit bytesWritten(x); } @@ -338,51 +303,46 @@ void FileTransfer::stream_bytesWritten(qint64 x) void FileTransfer::stream_error(int x) { reset(); - if(x == BSConnection::ErrRefused || x == BSConnection::ErrConnect) - error(ErrConnect); - else if(x == BSConnection::ErrProxy) - error(ErrProxy); + if (x == BSConnection::ErrRefused || x == BSConnection::ErrConnect) + emit error(ErrConnect); + else if (x == BSConnection::ErrProxy) + emit error(ErrProxy); else - error(ErrStream); + emit error(ErrStream); } void FileTransfer::man_waitForAccept(const FTRequest &req, const QString &streamType) { - d->state = WaitingForAccept; - d->peer = req.from; - d->id = req.id; - d->iq_id = req.iq_id; - d->fname = req.fname; - d->size = req.size; - d->desc = req.desc; + d->state = WaitingForAccept; + d->peer = req.from; + d->id = req.id; + d->iq_id = req.iq_id; + d->fname = req.fname; + d->size = req.size; + d->desc = req.desc; d->rangeSupported = req.rangeSupported; - d->streamType = streamType; - d->thumbnail = req.thumbnail; + d->streamType = streamType; + d->thumbnail = req.thumbnail; } -void FileTransfer::doAccept() -{ - d->c->accept(); -} +void FileTransfer::doAccept() { d->c->accept(); } //---------------------------------------------------------------------------- // FileTransferManager //---------------------------------------------------------------------------- -class FileTransferManager::Private -{ +class FileTransferManager::Private { public: - Client *client; - QList list, incoming; - QStringList streamPriority; - QHash streamMap; - QSet disabledStreamTypes; - JT_PushFT *pft; + Client *client; + QList list, incoming; + QStringList streamPriority; + QHash streamMap; + QSet disabledStreamTypes; + JT_PushFT *pft; }; -FileTransferManager::FileTransferManager(Client *client) -:QObject(client) +FileTransferManager::FileTransferManager(Client *client) : QObject(client) { - d = new Private; + d = new Private; d->client = client; if (client->s5bManager()) { d->streamPriority.append(S5BManager::ns()); @@ -406,10 +366,7 @@ FileTransferManager::~FileTransferManager() delete d; } -Client *FileTransferManager::client() const -{ - return d->client; -} +Client *FileTransferManager::client() const { return d->client; } FileTransfer *FileTransferManager::createTransfer() { @@ -419,8 +376,8 @@ FileTransfer *FileTransferManager::createTransfer() FileTransfer *FileTransferManager::takeIncoming() { - if(d->incoming.isEmpty()) - return 0; + if (d->incoming.isEmpty()) + return nullptr; FileTransfer *ft = d->incoming.takeFirst(); @@ -431,15 +388,14 @@ FileTransfer *FileTransferManager::takeIncoming() bool FileTransferManager::isActive(const FileTransfer *ft) const { - return d->list.contains(const_cast(ft)); + return d->list.contains(const_cast(ft)); } void FileTransferManager::setDisabled(const QString &ns, bool state) { if (state) { d->disabledStreamTypes.insert(ns); - } - else { + } else { d->disabledStreamTypes.remove(ns); } } @@ -447,8 +403,8 @@ void FileTransferManager::setDisabled(const QString &ns, bool state) void FileTransferManager::pft_incoming(const FTRequest &req) { QString streamType; - foreach(const QString& ns, d->streamPriority) { - if(req.streamTypes.contains(ns)) { + for (const QString &ns : std::as_const(d->streamPriority)) { + if (req.streamTypes.contains(ns)) { BytestreamManager *manager = streamManager(ns); if (manager && manager->isAcceptableSID(req.from, req.id)) { streamType = ns; @@ -457,22 +413,21 @@ void FileTransferManager::pft_incoming(const FTRequest &req) } } - if(streamType.isEmpty()) { - d->pft->respondError(req.from, req.iq_id, Stanza::Error::NotAcceptable, - "No valid stream types"); + if (streamType.isEmpty()) { + d->pft->respondError(req.from, req.iq_id, Stanza::Error::NotAcceptable, "No valid stream types"); return; } FileTransfer *ft = new FileTransfer(this); ft->man_waitForAccept(req, streamType); d->incoming.append(ft); - incomingReady(); + emit incomingReady(); } -BytestreamManager* FileTransferManager::streamManager(const QString &ns) const +BytestreamManager *FileTransferManager::streamManager(const QString &ns) const { if (d->disabledStreamTypes.contains(ns)) { - return 0; + return nullptr; } return d->streamMap.value(ns); } @@ -480,7 +435,7 @@ BytestreamManager* FileTransferManager::streamManager(const QString &ns) const QStringList FileTransferManager::streamPriority() const { QStringList ret; - foreach (const QString &ns, d->streamPriority) { + for (const QString &ns : std::as_const(d->streamPriority)) { if (!d->disabledStreamTypes.contains(ns)) { ret.append(ns); } @@ -490,8 +445,8 @@ QStringList FileTransferManager::streamPriority() const void FileTransferManager::stream_incomingReady(BSConnection *c) { - foreach(FileTransfer* ft, d->list) { - if(ft->d->needStream && ft->d->peer.compare(c->peer()) && ft->d->id == c->sid()) { + for (FileTransfer *ft : std::as_const(d->list)) { + if (ft->d->needStream && ft->d->peer.compare(c->peer()) && ft->d->id == c->sid()) { ft->takeConnection(c); return; } @@ -503,17 +458,21 @@ void FileTransferManager::stream_incomingReady(BSConnection *c) QString FileTransferManager::link(FileTransfer *ft) { QString id; - bool found; + bool found; do { found = false; +#if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0) + id = QString("ft_%1").arg(QRandomGenerator::global()->generate() & 0xffff, 4, 16, QChar('0')); +#else id = QString("ft_%1").arg(qrand() & 0xffff, 4, 16, QChar('0')); - foreach (FileTransfer* ft, d->list) { +#endif + for (FileTransfer *ft : std::as_const(d->list)) { if (ft->d->peer.compare(ft->d->peer) && ft->d->id == id) { found = true; break; } } - } while(found); + } while (found); d->list.append(ft); return id; } @@ -529,52 +488,38 @@ void FileTransferManager::con_reject(FileTransfer *ft) d->pft->respondError(ft->d->peer, ft->d->iq_id, Stanza::Error::Forbidden, "Declined"); } -void FileTransferManager::unlink(FileTransfer *ft) -{ - d->list.removeAll(ft); -} +void FileTransferManager::unlink(FileTransfer *ft) { d->list.removeAll(ft); } //---------------------------------------------------------------------------- // JT_FT //---------------------------------------------------------------------------- -class JT_FT::Private -{ +class JT_FT::Private { public: QDomElement iq; - Jid to; - qlonglong size, rangeOffset, rangeLength; - QString streamType; + Jid to; + qlonglong size, rangeOffset, rangeLength; + QString streamType; QStringList streamTypes; }; -JT_FT::JT_FT(Task *parent) -:Task(parent) -{ - d = new Private; -} +JT_FT::JT_FT(Task *parent) : Task(parent) { d = new Private; } -JT_FT::~JT_FT() -{ - delete d; -} +JT_FT::~JT_FT() { delete d; } -void JT_FT::request(const Jid &to, const QString &_id, const QString &fname, - qlonglong size, const QString &desc, +void JT_FT::request(const Jid &to, const QString &_id, const QString &fname, qlonglong size, const QString &desc, const QStringList &streamTypes, Thumbnail &thumb) { QDomElement iq; - d->to = to; - iq = createIQ(doc(), "set", to.full(), id()); - QDomElement si = doc()->createElement("si"); - si.setAttribute("xmlns", "http://jabber.org/protocol/si"); + d->to = to; + iq = createIQ(doc(), "set", to.full(), id()); + QDomElement si = doc()->createElementNS("http://jabber.org/protocol/si", "si"); si.setAttribute("id", _id); si.setAttribute("profile", "http://jabber.org/protocol/si/profile/file-transfer"); - QDomElement file = doc()->createElement("file"); - file.setAttribute("xmlns", "http://jabber.org/protocol/si/profile/file-transfer"); + QDomElement file = doc()->createElementNS("http://jabber.org/protocol/si/profile/file-transfer", "file"); file.setAttribute("name", fname); file.setAttribute("size", QString::number(size)); - if(!desc.isEmpty()) { + if (!desc.isEmpty()) { QDomElement de = doc()->createElement("desc"); de.appendChild(doc()->createTextNode(desc)); file.appendChild(de); @@ -584,25 +529,23 @@ void JT_FT::request(const Jid &to, const QString &_id, const QString &fname, if (!thumb.data.isEmpty()) { BoBData data = client()->bobManager()->append(thumb.data, thumb.mimeType); - thumb.uri = QLatin1String("cid:") + data.cid(); + thumb.uri = QLatin1String("cid:") + data.cid(); file.appendChild(thumb.toXml(doc())); } si.appendChild(file); - QDomElement feature = doc()->createElement("feature"); - feature.setAttribute("xmlns", "http://jabber.org/protocol/feature-neg"); - QDomElement x = doc()->createElement("x"); - x.setAttribute("xmlns", "jabber:x:data"); + QDomElement feature = doc()->createElementNS("http://jabber.org/protocol/feature-neg", "feature"); + QDomElement x = doc()->createElementNS("jabber:x:data", "x"); x.setAttribute("type", "form"); QDomElement field = doc()->createElement("field"); field.setAttribute("var", "stream-method"); field.setAttribute("type", "list-single"); - for(QStringList::ConstIterator it = streamTypes.begin(); it != streamTypes.end(); ++it) { + for (const auto &streamType : streamTypes) { QDomElement option = doc()->createElement("option"); - QDomElement value = doc()->createElement("value"); - value.appendChild(doc()->createTextNode(*it)); + QDomElement value = doc()->createElement("value"); + value.appendChild(doc()->createTextNode(streamType)); option.appendChild(value); field.appendChild(option); } @@ -614,64 +557,52 @@ void JT_FT::request(const Jid &to, const QString &_id, const QString &fname, iq.appendChild(si); d->streamTypes = streamTypes; - d->size = size; - d->iq = iq; + d->size = size; + d->iq = iq; } -qlonglong JT_FT::rangeOffset() const -{ - return d->rangeOffset; -} +qlonglong JT_FT::rangeOffset() const { return d->rangeOffset; } -qlonglong JT_FT::rangeLength() const -{ - return d->rangeLength; -} +qlonglong JT_FT::rangeLength() const { return d->rangeLength; } -QString JT_FT::streamType() const -{ - return d->streamType; -} +QString JT_FT::streamType() const { return d->streamType; } -void JT_FT::onGo() -{ - send(d->iq); -} +void JT_FT::onGo() { send(d->iq); } bool JT_FT::take(const QDomElement &x) { - if(!iqVerify(x, d->to, id())) + if (!iqVerify(x, d->to, id())) return false; - if(x.attribute("type") == "result") { + if (x.attribute("type") == "result") { QDomElement si = firstChildElement(x); - if(si.attribute("xmlns") != "http://jabber.org/protocol/si" || si.tagName() != "si") { + if (si.namespaceURI() != "http://jabber.org/protocol/si" || si.tagName() != "si") { setError(900, ""); return true; } - QString id = si.attribute("id"); + [[maybe_unused]] QString id = si.attribute("id"); qlonglong range_offset = 0; qlonglong range_length = 0; QDomElement file = si.elementsByTagName("file").item(0).toElement(); - if(!file.isNull()) { + if (!file.isNull()) { QDomElement range = file.elementsByTagName("range").item(0).toElement(); - if(!range.isNull()) { + if (!range.isNull()) { qlonglong x; - bool ok; - if(range.hasAttribute("offset")) { + bool ok; + if (range.hasAttribute("offset")) { x = range.attribute("offset").toLongLong(&ok); - if(!ok || x < 0) { + if (!ok || x < 0) { setError(900, ""); return true; } range_offset = x; } - if(range.hasAttribute("length")) { + if (range.hasAttribute("length")) { x = range.attribute("length").toLongLong(&ok); - if(!ok || x < 0) { + if (!ok || x < 0) { setError(900, ""); return true; } @@ -680,20 +611,20 @@ bool JT_FT::take(const QDomElement &x) } } - if(range_offset > d->size || (range_length > (d->size - range_offset))) { + if (range_offset > d->size || (range_length > (d->size - range_offset))) { setError(900, ""); return true; } - QString streamtype; + QString streamtype; QDomElement feature = si.elementsByTagName("feature").item(0).toElement(); - if(!feature.isNull() && feature.attribute("xmlns") == "http://jabber.org/protocol/feature-neg") { + if (!feature.isNull() && feature.namespaceURI() == "http://jabber.org/protocol/feature-neg") { QDomElement x = feature.elementsByTagName("x").item(0).toElement(); - if(!x.isNull() && x.attribute("type") == "submit") { + if (!x.isNull() && x.attribute("type") == "submit") { QDomElement field = x.elementsByTagName("field").item(0).toElement(); - if(!field.isNull() && field.attribute("var") == "stream-method") { + if (!field.isNull() && field.attribute("var") == "stream-method") { QDomElement value = field.elementsByTagName("value").item(0).toElement(); - if(!value.isNull()) + if (!value.isNull()) streamtype = value.text(); } } @@ -706,10 +637,9 @@ bool JT_FT::take(const QDomElement &x) d->rangeOffset = range_offset; d->rangeLength = range_length; - d->streamType = streamtype; + d->streamType = streamtype; setSuccess(); - } - else { + } else { setError(x); } @@ -719,37 +649,29 @@ bool JT_FT::take(const QDomElement &x) //---------------------------------------------------------------------------- // JT_PushFT //---------------------------------------------------------------------------- -JT_PushFT::JT_PushFT(Task *parent) -:Task(parent) -{ -} +JT_PushFT::JT_PushFT(Task *parent) : Task(parent) { } -JT_PushFT::~JT_PushFT() -{ -} +JT_PushFT::~JT_PushFT() { } -void JT_PushFT::respondSuccess(const Jid &to, const QString &id, qlonglong rangeOffset, qlonglong rangeLength, const QString &streamType) +void JT_PushFT::respondSuccess(const Jid &to, const QString &id, qlonglong rangeOffset, qlonglong rangeLength, + const QString &streamType) { QDomElement iq = createIQ(doc(), "result", to.full(), id); - QDomElement si = doc()->createElement("si"); - si.setAttribute("xmlns", "http://jabber.org/protocol/si"); + QDomElement si = doc()->createElementNS("http://jabber.org/protocol/si", "si"); - if(rangeOffset != 0 || rangeLength != 0) { - QDomElement file = doc()->createElement("file"); - file.setAttribute("xmlns", "http://jabber.org/protocol/si/profile/file-transfer"); + if (rangeOffset != 0 || rangeLength != 0) { + QDomElement file = doc()->createElementNS("http://jabber.org/protocol/si/profile/file-transfer", "file"); QDomElement range = doc()->createElement("range"); - if(rangeOffset > 0) + if (rangeOffset > 0) range.setAttribute("offset", QString::number(rangeOffset)); - if(rangeLength > 0) + if (rangeLength > 0) range.setAttribute("length", QString::number(rangeLength)); file.appendChild(range); si.appendChild(file); } - QDomElement feature = doc()->createElement("feature"); - feature.setAttribute("xmlns", "http://jabber.org/protocol/feature-neg"); - QDomElement x = doc()->createElement("x"); - x.setAttribute("xmlns", "jabber:x:data"); + QDomElement feature = doc()->createElementNS("http://jabber.org/protocol/feature-neg", "feature"); + QDomElement x = doc()->createElementNS("jabber:x:data", "x"); x.setAttribute("type", "submit"); QDomElement field = doc()->createElement("field"); @@ -766,10 +688,9 @@ void JT_PushFT::respondSuccess(const Jid &to, const QString &id, qlonglong range send(iq); } -void JT_PushFT::respondError(const Jid &to, const QString &id, - Stanza::Error::ErrorCond cond, const QString &str) +void JT_PushFT::respondError(const Jid &to, const QString &id, Stanza::Error::ErrorCond cond, const QString &str) { - QDomElement iq = createIQ(doc(), "error", to.full(), id); + QDomElement iq = createIQ(doc(), "error", to.full(), id); Stanza::Error error(Stanza::Error::Cancel, cond, str); iq.appendChild(error.toXml(*client()->doc(), client()->stream().baseNS())); send(iq); @@ -778,26 +699,26 @@ void JT_PushFT::respondError(const Jid &to, const QString &id, bool JT_PushFT::take(const QDomElement &e) { // must be an iq-set tag - if(e.tagName() != "iq") + if (e.tagName() != "iq") return false; - if(e.attribute("type") != "set") + if (e.attribute("type") != "set") return false; QDomElement si = firstChildElement(e); - if(si.attribute("xmlns") != "http://jabber.org/protocol/si" || si.tagName() != "si") + if (si.namespaceURI() != "http://jabber.org/protocol/si" || si.tagName() != "si") return false; - if(si.attribute("profile") != "http://jabber.org/protocol/si/profile/file-transfer") + if (si.attribute("profile") != "http://jabber.org/protocol/si/profile/file-transfer") return false; - Jid from(e.attribute("from")); + Jid from(e.attribute("from")); QString id = si.attribute("id"); QDomElement file = si.elementsByTagName("file").item(0).toElement(); - if(file.isNull()) + if (file.isNull()) return true; QString fname = file.attribute("name"); - if(fname.isEmpty()) { + if (fname.isEmpty()) { respondError(from, id, Stanza::Error::BadRequest, "Bad file name"); return true; } @@ -808,35 +729,36 @@ bool JT_PushFT::take(const QDomElement &e) fname = fi.fileName(); } - bool ok; + bool ok; qlonglong size = file.attribute("size").toLongLong(&ok); - if(!ok || size < 0) { + if (!ok || size < 0) { respondError(from, id, Stanza::Error::BadRequest, "Bad file size"); return true; } - QString desc; + QString desc; QDomElement de = file.elementsByTagName("desc").item(0).toElement(); - if(!de.isNull()) + if (!de.isNull()) desc = de.text(); - bool rangeSupported = false; - QDomElement range = file.elementsByTagName("range").item(0).toElement(); - if(!range.isNull()) + bool rangeSupported = false; + QDomElement range = file.elementsByTagName("range").item(0).toElement(); + if (!range.isNull()) rangeSupported = true; QStringList streamTypes; QDomElement feature = si.elementsByTagName("feature").item(0).toElement(); - if(!feature.isNull() && feature.attribute("xmlns") == "http://jabber.org/protocol/feature-neg") { + if (!feature.isNull() && feature.namespaceURI() == "http://jabber.org/protocol/feature-neg") { QDomElement x = feature.elementsByTagName("x").item(0).toElement(); - if(!x.isNull() /*&& x.attribute("type") == "form"*/) { + if (!x.isNull() /*&& x.attribute("type") == "form"*/) { QDomElement field = x.elementsByTagName("field").item(0).toElement(); - if(!field.isNull() && field.attribute("var") == "stream-method" && field.attribute("type") == "list-single") { + if (!field.isNull() && field.attribute("var") == "stream-method" + && field.attribute("type") == "list-single") { QDomNodeList nl = field.elementsByTagName("option"); - for(int n = 0; n < nl.count(); ++n) { - QDomElement e = nl.item(n).toElement(); + for (int n = 0; n < nl.count(); ++n) { + QDomElement e = nl.item(n).toElement(); QDomElement value = e.elementsByTagName("value").item(0).toElement(); - if(!value.isNull()) + if (!value.isNull()) streamTypes += value.text(); } } @@ -844,15 +766,15 @@ bool JT_PushFT::take(const QDomElement &e) } FTRequest r; - r.from = from; - r.iq_id = e.attribute("id"); - r.id = id; - r.fname = fname; - r.size = size; - r.desc = desc; + r.from = from; + r.iq_id = e.attribute("id"); + r.id = id; + r.fname = fname; + r.size = size; + r.desc = desc; r.rangeSupported = rangeSupported; - r.streamTypes = streamTypes; - r.thumbnail = Thumbnail(file.firstChildElement(QLatin1String("thumbnail"))); + r.streamTypes = streamTypes; + r.thumbnail = Thumbnail(file.firstChildElement(QLatin1String("thumbnail"))); emit incoming(r); return true; diff --git a/src/xmpp/xmpp-im/filetransfer.h b/src/xmpp/xmpp-im/filetransfer.h index bdb24f8a..6c8b57c5 100644 --- a/src/xmpp/xmpp-im/filetransfer.h +++ b/src/xmpp/xmpp-im/filetransfer.h @@ -20,172 +20,170 @@ #ifndef XMPP_FILETRANSFER_H #define XMPP_FILETRANSFER_H -#include "im.h" - -namespace XMPP +#include "xmpp/jid/jid.h" +#include "xmpp_task.h" +#include "xmpp_thumbs.h" + +namespace XMPP { +class BSConnection; +class BytestreamManager; +class Client; +class FileTransferManager; +struct FTRequest; +class Thumbnail; + +/*class AbstractFileTransfer { - //class BSConnection; - class BSConnection; - class BytestreamManager; - struct FTRequest; - - /*class AbstractFileTransfer - { - public: - // Receive - virtual Jid peer() const = 0; - virtual QString fileName() const = 0; - virtual qlonglong fileSize() const = 0; - virtual QString description() const { return ""; } - virtual bool rangeSupported() const { return false; } - virtual void accept(qlonglong offset=0, qlonglong length=0) = 0; - };*/ - - class FileTransfer : public QObject /*, public AbstractFileTransfer */ - { - Q_OBJECT - public: - enum { ErrReject, ErrNeg, ErrConnect, ErrProxy, ErrStream, Err400 }; - enum { Idle, Requesting, Connecting, WaitingForAccept, Active }; - ~FileTransfer(); - - FileTransfer *copy() const; - - void setProxy(const Jid &proxy); - - // send - void sendFile(const Jid &to, const QString &fname, qlonglong size, const QString &desc, Thumbnail &thumb); - qlonglong offset() const; - qlonglong length() const; - int dataSizeNeeded() const; - void writeFileData(const QByteArray &a); - const Thumbnail &thumbnail() const; - - // receive - Jid peer() const; - QString fileName() const; - qlonglong fileSize() const; - QString description() const; - bool rangeSupported() const; - void accept(qlonglong offset=0, qlonglong length=0); - - // both - void close(); // reject, or stop sending/receiving - BSConnection *bsConnection() const; // active link - - signals: - void accepted(); // indicates BSConnection has started - void connected(); - void readyRead(const QByteArray &a); - void bytesWritten(qint64); - void error(int); - - private slots: - void ft_finished(); - void stream_connected(); - void stream_connectionClosed(); - void stream_readyRead(); - void stream_bytesWritten(qint64); - void stream_error(int); - void doAccept(); - void reset(); - - private: - class Private; - Private *d; - - friend class FileTransferManager; - FileTransfer(FileTransferManager *, QObject *parent=0); - FileTransfer(const FileTransfer& other); - void man_waitForAccept(const FTRequest &req, const QString &streamType); - void takeConnection(BSConnection *c); - }; - - class FileTransferManager : public QObject - { - Q_OBJECT public: - FileTransferManager(Client *); - ~FileTransferManager(); - - bool isActive(const FileTransfer *ft) const; - void setDisabled(const QString &ns, bool state = true); - - Client *client() const; - FileTransfer *createTransfer(); - FileTransfer *takeIncoming(); - - signals: - void incomingReady(); - - private slots: - void pft_incoming(const FTRequest &req); - - private: - class Private; - Private *d; - - friend class Client; - void stream_incomingReady(BSConnection *); - - friend class FileTransfer; - BytestreamManager* streamManager(const QString &ns) const; - QStringList streamPriority() const; - QString link(FileTransfer *); - void con_accept(FileTransfer *); - void con_reject(FileTransfer *); - void unlink(FileTransfer *); - }; - - class JT_FT : public Task - { - Q_OBJECT - public: - JT_FT(Task *parent); - ~JT_FT(); - - void request(const Jid &to, const QString &id, const QString &fname, - qlonglong size, const QString &desc, - const QStringList &streamTypes, Thumbnail &thumb); - qlonglong rangeOffset() const; - qlonglong rangeLength() const; - QString streamType() const; - - void onGo(); - bool take(const QDomElement &); - - private: - class Private; - Private *d; - }; - - struct FTRequest - { - Jid from; - QString iq_id, id; - QString fname; - qlonglong size; - QString desc; - bool rangeSupported; - QStringList streamTypes; - Thumbnail thumbnail; - }; - class JT_PushFT : public Task - { - Q_OBJECT - public: - JT_PushFT(Task *parent); - ~JT_PushFT(); - - void respondSuccess(const Jid &to, const QString &id, qlonglong rangeOffset, qlonglong rangeLength, const QString &streamType); - void respondError(const Jid &to, const QString &id, - Stanza::Error::ErrorCond cond, const QString &str); - - bool take(const QDomElement &); - - signals: - void incoming(const FTRequest &req); - }; -} - -#endif + // Receive + virtual Jid peer() const = 0; + virtual QString fileName() const = 0; + virtual qlonglong fileSize() const = 0; + virtual QString description() const { return ""; } + virtual bool rangeSupported() const { return false; } + virtual void accept(qlonglong offset=0, qlonglong length=0) = 0; +};*/ + +class FileTransfer : public QObject /*, public AbstractFileTransfer */ +{ + Q_OBJECT +public: + enum { ErrReject, ErrNeg, ErrConnect, ErrProxy, ErrStream, Err400 }; + enum { Idle, Requesting, Connecting, WaitingForAccept, Active }; + ~FileTransfer(); + + FileTransfer *copy() const; + + void setProxy(const Jid &proxy); + + // send + void sendFile(const Jid &to, const QString &fname, qlonglong size, const QString &desc, Thumbnail &thumb); + qlonglong offset() const; + qlonglong length() const; + int dataSizeNeeded() const; + void writeFileData(const QByteArray &a); + const Thumbnail &thumbnail() const; + + // receive + Jid peer() const; + QString fileName() const; + qlonglong fileSize() const; + QString description() const; + bool rangeSupported() const; + void accept(qlonglong offset = 0, qlonglong length = 0); + + // both + void close(); // reject, or stop sending/receiving + BSConnection *bsConnection() const; // active link + +signals: + void accepted(); // indicates BSConnection has started + void connected(); + void readyRead(const QByteArray &a); + void bytesWritten(qint64); + void error(int); + +private slots: + void ft_finished(); + void stream_connected(); + void stream_connectionClosed(); + void stream_readyRead(); + void stream_bytesWritten(qint64); + void stream_error(int); + void doAccept(); + void reset(); + +private: + class Private; + Private *d; + + friend class FileTransferManager; + FileTransfer(FileTransferManager *, QObject *parent = nullptr); + FileTransfer(const FileTransfer &other); + void man_waitForAccept(const FTRequest &req, const QString &streamType); + void takeConnection(BSConnection *c); +}; + +class FileTransferManager : public QObject { + Q_OBJECT +public: + FileTransferManager(Client *); + ~FileTransferManager(); + + bool isActive(const FileTransfer *ft) const; + void setDisabled(const QString &ns, bool state = true); + + Client *client() const; + FileTransfer *createTransfer(); + FileTransfer *takeIncoming(); + +signals: + void incomingReady(); + +private slots: + void pft_incoming(const FTRequest &req); + +private: + class Private; + Private *d; + + friend class Client; + void stream_incomingReady(BSConnection *); + + friend class FileTransfer; + BytestreamManager *streamManager(const QString &ns) const; + QStringList streamPriority() const; + QString link(FileTransfer *); + void con_accept(FileTransfer *); + void con_reject(FileTransfer *); + void unlink(FileTransfer *); +}; + +class JT_FT : public Task { + Q_OBJECT +public: + JT_FT(Task *parent); + ~JT_FT(); + + void request(const Jid &to, const QString &id, const QString &fname, qlonglong size, const QString &desc, + const QStringList &streamTypes, Thumbnail &thumb); + qlonglong rangeOffset() const; + qlonglong rangeLength() const; + QString streamType() const; + + void onGo(); + bool take(const QDomElement &); + +private: + class Private; + Private *d; +}; + +struct FTRequest { + Jid from; + QString iq_id, id; + QString fname; + qlonglong size; + QString desc; + bool rangeSupported; + QStringList streamTypes; + Thumbnail thumbnail; +}; +class JT_PushFT : public Task { + Q_OBJECT +public: + JT_PushFT(Task *parent); + ~JT_PushFT(); + + void respondSuccess(const Jid &to, const QString &id, qlonglong rangeOffset, qlonglong rangeLength, + const QString &streamType); + void respondError(const Jid &to, const QString &id, Stanza::Error::ErrorCond cond, const QString &str); + + bool take(const QDomElement &); + +signals: + void incoming(const FTRequest &req); +}; +} // namespace XMPP + +#endif // XMPP_FILETRANSFER_H diff --git a/src/xmpp/xmpp-im/httpfileupload.cpp b/src/xmpp/xmpp-im/httpfileupload.cpp index 67d93fc0..efbdc696 100644 --- a/src/xmpp/xmpp-im/httpfileupload.cpp +++ b/src/xmpp/xmpp-im/httpfileupload.cpp @@ -17,16 +17,17 @@ * */ +#include "httpfileupload.h" + +#include "xmpp_client.h" +#include "xmpp_serverinfomanager.h" +#include "xmpp_tasks.h" +#include "xmpp_xmlcommon.h" + #include #include #include #include -#include - -#include "httpfileupload.h" -#include "xmpp_tasks.h" -#include "xmpp_xmlcommon.h" -#include "xmpp_serverinfomanager.h" using namespace XMPP; @@ -36,49 +37,40 @@ static QLatin1String xmlns_v0_3_1("urn:xmpp:http:upload:0"); //---------------------------------------------------------------------------- // HttpFileUpload //---------------------------------------------------------------------------- -class HttpFileUpload::Private -{ +class HttpFileUpload::Private { public: - HttpFileUpload::State state = State::None; - XMPP::Client *client = nullptr; - QIODevice *sourceDevice = nullptr; - QPointer qnam = nullptr; - quint64 fileSize = 0; - QString fileName; - QString mediaType; - QList httpHosts; + HttpFileUpload::State state = State::None; + XMPP::Client *client = nullptr; + QIODevice *sourceDevice = nullptr; + QPointer qnam = nullptr; + quint64 fileSize = 0; + QString fileName; + QString mediaType; + QList httpHosts; struct { HttpFileUpload::ErrorCode statusCode = HttpFileUpload::ErrorCode::NoError; - QString statusString; - QString getUrl; - QString putUrl; - XEP0363::HttpHeaders putHeaders; - quint64 sizeLimit = 0; + QString statusString; + QString getUrl; + QString putUrl; + XEP0363::HttpHeaders putHeaders; + quint64 sizeLimit = 0; } result; }; HttpFileUpload::HttpFileUpload(XMPP::Client *client, QIODevice *source, size_t fsize, const QString &dstFilename, - const QString &mType) : - QObject(client), - d(new Private) + const QString &mType) : QObject(client), d(new Private) { - d->client = client; + d->client = client; d->sourceDevice = source; - d->fileName = dstFilename; - d->fileSize = fsize; - d->mediaType = mType; + d->fileName = dstFilename; + d->fileSize = fsize; + d->mediaType = mType; } -HttpFileUpload::~HttpFileUpload() -{ - qDebug("destroying"); -} +HttpFileUpload::~HttpFileUpload() { qDebug("destroying"); } -void HttpFileUpload::setNetworkAccessManager(QNetworkAccessManager *qnam) -{ - d->qnam = qnam; -} +void HttpFileUpload::setNetworkAccessManager(QNetworkAccessManager *qnam) { d->qnam = qnam; } void HttpFileUpload::start() { @@ -88,169 +80,186 @@ void HttpFileUpload::start() setState(State::GettingSlot); d->result.statusCode = HttpFileUpload::ErrorCode::NoError; + + auto mgr = d->client->httpFileUploadManager(); + if (mgr->discoveryStatus() == HttpFileUploadManager::DiscoNotFound) { + d->result.statusCode = HttpFileUpload::ErrorCode::NoUploadService; + d->result.statusString = "No suitable http upload services were found"; + done(State::Error); + return; + } + + if (mgr->discoveryStatus() == HttpFileUploadManager::DiscoFound) { + d->httpHosts = mgr->discoHosts(); + tryNextServer(); + return; + } + static QList> featureOptions; if (featureOptions.isEmpty()) { featureOptions << (QSet() << xmlns_v0_2_5) << (QSet() << xmlns_v0_3_1); } d->client->serverInfoManager()->queryServiceInfo( - QLatin1String("store"), QLatin1String("file"), - featureOptions, QRegExp("^(upload|http|stor|file|dis|drive).*"), ServerInfoManager::SQ_CheckAllOnNoMatch, - [this](const QList &items) - { - d->httpHosts.clear(); - for (const auto &item: items) { - const QStringList &l = item.features().list(); - XEP0363::version ver = XEP0363::vUnknown; - QString xmlns; - quint64 sizeLimit = 0; - if (l.contains(xmlns_v0_3_1)) { - ver = XEP0363::v0_3_1; - xmlns = xmlns_v0_3_1; - } else if (l.contains(xmlns_v0_2_5)) { - ver = XEP0363::v0_2_5; - xmlns = xmlns_v0_2_5; - } - if (ver != XEP0363::vUnknown) { - QList> hosts; - const XData::Field field = item.registeredExtension(xmlns).getField(QLatin1String("max-file-size")); - if (field.isValid() && field.type() == XData::Field::Field_TextSingle) - sizeLimit = field.value().at(0).toULongLong(); - HttpHost host; - host.ver = ver; - host.jid = item.jid(); - host.sizeLimit = sizeLimit; - QVariant metaProps(d->client->serverInfoManager()->serviceMeta(host.jid, "httpprops")); - if (metaProps.isValid()) { - host.props = HostProps(metaProps.value()); - } else { - host.props = SecureGet | SecurePut; - if (ver == XEP0363::v0_3_1) - host.props |= NewestVer; + QLatin1String("store"), QLatin1String("file"), featureOptions, + QRegularExpression("^(upload|http|stor|file|dis|drive).*"), ServerInfoManager::SQ_CheckAllOnNoMatch, + [this](const QList &items) { + d->httpHosts.clear(); + for (const auto &item : items) { + const QStringList &l = item.features().list(); + XEP0363::version ver = XEP0363::vUnknown; + QString xmlns; + quint64 sizeLimit = 0; + if (l.contains(xmlns_v0_3_1)) { + ver = XEP0363::v0_3_1; + xmlns = xmlns_v0_3_1; + } else if (l.contains(xmlns_v0_2_5)) { + ver = XEP0363::v0_2_5; + xmlns = xmlns_v0_2_5; } - int value = 0; - if (host.props & SecureGet) value += 5; - if (host.props & SecurePut) value += 5; - if (host.props & NewestVer) value += 3; - if (host.props & Failure) value -= 15; - if (!sizeLimit || d->fileSize < sizeLimit) - hosts.append({host,value}); - - // no sorting in preference order. most preferred go first - std::sort(hosts.begin(), hosts.end(), [](const auto &a, const auto &b){ - return a.second > b.second; - }); - for (auto &hp: hosts) { - d->httpHosts.append(hp.first); + if (ver != XEP0363::vUnknown) { + QVector> hosts; + const XData::Field field = item.registeredExtension(xmlns).getField(QLatin1String("max-file-size")); + if (field.isValid() && field.type() == XData::Field::Field_TextSingle) + sizeLimit = field.value().at(0).toULongLong(); + HttpHost host; + host.ver = ver; + host.jid = item.jid(); + host.sizeLimit = sizeLimit; + QVariant metaProps(d->client->serverInfoManager()->serviceMeta(host.jid, "httpprops")); + if (metaProps.isValid()) { + host.props = HostProps(metaProps.value()); + } else { + host.props = SecureGet | SecurePut; + if (ver == XEP0363::v0_3_1) + host.props |= NewestVer; + } + int value = 0; + if (host.props & SecureGet) + value += 5; + if (host.props & SecurePut) + value += 5; + if (host.props & NewestVer) + value += 3; + if (host.props & Failure) + value -= 15; + if (!sizeLimit || d->fileSize < sizeLimit) + hosts.append({ host, value }); + + // no sorting in preference order. most preferred go first + std::sort(hosts.begin(), hosts.end(), + [](const auto &a, const auto &b) { return a.second > b.second; }); + for (auto &hp : hosts) { + d->httpHosts.append(hp.first); + } } } - } - //d->currentHost = d->httpHosts.begin(); - if (d->httpHosts.isEmpty()) { // if empty as the last resort check all services - d->result.statusCode = HttpFileUpload::ErrorCode::NoUploadService; - d->result.statusString = "No suitable http upload services were found"; - done(State::Error); - } else { - tryNextServer(); - } - }); + // d->currentHost = d->httpHosts.begin(); + d->client->httpFileUploadManager()->setDiscoHosts(d->httpHosts); + if (d->httpHosts.isEmpty()) { // if empty as the last resort check all services + d->result.statusCode = HttpFileUpload::ErrorCode::NoUploadService; + d->result.statusString = "No suitable http upload services were found"; + done(State::Error); + } else { + tryNextServer(); + } + }); } void HttpFileUpload::tryNextServer() { if (d->httpHosts.isEmpty()) { // if empty as the last resort check all services - d->result.statusCode = HttpFileUpload::ErrorCode::NoUploadService; - d->result.statusString = "All http services are either non compliant or returned errors"; + if (d->result.statusCode == HttpFileUpload::ErrorCode::NoError) { + d->result.statusCode = HttpFileUpload::ErrorCode::NoUploadService; + d->result.statusString = "All http services are either non compliant or returned errors"; + } done(State::Error); return; } - HttpHost host = d->httpHosts.takeFirst(); + HttpHost host = d->httpHosts.takeFirst(); d->result.sizeLimit = host.sizeLimit; - auto jt = new JT_HTTPFileUpload(d->client->rootTask()); - connect(jt, &JT_HTTPFileUpload::finished, this, [this, jt, host]() mutable { - if (!jt->success()) { - host.props |= Failure; - int code = jt->statusCode(); - if (code < 300) { - code++; // ErrDisc and ErrTimeout. but 0 code is already occupated + auto jt = new JT_HTTPFileUpload(d->client->rootTask()); + connect( + jt, &JT_HTTPFileUpload::finished, this, + [this, jt, host]() mutable { + if (!jt->success()) { + host.props |= Failure; + int code = jt->statusCode(); + if (code < 300) { + code++; // ErrDisc and ErrTimeout. but 0 code is already occupated + } + d->result.statusCode = static_cast(jt->statusCode()); + d->result.statusString = jt->statusString(); + d->client->serverInfoManager()->setServiceMeta(host.jid, QLatin1String("httpprops"), int(host.props)); + if (d->httpHosts.isEmpty()) + done(State::Error); + else + tryNextServer(); + return; } - d->result.statusCode = static_cast(jt->statusCode()); - d->result.statusString = jt->statusString(); - d->client->serverInfoManager()->setServiceMeta(host.jid, QLatin1String("httpprops"), int(host.props)); - if (d->httpHosts.isEmpty()) - done(State::Error); + + d->result.getUrl = jt->url(JT_HTTPFileUpload::GetUrl); + d->result.putUrl = jt->url(JT_HTTPFileUpload::PutUrl); + d->result.putHeaders = jt->headers(); + if (d->result.getUrl.startsWith("https://")) + host.props |= SecureGet; else - tryNextServer(); - return; - } + host.props &= ~SecureGet; + if (d->result.putUrl.startsWith("https://")) + host.props |= SecurePut; + else + host.props &= ~SecurePut; + host.props &= ~Failure; - d->result.getUrl = jt->url(JT_HTTPFileUpload::GetUrl); - d->result.putUrl = jt->url(JT_HTTPFileUpload::PutUrl); - d->result.putHeaders = jt->headers(); - if (d->result.getUrl.startsWith("https://")) - host.props |= SecureGet; - else - host.props &= ~SecureGet; - if (d->result.putUrl.startsWith("https://")) - host.props |= SecurePut; - else - host.props &= ~SecurePut; - host.props &= ~Failure; - - d->client->serverInfoManager()->setServiceMeta(host.jid, QLatin1String("httpprops"), int(host.props)); - - if (!d->qnam) { // w/o network access manager, it's not more than getting slots - done(State::Success); - return; - } + d->client->serverInfoManager()->setServiceMeta(host.jid, QLatin1String("httpprops"), int(host.props)); - setState(State::HttpRequest); - // time for a http request - QNetworkRequest req(d->result.putUrl); - for (auto &h: d->result.putHeaders) { - req.setRawHeader(h.name.toLatin1(), h.value.toLatin1()); - } - auto reply = d->qnam->put(req, d->sourceDevice); - connect(reply, &QNetworkReply::finished, this, [this, reply](){ - if (reply->error() == QNetworkReply::NoError) { + if (!d->qnam) { // w/o network access manager, it's not more than getting slots done(State::Success); - } else { - d->result.statusCode = ErrorCode::HttpFailed; - d->result.statusString = reply->errorString(); - if (d->httpHosts.isEmpty()) - done(State::Error); - else - tryNextServer(); + return; } - reply->deleteLater(); - }); - }, Qt::QueuedConnection); + setState(State::HttpRequest); + // time for a http request + QNetworkRequest req(d->result.putUrl); + for (auto &h : d->result.putHeaders) + req.setRawHeader(h.name.toLatin1(), h.value.toLatin1()); + if (!d->mediaType.isEmpty()) + req.setHeader(QNetworkRequest::ContentTypeHeader, d->mediaType); + + auto reply = d->qnam->put(req, d->sourceDevice); + connect(reply, &QNetworkReply::uploadProgress, this, &HttpFileUpload::progress); + connect(reply, &QNetworkReply::finished, this, [this, reply]() { + if (reply->error() == QNetworkReply::NoError) { + done(State::Success); + } else { + d->result.statusCode = ErrorCode::HttpFailed; + d->result.statusString = reply->errorString(); + qDebug("http upload failed: %s", qPrintable(d->result.statusString)); + if (d->httpHosts.isEmpty()) + done(State::Error); + else + tryNextServer(); + } + reply->deleteLater(); + }); + }, + Qt::QueuedConnection); jt->request(host.jid, d->fileName, d->fileSize, d->mediaType, host.ver); jt->go(true); } -bool HttpFileUpload::success() const -{ - return d->state == State::Success; -} +bool HttpFileUpload::success() const { return d->state == State::Success; } -HttpFileUpload::ErrorCode HttpFileUpload::statusCode() const -{ - return d->result.statusCode; -} +HttpFileUpload::ErrorCode HttpFileUpload::statusCode() const { return d->result.statusCode; } -const QString & HttpFileUpload::statusString() const -{ - return d->result.statusString; -} +const QString &HttpFileUpload::statusString() const { return d->result.statusString; } HttpFileUpload::HttpSlot HttpFileUpload::getHttpSlot() { - HttpSlot slot; + HttpSlot slot {}; if (d->state == State::Success) { - slot.get.url = d->result.getUrl; - slot.put.url = d->result.putUrl; - slot.put.headers = d->result.putHeaders; + slot.get.url = d->result.getUrl; + slot.put.url = d->result.putUrl; + slot.put.headers = d->result.putHeaders; slot.limits.fileSize = d->result.sizeLimit; } return slot; @@ -275,40 +284,46 @@ void HttpFileUpload::done(State state) //---------------------------------------------------------------------------- // JT_HTTPFileUpload //---------------------------------------------------------------------------- -class JT_HTTPFileUpload::Private -{ +class JT_HTTPFileUpload::Private { public: - Jid to; - QDomElement iq; - QStringList urls; - XEP0363::version ver; + Jid to; + QDomElement iq; + QStringList urls; + XEP0363::version ver; XEP0363::HttpHeaders headers; }; -JT_HTTPFileUpload::JT_HTTPFileUpload(Task *parent) - : Task(parent) +JT_HTTPFileUpload::JT_HTTPFileUpload(Task *parent) : Task(parent) { - d = new Private; + d = new Private; d->ver = XEP0363::vUnknown; d->urls << QString() << QString(); } -JT_HTTPFileUpload::~JT_HTTPFileUpload() -{ - delete d; -} +JT_HTTPFileUpload::~JT_HTTPFileUpload() { delete d; } -void JT_HTTPFileUpload::request(const Jid &to, const QString &fname, - quint64 fsize, const QString &ftype, XEP0363::version ver) +void JT_HTTPFileUpload::request(const Jid &to, const QString &fname, quint64 fsize, const QString &ftype, + XEP0363::version ver) { - d->to = to; - d->ver = ver; - d->iq = createIQ(doc(), "get", to.full(), id()); - QDomElement req = doc()->createElement("request"); - switch (ver) - { + QString ns; + switch (ver) { + case XEP0363::v0_2_5: + ns = xmlns_v0_2_5; + break; + case XEP0363::v0_3_1: + ns = xmlns_v0_3_1; + break; + default: + return; + } + + d->to = to; + d->ver = ver; + d->iq = createIQ(doc(), "get", to.full(), id()); + QDomElement req = doc()->createElementNS(ns, "request"); + switch (ver) { case XEP0363::v0_2_5: - req.setAttribute("xmlns", xmlns_v0_2_5); + ns = xmlns_v0_2_5; req.appendChild(textTag(doc(), "filename", fname)); req.appendChild(textTag(doc(), "size", QString::number(fsize))); if (!ftype.isEmpty()) { @@ -316,7 +331,7 @@ void JT_HTTPFileUpload::request(const Jid &to, const QString &fname, } break; case XEP0363::v0_3_1: - req.setAttribute("xmlns", xmlns_v0_3_1); + ns = xmlns_v0_3_1; req.setAttribute("filename", fname); req.setAttribute("size", fsize); if (!ftype.isEmpty()) @@ -329,15 +344,9 @@ void JT_HTTPFileUpload::request(const Jid &to, const QString &fname, d->iq.appendChild(req); } -QString JT_HTTPFileUpload::url(UrlType t) const -{ - return d->urls.value(t); -} +QString JT_HTTPFileUpload::url(UrlType t) const { return d->urls.value(t); } -XEP0363::HttpHeaders JT_HTTPFileUpload::headers() const -{ - return d->headers; -} +XEP0363::HttpHeaders JT_HTTPFileUpload::headers() const { return d->headers; } void JT_HTTPFileUpload::onGo() { @@ -355,36 +364,34 @@ bool JT_HTTPFileUpload::take(const QDomElement &e) return true; } - bool correct_xmlns = false; - QString getUrl, putUrl; + bool correct_xmlns = false; + QString getUrl, putUrl; XEP0363::HttpHeaders headers; - const QDomElement &slot = e.firstChildElement("slot"); + const QDomElement &slot = e.firstChildElement("slot"); if (!slot.isNull()) { const QDomElement &get = slot.firstChildElement("get"); const QDomElement &put = slot.firstChildElement("put"); - switch (d->ver) - { + switch (d->ver) { case XEP0363::v0_2_5: - correct_xmlns = slot.attribute("xmlns") == xmlns_v0_2_5; - getUrl = tagContent(get); - putUrl = tagContent(put); + correct_xmlns = slot.namespaceURI() == xmlns_v0_2_5; + getUrl = tagContent(get); + putUrl = tagContent(put); break; case XEP0363::v0_3_1: - correct_xmlns = slot.attribute("xmlns") == xmlns_v0_3_1; - getUrl = get.attribute("url"); + correct_xmlns = slot.namespaceURI() == xmlns_v0_3_1; + getUrl = get.attribute("url"); if (!put.isNull()) { - putUrl = put.attribute("url"); + putUrl = put.attribute("url"); QDomElement he = put.firstChildElement("header"); while (!he.isNull()) { // only next are allowed: Authorization, Cookie, Expires QString header = he.attribute("name").trimmed().remove(QLatin1Char('\n')); - QString value = he.text().trimmed().remove(QLatin1Char('\n')); - if (!value.isEmpty() && - (header.compare(QLatin1String("Authorization"), Qt::CaseInsensitive) == 0 || - header.compare(QLatin1String("Cookie"), Qt::CaseInsensitive) == 0 || - header.compare(QLatin1String("Expires"), Qt::CaseInsensitive) == 0)) - { - headers.append(XEP0363::HttpHeader{header, value}); + QString value = he.text().trimmed().remove(QLatin1Char('\n')); + if (!value.isEmpty() + && (header.compare(QLatin1String("Authorization"), Qt::CaseInsensitive) == 0 + || header.compare(QLatin1String("Cookie"), Qt::CaseInsensitive) == 0 + || header.compare(QLatin1String("Expires"), Qt::CaseInsensitive) == 0)) { + headers.append(XEP0363::HttpHeader { header, value }); } he = he.nextSiblingElement("header"); } @@ -403,50 +410,53 @@ bool JT_HTTPFileUpload::take(const QDomElement &e) d->urls[PutUrl] = putUrl; d->headers = headers; setSuccess(); - } - else + } else setError(ErrInvalidResponse, "Either `put` or `get` URL is missing in the server's reply."); return true; } - class HttpFileUploadManager::Private { public: - Client *client = nullptr; - QPointer qnam; - bool externalQnam = false; - QLinkedList hosts; + Client *client = nullptr; + QPointer externalQnam; + int discoStatus = 0; + QList discoHosts; + std::list hosts; }; +HttpFileUploadManager::HttpFileUploadManager(Client *parent) : QObject(parent), d(new Private) { d->client = parent; } +HttpFileUploadManager::~HttpFileUploadManager() { delete d; } -HttpFileUploadManager::HttpFileUploadManager(Client *parent) : - QObject(parent), - d(new Private) -{ - d->client = parent; -} +int HttpFileUploadManager::discoveryStatus() const { return d->discoStatus; } -void HttpFileUploadManager::setNetworkAccessManager(QNetworkAccessManager *qnam) -{ - d->externalQnam = true; - d->qnam = qnam; -} +void HttpFileUploadManager::setNetworkAccessManager(QNetworkAccessManager *qnam) { d->externalQnam = qnam; } -HttpFileUpload* HttpFileUploadManager::upload(const QString &srcFilename, const QString &dstFilename, const QString &mType) +HttpFileUpload *HttpFileUploadManager::upload(const QString &srcFilename, const QString &dstFilename, + const QString &mType) { auto f = new QFile(srcFilename); f->open(QIODevice::ReadOnly); auto hfu = upload(f, f->size(), dstFilename, mType); + connect(hfu, &HttpFileUpload::finished, this, [f]() { f->close(); }); f->setParent(hfu); return hfu; } -HttpFileUpload* HttpFileUploadManager::upload(QIODevice *source, size_t fsize, const QString &dstFilename, const QString &mType) +HttpFileUpload *HttpFileUploadManager::upload(QIODevice *source, quint64 fsize, const QString &dstFilename, + const QString &mType) { - auto hfu = new HttpFileUpload(d->client, source, fsize, dstFilename, mType); - QNetworkAccessManager *qnam = d->externalQnam? d->qnam.data() : d->client->networkAccessManager(); + auto hfu = new HttpFileUpload(d->client, source, fsize, dstFilename, mType); + QNetworkAccessManager *qnam = d->externalQnam ? d->externalQnam.data() : d->client->networkAccessManager(); hfu->setNetworkAccessManager(qnam); QMetaObject::invokeMethod(hfu, "start", Qt::QueuedConnection); return hfu; } + +const QList &HttpFileUploadManager::discoHosts() const { return d->discoHosts; } + +void HttpFileUploadManager::setDiscoHosts(const QList &hosts) +{ + d->discoStatus = hosts.size() ? DiscoFound : DiscoNotFound; + d->discoHosts = hosts; +} diff --git a/src/xmpp/xmpp-im/httpfileupload.h b/src/xmpp/xmpp-im/httpfileupload.h index 43298393..2896bb0d 100644 --- a/src/xmpp/xmpp-im/httpfileupload.h +++ b/src/xmpp/xmpp-im/httpfileupload.h @@ -20,31 +20,34 @@ #ifndef XMPP_HTTPFILEUPLOAD_H #define XMPP_HTTPFILEUPLOAD_H -#include +#include "xmpp/jid/jid.h" +#include "xmpp_task.h" + #include +#include +class QIODevice; class QNetworkAccessManager; -#include "im.h" - -namespace XMPP -{ - +namespace XMPP { +class Client; namespace XEP0363 { -enum version { vUnknown, v0_2_5, v0_3_1 }; -struct HttpHeader { QString name; QString value; }; -typedef QList HttpHeaders; + enum version { vUnknown, v0_2_5, v0_3_1 }; + struct HttpHeader { + QString name; + QString value; + }; + typedef QList HttpHeaders; } -class HttpFileUpload : public QObject -{ +class HttpFileUpload : public QObject { Q_OBJECT public: enum HostPropFlag { SecureGet = 1, // 0.2.5 of the xep didn't require that SecurePut = 2, // 0.2.5 of the xep didn't require that NewestVer = 4, - Failure = 8 // had some failure (no/unexpected response to slot request, early http errors) + Failure = 8 // had some failure (no/unexpected response to slot request, early http errors) }; Q_DECLARE_FLAGS(HostProps, HostPropFlag) @@ -62,7 +65,7 @@ class HttpFileUpload : public QObject QString url; } get; struct { - QString url; + QString url; QList headers; } put; struct { @@ -72,13 +75,13 @@ class HttpFileUpload : public QObject struct HttpHost { XEP0363::version ver; - Jid jid; - quint64 sizeLimit; - HostProps props; + Jid jid; + quint64 sizeLimit; + HostProps props; }; HttpFileUpload(Client *client, QIODevice *source, size_t fsize, const QString &dstFilename, - const QString &mType = QString::null); + const QString &mType = QString()); HttpFileUpload(const HttpFileUpload &) = delete; ~HttpFileUpload(); @@ -92,10 +95,10 @@ class HttpFileUpload : public QObject */ void setNetworkAccessManager(QNetworkAccessManager *qnam); - bool success() const; - ErrorCode statusCode() const; - const QString & statusString() const; - HttpSlot getHttpSlot(); + bool success() const; + ErrorCode statusCode() const; + const QString &statusString() const; + HttpSlot getHttpSlot(); public slots: void start(); @@ -103,6 +106,7 @@ public slots: signals: void stateChanged(); void finished(); + void progress(qint64 bytesReceived, qint64 bytesTotal); private: enum State { None, GettingSlot, HttpRequest, Success, Error }; @@ -119,18 +123,18 @@ public slots: }; Q_DECLARE_OPERATORS_FOR_FLAGS(HttpFileUpload::HostProps) -class JT_HTTPFileUpload : public Task -{ +class JT_HTTPFileUpload : public Task { Q_OBJECT public: enum UrlType { GetUrl = 0, PutUrl = 1 }; - enum { ErrInvalidResponse = int(HttpFileUpload::ErrorCode::SlotReceiveFailed) - 1 }; // -1 to be mapped to ErrDisc, ErrTimeout, ... + enum { + ErrInvalidResponse = int(HttpFileUpload::ErrorCode::SlotReceiveFailed) - 1 + }; // -1 to be mapped to ErrDisc, ErrTimeout, ... JT_HTTPFileUpload(Task *parent); ~JT_HTTPFileUpload(); - void request(const Jid &to, const QString &fname, - quint64 fsize, const QString &ftype, XEP0363::version ver); + void request(const Jid &to, const QString &fname, quint64 fsize, const QString &ftype, XEP0363::version ver); QString url(UrlType t) const; XEP0363::HttpHeaders headers() const; @@ -142,13 +146,18 @@ class JT_HTTPFileUpload : public Task Private *d; }; -class HttpFileUploadManager : public QObject -{ +class HttpFileUploadManager : public QObject { Q_OBJECT public: - typedef std::function Callback; // params: success, detail. where detail could be a "get" url + enum { DiscoNone = 0x0, DiscoNotFound = 0x1, DiscoFound = 0x2 }; + + typedef std::function + Callback; // params: success, detail. where detail could be a "get" url HttpFileUploadManager(Client *parent); + ~HttpFileUploadManager(); + + int discoveryStatus() const; /** * @brief setNetworkAccessManager sets network access manager to do http requests. @@ -170,8 +179,8 @@ class HttpFileUploadManager : public QObject * @param mType meta type. image/png for example * @return returns a handler object which will signal "finished" when ready */ - HttpFileUpload* upload(const QString &srcFilename, const QString &dstFilename = QString::null, - const QString &mType = QString::null); + HttpFileUpload *upload(const QString &srcFilename, const QString &dstFilename = QString(), + const QString &mType = QString()); /** * @brief uploads data of given size from the given to remote server @@ -181,14 +190,17 @@ class HttpFileUploadManager : public QObject * @param mType - meta type * @return returns a handler object which will signal "finished" when ready */ - HttpFileUpload* upload(QIODevice *source, size_t fsize, const QString &dstFilename, - const QString &mType = QString::null); + HttpFileUpload *upload(QIODevice *source, quint64 fsize, const QString &dstFilename, + const QString &mType = QString()); private: + friend class HttpFileUpload; + const QList &discoHosts() const; + void setDiscoHosts(const QList &hosts); + class Private; Private *d; }; +} // namespace XMPP -} - -#endif +#endif // XMPP_HTTPFILEUPLOAD_H diff --git a/src/xmpp/xmpp-im/im.h b/src/xmpp/xmpp-im/im.h index 78d336c8..d1175d56 100644 --- a/src/xmpp/xmpp-im/im.h +++ b/src/xmpp/xmpp-im/im.h @@ -20,113 +20,33 @@ #ifndef XMPP_IM_H #define XMPP_IM_H -#include -//Added by qt3to4: -#include - -#include "xmpp.h" -#include "xmpp/jid/jid.h" -#include "xmpp_muc.h" -#include "xmpp_message.h" -#include "xmpp_chatstate.h" -#include "xmpp_status.h" -#include "xmpp_htmlelement.h" -#include "xmpp_features.h" -#include "xmpp_httpauthrequest.h" -#include "xmpp_url.h" -#include "xmpp_task.h" -#include "xmpp_resource.h" -#include "xmpp_resourcelist.h" -#include "xmpp_roster.h" -#include "xmpp_rosteritem.h" -#include "xmpp_liverosteritem.h" -#include "xmpp_liveroster.h" -#include "xmpp_rosterx.h" -#include "xmpp_xdata.h" -#include "xmpp_discoitem.h" -#include "xmpp_agentitem.h" -#include "xmpp_client.h" -#include "xmpp_address.h" -#include "xmpp_hash.h" -#include "xmpp_thumbs.h" -#include "xmpp_pubsubitem.h" -#include "xmpp_pubsubretraction.h" - -namespace XMPP -{ - typedef QList AgentList; - typedef QList DiscoList; - - class FormField - { - public: - enum { username, nick, password, name, first, last, email, address, city, state, zip, phone, url, date, misc }; - FormField(const QString &type="", const QString &value=""); - ~FormField(); - - int type() const; - QString fieldName() const; - QString realName() const; - bool isSecret() const; - const QString & value() const; - void setType(int); - bool setType(const QString &); - void setValue(const QString &); - - private: - int tagNameToType(const QString &) const; - QString typeToTagName(int) const; - - int v_type; - QString v_value; - - class Private; - Private *d = nullptr; - }; - - class Form : public QList - { - public: - Form(const Jid &j=""); - ~Form(); - - Jid jid() const; - QString instructions() const; - QString key() const; - void setJid(const Jid &); - void setInstructions(const QString &); - void setKey(const QString &); - - private: - Jid v_jid; - QString v_instructions, v_key; - - class Private; - Private *d = nullptr; - }; - - class SearchResult - { - public: - SearchResult(const Jid &jid=""); - ~SearchResult(); - - const Jid & jid() const; - const QString & nick() const; - const QString & first() const; - const QString & last() const; - const QString & email() const; - - void setJid(const Jid &); - void setNick(const QString &); - void setFirst(const QString &); - void setLast(const QString &); - void setEmail(const QString &); - - private: - Jid v_jid; - QString v_nick, v_first, v_last, v_email; - }; -} - -#endif +#include "iris/xmpp.h" +#include "iris/xmpp_address.h" +#include "iris/xmpp_agentitem.h" +#include "iris/xmpp_chatstate.h" +#include "iris/xmpp_client.h" +#include "iris/xmpp_discoitem.h" +#include "iris/xmpp_features.h" +#include "iris/xmpp_form.h" +#include "iris/xmpp_hash.h" +#include "iris/xmpp_htmlelement.h" +#include "iris/xmpp_httpauthrequest.h" +#include "iris/xmpp_jid.h" +#include "iris/xmpp_liveroster.h" +#include "iris/xmpp_liverosteritem.h" +#include "iris/xmpp_message.h" +#include "iris/xmpp_muc.h" +#include "iris/xmpp_pubsubitem.h" +#include "iris/xmpp_pubsubretraction.h" +#include "iris/xmpp_resource.h" +#include "iris/xmpp_resourcelist.h" +#include "iris/xmpp_roster.h" +#include "iris/xmpp_rosteritem.h" +#include "iris/xmpp_rosterx.h" +#include "iris/xmpp_status.h" +#include "iris/xmpp_task.h" +#include "iris/xmpp_thumbs.h" +#include "iris/xmpp_url.h" +#include "iris/xmpp_xdata.h" + +#endif // XMPP_IM_H diff --git a/src/xmpp/xmpp-im/jingle-application.cpp b/src/xmpp/xmpp-im/jingle-application.cpp new file mode 100644 index 00000000..8e704c05 --- /dev/null +++ b/src/xmpp/xmpp-im/jingle-application.cpp @@ -0,0 +1,432 @@ +/* + * jignle-application.cpp - Base Jingle application classes + * Copyright (C) 2019 Sergey Ilinykh + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library. If not, see . + * + */ + +#include "jingle-application.h" +#include "jingle-session.h" +#include "xmpp_client.h" +#include "xmpp_task.h" + +#include +#include + +namespace XMPP { namespace Jingle { + + class ConnectionWaiter : public QObject { + Q_OBJECT + + std::function ready; + std::function failed; + Connection::Ptr connection; + QWeakPointer transport; + + void waitConnected() + { + connect(connection.data(), &Connection::error, this, + [this](int code) { onFailed(QString("error=%1").arg(code)); }); + connect(connection.data(), &Connection::connected, this, &ConnectionWaiter::onReady); + } + + void onFailed(const QString &errorMessage = QString()) + { + if (!errorMessage.isEmpty()) + qDebug("ConnectionWaiter: error: %s", qPrintable(errorMessage)); + if (connection) + connection->disconnect(this); // qt signals + if (auto t = transport.lock()) { + t->disconnect(this); + } + failed(); + deleteLater(); + } + + void onReady() + { + connection->disconnect(this); // qt signals + transport.lock()->disconnect(this); + ready(connection); + deleteLater(); + } + + public: + ConnectionWaiter(TransportFeatures features, std::function &&ready, + std::function &&failed, Application *app) : + QObject(app), ready(std::move(ready)), failed(std::move(failed)) + { + auto tr = app->transport(); + transport = tr; + Q_ASSERT(!tr.isNull()); + + connect(tr.data(), &Transport::stateChanged, this, [this]() { + auto locked = transport.lock(); + if (locked->state() == State::Finished) { + onFailed(QLatin1String("Transport") + locked->pad()->ns() + + QLatin1String(" is in finished state and no connection => transport failure")); + } + }); + if (tr->isLocal()) { + connection = tr->addChannel(features, app->contentName()); + if (!connection) { + onFailed(QString("No channel on %1 transport").arg(tr->pad()->ns())); + return; + } + waitConnected(); + } else { + app->transport()->addAcceptor( + features, [this, self = QPointer(this)](Connection::Ptr newConnection) { + if (!self || connection) + return false; + connection = newConnection; + if (connection->isOpen()) + onReady(); + else + waitConnected(); + return true; + }); + } + } + + ~ConnectionWaiter() { qDebug("~ConnectionWaiter"); } + }; + + //---------------------------------------------------------------------------- + // Application + //---------------------------------------------------------------------------- + ApplicationManager::ApplicationManager(QObject *parent) : QObject(parent) { } + QStringList ApplicationManager::ns() const { return discoFeatures(); } + + //---------------------------------------------------------------------------- + // Application + //---------------------------------------------------------------------------- + Application::Update Application::evaluateOutgoingUpdate() + { + _update = { Action::NoAction, Reason() }; + if (_state == State::Finished || _state == State::Created + || _pendingTransportReplace == PendingTransportReplace::NeedAck) + return _update; + + if (_terminationReason.isValid()) { + _update = { Action::ContentRemove, _terminationReason }; + return _update; + } + + // missing transport means it's an incoming application with invalid transport, + // but basically it shouldn't happen + if ((isRemote() && _state == State::Pending) || !_transport) { + return _update; + } + + bool inTrReplace = _pendingTransportReplace == PendingTransportReplace::InProgress; + + if (_transport->state() == State::Finished) { + if (inTrReplace && _transport->creator() != _pad->session()->role()) + _update = { Action::TransportReject, _transport->lastReason() }; + else + _update = { _transportSelector->hasMoreTransports() ? Action::TransportReplace : Action::ContentRemove, + _transport->lastReason() }; + return _update; + } + + switch (_state) { + case State::ApprovedToSend: + if (_transport->state() >= State::Accepted) { + _update + = { _pad->session()->role() == _creator ? Action::ContentAdd : Action::ContentAccept, Reason() }; + } else if (_transport->hasUpdates() && _transport->state() == State::ApprovedToSend) { + if (_pendingTransportReplace == PendingTransportReplace::Planned) { + _update = { Action::TransportReplace, _transportReplaceReason }; + } else if (inTrReplace) { // both sides already know it's replace. but not accepted yet. + _update = { _transport->isLocal() ? Action::TransportInfo : Action::TransportAccept, Reason() }; + } else + _update = { _pad->session()->role() == _creator ? Action::ContentAdd : Action::ContentAccept, + Reason() }; + } + break; + case State::Pending: + if (_creator != _pad->session()->role() && !inTrReplace && _transport->hasUpdates() + && _transport->state() == State::ApprovedToSend) { + // if remote transport has initial updates and it's not transport-replace then it's time to accept the + // content + _update = { Action::ContentAccept, Reason() }; + break; + } + // fallthrough + case State::Connecting: + if (inTrReplace) { + // for transport replace we handle just replace until it's finished + if (_transport->state() == State::Finished) { // 2nd transport failed shortly + _update = { _transportSelector->hasMoreTransports() + ? (_transport->isLocal() ? Action::TransportReplace : Action::TransportReject) + : Action::ContentRemove, + _transport->lastReason() }; + } else if (_transport->hasUpdates() && _transport->state() == State::ApprovedToSend) { + _update = { _transport->isLocal() ? Action::TransportInfo : Action::TransportAccept, Reason() }; + } + break; + } + + if (_transport->hasUpdates()) { + if (_transport->state() >= State::ApprovedToSend && _transport->state() < State::Finished) + _update = { _pendingTransportReplace == PendingTransportReplace::Planned ? Action::TransportReplace + : Action::TransportInfo, + Reason() }; + } else if (_transport->state() == State::Finished) { + _update = { _transportSelector->hasMoreTransports() ? Action::TransportReplace : Action::ContentRemove, + _transport->lastReason() }; + } + break; + case State::Active: + if (_transport->hasUpdates()) + _update = { Action::TransportInfo, Reason() }; + + break; + default: + break; + } + return _update; + } + + OutgoingUpdate Application::takeOutgoingUpdate() + { + + QDomElement transportEl; + OutgoingUpdateCB transportCB; + auto client = _pad->session()->manager()->client(); + auto doc = client->doc(); + + ContentBase cb(_creator, _contentName); + // we need to send senders for initial offer/answer + if (_state == State::ApprovedToSend) + cb.senders = _senders; + QList updates; + auto contentEl = cb.toXml(doc, "content"); + updates << contentEl; + + switch (_update.action) { + case Action::ContentReject: + case Action::ContentRemove: + if (_update.reason.isValid()) + updates << _update.reason.toXml(doc); + return OutgoingUpdate { updates, [this](bool) { setState(State::Finished); } }; + case Action::ContentAdd: + contentEl.appendChild(makeLocalOffer()); + std::tie(transportEl, transportCB) = wrapOutgoingTransportUpdate(); + contentEl.appendChild(transportEl); + + setState(State::Unacked); + return OutgoingUpdate { updates, [this, transportCB](Task *task) { + transportCB(task); + if (task->success()) + setState(State::Pending); + } }; + + case Action::ContentAccept: + contentEl.appendChild(makeLocalAnswer()); + std::tie(transportEl, transportCB) = wrapOutgoingTransportUpdate(true); + contentEl.appendChild(transportEl); + + setState(State::Unacked); + return OutgoingUpdate { updates, [this, transportCB](Task *task) { + transportCB(task); + if (task->success()) + setState(State::Connecting); + } }; + case Action::TransportInfo: + Q_ASSERT(_transport->hasUpdates()); + std::tie(transportEl, transportCB) = wrapOutgoingTransportUpdate(); + contentEl.appendChild(transportEl); + return OutgoingUpdate { updates, transportCB }; + case Action::TransportReplace: + Q_ASSERT(_transport->hasUpdates()); + std::tie(transportEl, transportCB) = wrapOutgoingTransportUpdate(); + contentEl.appendChild(transportEl); + if (_pendingTransportReplace == PendingTransportReplace::Planned) { + _pendingTransportReplace = PendingTransportReplace::NeedAck; + } + if (_update.reason.isValid()) + updates << _update.reason.toXml(doc); + return OutgoingUpdate { updates, [this, transportCB](Task *task) { + transportCB(task); + if (task->success()) + _pendingTransportReplace = PendingTransportReplace::InProgress; + // else transport will report failure from its callback => select next tran. + } }; + case Action::TransportAccept: + Q_ASSERT(_transport->hasUpdates()); + std::tie(transportEl, transportCB) = wrapOutgoingTransportUpdate(); + contentEl.appendChild(transportEl); + return OutgoingUpdate { updates, [this, transportCB](Task *task) { + transportCB(task); + if (task->success()) { + _pendingTransportReplace = PendingTransportReplace::None; + if (_state == State::Connecting || _state == State::Active) + _transport->start(); + } + // else transport will report failure from its callback => select next tran. + } }; + default: + break; + } + + return OutgoingUpdate(); // TODO + } + + OutgoingTransportInfoUpdate Application::wrapOutgoingTransportUpdate(bool ensureTransportElement) + { + QDomElement transportEl; + OutgoingUpdateCB transportCB; + std::tie(transportEl, transportCB) = _transport->takeOutgoingUpdate(ensureTransportElement); + auto wrapCB = [tr = _transport.toWeakRef(), cb = std::move(transportCB)](Task *task) { + auto transport = tr.lock(); + if (!transport) { + return; + } + if (cb) + cb(task); + }; + return OutgoingTransportInfoUpdate { transportEl, wrapCB }; + } + + void Application::expectSingleConnection(TransportFeatures features, std::function &&ready) + { + new ConnectionWaiter( + features, std::move(ready), + [this]() { + qDebug("Application::expectSingleConnection: stopping failed %s transport", + qPrintable(_transport->pad()->ns())); + _transport->stop(); + selectNextTransport(); + }, + this); + } + + bool Application::isRemote() const { return _pad->session()->role() != _creator; } + + bool Application::selectNextTransport(const QSharedPointer alikeTransport) + { + qDebug("selecting next transport"); + if (!_transportSelector->hasMoreTransports()) { + if (_transport) { + qDebug("Application::selectNextTransport: stopping %s transport", qPrintable(_transport->pad()->ns())); + _transport->disconnect(this); + _transport->stop(); + } + _state = (isRemote() || _state > State::ApprovedToSend) ? State::Finishing : State::Finished; + _terminationReason = Reason(Reason::FailedTransport); + emit updated(); // will be evaluated to content-remove + return false; + } + + if (alikeTransport) { + auto tr = _transportSelector->getAlikeTransport(alikeTransport); + if (tr && setTransport(tr)) + return true; + } + + QSharedPointer t; + while ((t = _transportSelector->getNextTransport())) + if (setTransport(t)) + return true; + + emit updated(); // will be evaluated to content-remove + return false; + } + + bool Application::wantBetterTransport(const QSharedPointer &t) const + { + if (!_transportSelector->hasTransport(t)) + return false; + + return !_transport || _transportSelector->compare(t, _transport) > 0; + } + + void Application::incomingTransportAccept(const QDomElement &el) + { + if (_pendingTransportReplace != PendingTransportReplace::InProgress) { + return; // ignore out of order + } + _pendingTransportReplace = PendingTransportReplace::None; + if (_transport->update(el) && _state >= State::Connecting) + _transport->start(); + } + + bool Application::isTransportReplaceEnabled() const { return true; } + + bool Application::setTransport(const QSharedPointer &transport, const Reason &reason) + { + if (!isTransportReplaceEnabled() || !_transportSelector->replace(_transport, transport)) + return false; + + // in case we automatically select a new transport on our own we definitely will come up to this point + if (_transport) { + if (_transport->state() < State::Unacked && _transport->creator() == _pad->session()->role() + && _transport->pad()->ns() != transport->pad()->ns()) { + // the transport will be reused later since the remote doesn't know about it yet + _transportSelector->backupTransport(_transport); + } + + if (transport->isLocal()) { + auto ts = _transport->state() == State::Finished ? _transport->prevState() : _transport->state(); + if (_transport->isRemote() || ts > State::Unacked) { + // if remote knows of the current transport + _pendingTransportReplace = PendingTransportReplace::Planned; + } else if (_transport->isLocal() && ts == State::Unacked) { + // if remote may know but we don't know yet about it + _pendingTransportReplace = PendingTransportReplace::NeedAck; + } + } else { + _pendingTransportReplace = PendingTransportReplace::InProgress; + } + + if (_pendingTransportReplace != PendingTransportReplace::None) { + if (_transport->state() == State::Finished) { // initiate replace? + _transportReplaceReason = reason.isValid() ? reason : _transport->lastReason(); + } else { + _transportReplaceReason = reason; + } + } + qDebug("Application::setTransport: resetting %s transport in favor of %s", + qPrintable(_transport->pad()->ns()), qPrintable(transport->pad()->ns())); + _transport->disconnect(this); + _transport.reset(); + } else { + qDebug("setting transport %s", qPrintable(transport->pad()->ns())); + } + + _transport = transport; + + connect(_transport.data(), &Transport::updated, this, &Application::updated); + connect(_transport.data(), &Transport::failed, this, [this]() { selectNextTransport(); }); + + if (_transport && _transport->state() < State::Finishing && _state >= State::ApprovedToSend) { + QTimer::singleShot(0, this, [this, wp = _transport.toWeakRef()]() { + auto p = wp.lock(); + if (p && p == _transport) { + prepareTransport(); + } + }); + } + + return true; + } + + bool ApplicationManagerPad::incomingSessionInfo(const QDomElement &) { return false; /* unsupported by default */ } + +}} + +#include "jingle-application.moc" diff --git a/src/xmpp/xmpp-im/jingle-application.h b/src/xmpp/xmpp-im/jingle-application.h new file mode 100644 index 00000000..3b0edbbc --- /dev/null +++ b/src/xmpp/xmpp-im/jingle-application.h @@ -0,0 +1,232 @@ +/* + * jignle-application.h - Base Jingle application classes + * Copyright (C) 2019 Sergey Ilinykh + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library. If not, see . + * + */ + +#ifndef JINGLE_APPLICATION_H +#define JINGLE_APPLICATION_H + +#include "jingle-transport.h" + +class QTimer; + +namespace XMPP { namespace Jingle { + + class ApplicationManager; + class ApplicationManagerPad : public SessionManagerPad { + Q_OBJECT + public: + typedef QSharedPointer Ptr; + + using SessionManagerPad::SessionManagerPad; + + virtual ApplicationManager *manager() const = 0; + + /* + * for example we transfer a file + * then first file may generate name "file1", next "file2" etc + * As result it will be sent as + */ + virtual QString generateContentName(Origin senders) = 0; + + virtual bool incomingSessionInfo(const QDomElement &el); + }; + + // Represents a session for single application. for example a single file in a file transfer session. + // There maybe multiple application instances in a session. + // It's designed as QObject to exposed to JavaScript (qml/webkit) + class Application : public QObject { + Q_OBJECT + public: + struct Update { + Action action; + Reason reason; + }; + + enum SetDescError { + Ok, + Unparsed, + IncompatibleParameters // this one is for + }; + + enum ApplicationFlag { + InitialApplication = 0x1, // the app came with session-initiate + UserFlag = 0x100 + }; + Q_DECLARE_FLAGS(ApplicationFlags, ApplicationFlag) + + virtual void setState(State state) = 0; // likely just remember the state and not generate any signals + virtual XMPP::Stanza::Error lastError() const = 0; + virtual Reason lastReason() const = 0; + + inline ApplicationManagerPad::Ptr pad() const { return _pad; } + inline State state() const { return _state; } + inline Origin creator() const { return _creator; } + inline Origin senders() const { return _senders; } + inline QString contentName() const { return _contentName; } + inline QSharedPointer transport() const { return _transport; } + inline TransportSelector *transportSelector() const { return _transportSelector.get(); } + bool isRemote() const; + inline bool isLocal() const { return !isRemote(); } + inline ApplicationFlags flags() const { return _flags; } + inline void markInitialApplication(bool state) + { + if (state) + _flags |= InitialApplication; + else + _flags &= ~InitialApplication; + } + + virtual SetDescError setRemoteOffer(const QDomElement &description) = 0; + virtual SetDescError setRemoteAnswer(const QDomElement &description) = 0; + virtual QDomElement makeLocalOffer() = 0; + virtual QDomElement makeLocalAnswer() = 0; + + /** + * @brief evaluateOutgoingUpdate computes and prepares next update which will be taken with takeOutgoingUpdate + * The updated will be taked immediately if considered to be most preferred among other updates types of + * other applications. + * @return update type + */ + virtual Update evaluateOutgoingUpdate(); + // this may return something only when evaluateOutgoingUpdate() != NoAction + virtual OutgoingUpdate takeOutgoingUpdate(); + + /** + * @brief setTransport checks if transport is compatible and stores it + * @param transport + * @return false if not compatible + */ + bool setTransport(const QSharedPointer &transport, const Reason &reason = Reason()); + + /** + * @brief selectNextTransport selects next transport from compatible transports list. + * The list is usually stored in the application + * @return + */ + bool selectNextTransport(const QSharedPointer alikeTransport = QSharedPointer()); + + /** + * @brief Checks where transport-replace is possible atm + * @return + */ + virtual bool isTransportReplaceEnabled() const; + + /** + * @brief wantBetterTransport checks if the transport is a better match for the application + * Used in content is provided twice with two different transports + * @return + */ + virtual bool wantBetterTransport(const QSharedPointer &) const; + + /** + * @brief prepare to send content-add/session-initiate + * When ready, the application first set update type to ContentAdd and then emit updated() + */ + virtual void prepare() = 0; + virtual void start() = 0; + virtual void remove(Reason::Condition cond = Reason::Success, const QString &comment = QString()) = 0; + + virtual void incomingRemove(const Reason &r) = 0; + void incomingTransportAccept(const QDomElement &el); + + protected: + /** + * @brief wraps transport update so transport can be safely-deleted before callback is triggered + */ + OutgoingTransportInfoUpdate wrapOutgoingTransportUpdate(bool ensureTransportElement = false); + + /** + * @brief initTransport in general connects any necessary for the application transport signals + */ + virtual void prepareTransport() = 0; + + void expectSingleConnection(TransportFeatures features, std::function &&ready); + + signals: + void updated(); // signal for session it has to send updates to remote. so it will follow with + // takeOutgoingUpdate() eventually + void stateChanged(State); + + protected: + State _state = State::Created; + ApplicationFlags _flags; + + enum class PendingTransportReplace { + None, // not in the replace mode + Planned, // didn't send a replacement yet. working on it. + NeedAck, // we sent replacement. waiting for iq ack + InProgress // not yet accepted but acknowledged + }; + + // has to be set when whatever way remote knows about the current transport + // bool _remoteKnowsOfTheTransport = false; + + // per session object responsible for all applications of this type + QSharedPointer _pad; + + // content properties as come from the request + QString _contentName; + Origin _creator; + Origin _senders; + + // current transport. either local or remote. has info about origin and state + QSharedPointer _transport; + std::unique_ptr _transportSelector; + + // if transport-replace is in progress. will be set to true when accepted by both sides. + PendingTransportReplace _pendingTransportReplace = PendingTransportReplace::None; + + // while it's valid - we are in unaccepted yet transport-replace + Reason _transportReplaceReason; + + // when set the content will be removed with this reason + Reason _terminationReason; + + // evaluated update to be sent + Update _update; + + QTimer *transportInitTimer = nullptr; + }; + + inline bool operator<(const Application::Update &a, const Application::Update &b) + { + return a.action < b.action || (a.action == b.action && a.reason.condition() < b.reason.condition()); + } + + class ApplicationManager : public QObject { + Q_OBJECT + public: + ApplicationManager(QObject *parent = nullptr); + + virtual void setJingleManager(Manager *jm) = 0; + virtual Application *startApplication(const ApplicationManagerPad::Ptr &pad, const QString &contentName, + Origin creator, Origin senders) + = 0; + virtual ApplicationManagerPad *pad(Session *session) = 0; + + // this method is supposed to gracefully close all related sessions as a preparation for plugin unload for + // example + virtual void closeAll(const QString &ns = QString()) = 0; + + virtual QStringList ns() const; + virtual QStringList discoFeatures() const = 0; + }; + +}} + +#endif diff --git a/src/xmpp/xmpp-im/jingle-connection.cpp b/src/xmpp/xmpp-im/jingle-connection.cpp new file mode 100644 index 00000000..a030c890 --- /dev/null +++ b/src/xmpp/xmpp-im/jingle-connection.cpp @@ -0,0 +1,57 @@ +/* + * jignle-connection.cpp - Jingle Connection - minimal data transfer unit for an application + * Copyright (C) 2021 Sergey Ilinykh + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library. If not, see . + * + */ + +#include "jingle-connection.h" + +namespace XMPP { namespace Jingle { + + bool Connection::hasPendingDatagrams() const { return false; } + + NetworkDatagram Connection::readDatagram(qint64 maxSize) + { + qCritical("Calling unimplemented function receiveDatagram"); + Q_UNUSED(maxSize) + return NetworkDatagram(); + } + + bool Connection::writeDatagram(const NetworkDatagram &) + { + qCritical("Calling unimplemented function sendDatagram"); + return false; + } + + qint64 Connection::writeData(const char *, qint64) + { + qCritical("Calling unimplemented function writeData"); + return 0; + } + + qint64 Connection::readData(char *, qint64) + { + qCritical("Calling unimplemented function readData"); + return 0; + } + + size_t Connection::blockSize() const + { + return 0; // means "block" is not applicable for this kind of connection + } + + int Connection::component() const { return 0; } +}} diff --git a/src/xmpp/xmpp-im/jingle-connection.h b/src/xmpp/xmpp-im/jingle-connection.h new file mode 100644 index 00000000..486026d1 --- /dev/null +++ b/src/xmpp/xmpp-im/jingle-connection.h @@ -0,0 +1,94 @@ +/* + * jignle-connection.h - Jingle Connection - minimal data transfer unit for an application + * Copyright (C) 2021 Sergey Ilinykh + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library. If not, see . + * + */ + +#ifndef JINGLE_CONNECTION_H +#define JINGLE_CONNECTION_H + +/** + * A transport may have multiple connections. + * For example an ICE transport may have up to 65537 connections (65535 data/sctp-channels + 2 raw) + */ + +#include "iris/bytestream.h" +#include "jingle.h" + +#if QT_VERSION >= QT_VERSION_CHECK(5, 8, 0) +#include +#else +#include +#endif + +namespace XMPP { namespace Jingle { +#if QT_VERSION < QT_VERSION_CHECK(5, 8, 0) + // stub implementation + class NetworkDatagram { + public: + bool _valid = false; + QByteArray _data; + inline NetworkDatagram(const QByteArray &data, const QHostAddress &destinationAddress = QHostAddress(), + quint16 port = 0) : _valid(true), _data(data) + { + Q_UNUSED(destinationAddress); + Q_UNUSED(port) + } + inline NetworkDatagram() { } + + inline bool isValid() const { return _valid; } + inline QByteArray data() const { return _data; } + }; +#else + typedef QNetworkDatagram NetworkDatagram; +#endif + + class Connection : public ByteStream { + Q_OBJECT + public: + using Ptr = QSharedPointer; // will be shared between transport and application + virtual bool hasPendingDatagrams() const; + virtual NetworkDatagram readDatagram(qint64 maxSize = -1); + virtual bool writeDatagram(const NetworkDatagram &data); + virtual size_t blockSize() const; + virtual int component() const; + virtual TransportFeatures features() const = 0; + + inline void setId(const QString &id) { _id = id; } + inline bool isRemote() const { return _isRemote; } + inline void setRemote(bool value) { _isRemote = value; } + + signals: + void connected(); + void disconnected(); + + protected: + qint64 writeData(const char *data, qint64 maxSize); + qint64 readData(char *data, qint64 maxSize); + + bool _isRemote = false; + QString _id; + }; + + using ConnectionAcceptorCallback = std::function; + struct ConnectionAcceptor { + TransportFeatures features; + ConnectionAcceptorCallback callback; + int componentIndex; + }; +}} + +#endif // JINGLE_CONNECTION_H diff --git a/src/xmpp/xmpp-im/jingle-file.cpp b/src/xmpp/xmpp-im/jingle-file.cpp new file mode 100644 index 00000000..29724e3f --- /dev/null +++ b/src/xmpp/xmpp-im/jingle-file.cpp @@ -0,0 +1,397 @@ +/* + * jignle-file.h - Jingle file usually used in file transfer + * Copyright (C) 2019 Sergey Ilinykh + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library. If not, see . + * + */ + +#include "jingle-file.h" + +#include "xmpp_xmlcommon.h" + +#include +#include +#include +#include + +namespace XMPP::Jingle::FileTransfer { + +const QString NS = QStringLiteral("urn:xmpp:jingle:apps:file-transfer:5"); + +static const QString THUMBNAIL_TAG = QStringLiteral("thumbnail"); +static const QString RANGE_TAG = QStringLiteral("range"); +static const QString DATE_TAG = QStringLiteral("date"); +static const QString DESC_TAG = QStringLiteral("desc"); +static const QString MEDIA_TYPE_TAG = QStringLiteral("media-type"); +static const QString NAME_TAG = QStringLiteral("name"); +static const QString SIZE_TAG = QStringLiteral("size"); +static const QString FILETAG = QStringLiteral("file"); +static const QString AMPLITUDES_TAG = QStringLiteral("amplitudes"); + +const QString AMPLITUDES_NS = QStringLiteral("urn:audio:amplitudes"); + +QDomElement Range::toXml(QDomDocument *doc) const +{ + auto r = doc->createElement(RANGE_TAG); + if (length) { + r.setAttribute(QStringLiteral("length"), QString::number(length)); + } + if (offset) { + r.setAttribute(QStringLiteral("offset"), QString::number(offset)); + } + for (auto const &h : hashes) { + auto hel = h.toXml(doc); + if (!hel.isNull()) { + r.appendChild(hel); + } + } + return r; +} + +//---------------------------------------------------------------------------- +// File +//---------------------------------------------------------------------------- +class File::Private : public QSharedData { +public: + bool rangeSupported = false; + bool hasSize = false; + QDateTime date; + QString mediaType; + QString name; + QString desc; + std::uint64_t size = 0; + Range range; + QList hashes; + Thumbnail thumbnail; + QByteArray amplitudes; +}; + +File::File() { } + +File::~File() { } + +File &File::operator=(const File &other) +{ + d = other.d; + return *this; +} + +File::File(const File &other) : d(other.d) { } + +File::File(const QDomElement &file) +{ + QDateTime date; + QString mediaType; + QString name; + QString desc; + std::uint64_t size = 0; + bool rangeSupported = false; + bool hasSize = false; + Range range; + QList hashes; + Thumbnail thumbnail; + QByteArray amplitudes; + + bool ok; + + for (QDomElement ce = file.firstChildElement(); !ce.isNull(); ce = ce.nextSiblingElement()) { + + if (ce.tagName() == DATE_TAG) { + date = QDateTime::fromString(ce.text().left(19), Qt::ISODate); + if (!date.isValid()) { + return; + } + + } else if (ce.tagName() == MEDIA_TYPE_TAG) { + mediaType = ce.text(); + + } else if (ce.tagName() == NAME_TAG) { + name = ce.text(); + + } else if (ce.tagName() == SIZE_TAG) { + size = ce.text().toULongLong(&ok); + if (!ok) { + return; + } + hasSize = true; + + } else if (ce.tagName() == RANGE_TAG) { + if (ce.hasAttribute(QLatin1String("offset"))) { + range.offset = ce.attribute(QLatin1String("offset")).toLongLong(&ok); + if (!ok || range.offset < 0) { + return; + } + } + if (ce.hasAttribute(QLatin1String("length"))) { + range.length = ce.attribute(QLatin1String("length")).toLongLong(&ok); + if (!ok || range.length <= 0) { // length should absent if we need to read till end of file. + // 0-length is nonsense + return; + } + } + QDomElement hashEl = ce.firstChildElement(QLatin1String("hash")); + for (; !hashEl.isNull(); hashEl = hashEl.nextSiblingElement(QLatin1String("hash"))) { + if (hashEl.namespaceURI() == HASH_NS) { + auto hash = Hash(hashEl); + if (hash.type() == Hash::Type::Unknown) { + continue; + } + range.hashes.append(hash); + } + } + rangeSupported = true; + + } else if (ce.tagName() == DESC_TAG) { + desc = ce.text(); + + } else if (ce.tagName() == QLatin1String("hash")) { + if (ce.namespaceURI() == HASH_NS) { + Hash h(ce); + if (h.type() == Hash::Type::Unknown) { + return; + } + hashes.append(h); + } + + } else if (ce.tagName() == QLatin1String("hash-used")) { + if (ce.namespaceURI() == HASH_NS) { + Hash h(ce); + if (h.type() == Hash::Type::Unknown) { + return; + } + hashes.append(h); + } + + } else if (ce.tagName() == THUMBNAIL_TAG) { + thumbnail = Thumbnail(ce); + } else if (ce.tagName() == AMPLITUDES_TAG && ce.namespaceURI() == AMPLITUDES_NS) { + amplitudes = QByteArray::fromBase64(ce.text().toLatin1()); + } + } + + auto p = new Private; + p->date = date; + p->mediaType = mediaType; + p->name = name; + p->desc = desc; + p->size = size; + p->rangeSupported = rangeSupported; + p->hasSize = hasSize; + p->range = range; + p->hashes = hashes; + p->thumbnail = thumbnail; + p->amplitudes = amplitudes; + + d = p; +} + +QDomElement File::toXml(QDomDocument *doc) const +{ + if (!isValid() || d->hashes.isEmpty()) { + return QDomElement(); + } + QDomElement el = doc->createElementNS(NS, QStringLiteral("file")); + if (d->date.isValid()) { + el.appendChild(XMLHelper::textTag(*doc, DATE_TAG, d->date.toString(Qt::ISODate))); + } + if (d->desc.size()) { + el.appendChild(XMLHelper::textTag(*doc, DESC_TAG, d->desc)); + } + for (const auto &h : d->hashes) { + el.appendChild(h.toXml(doc)); + } + if (d->mediaType.size()) { + el.appendChild(XMLHelper::textTag(*doc, MEDIA_TYPE_TAG, d->mediaType)); + } + if (d->name.size()) { + el.appendChild(XMLHelper::textTag(*doc, NAME_TAG, d->name)); + } + if (d->hasSize) { + el.appendChild(XMLHelper::textTag(*doc, SIZE_TAG, qint64(d->size))); + } + if (d->rangeSupported || d->range.isValid()) { + el.appendChild(d->range.toXml(doc)); + } + if (d->thumbnail.isValid()) { + el.appendChild(d->thumbnail.toXml(doc)); + } + if (d->amplitudes.size()) { + el.appendChild(XMLHelper::textTagNS(doc, AMPLITUDES_NS, AMPLITUDES_TAG, d->amplitudes)); + } + return el; +} + +bool File::merge(const File &other) +{ + if (!d->thumbnail.isValid()) { + d->thumbnail = other.thumbnail(); + } + for (auto const &h : other.d->hashes) { + auto it = std::find_if(d->hashes.constBegin(), d->hashes.constEnd(), + [&h](auto const &v) { return h.type() == v.type(); }); + if (it == d->hashes.constEnd()) { + d->hashes.append(h); + } else if (h.data() != it->data()) { + return false; // hashes are different + } + } + return true; +} + +bool File::hasComputedHashes() const +{ + if (!d) + return false; + for (auto const &h : d->hashes) { + if (h.data().size()) + return true; + } + return false; +} + +bool File::hasSize() const { return d->hasSize; } + +QDateTime File::date() const { return d ? d->date : QDateTime(); } + +QString File::description() const { return d ? d->desc : QString(); } + +QList File::hashes() const { return d ? d->hashes : QList(); } +QList File::computedHashes() const +{ + QList ret; + if (!d) + return ret; + for (auto const &h : d->hashes) { + if (h.data().size()) + ret.append(h); + } + return ret; +} + +Hash File::hash(Hash::Type t) const +{ + if (d && d->hashes.count()) { + if (t == Hash::Unknown) + return d->hashes.at(0); + for (auto const &h : d->hashes) { + if (h.type() == t) { + return h; + } + } + } + return Hash(); +} + +QString File::mediaType() const { return d ? d->mediaType : QString(); } + +QString File::name() const { return d ? d->name : QString(); } + +std::uint64_t File::size() const { return d ? d->size : 0; } + +Range File::range() const { return d ? d->range : Range(); } + +Thumbnail File::thumbnail() const { return d ? d->thumbnail : Thumbnail(); } + +QByteArray File::amplitudes() const { return d ? d->amplitudes : QByteArray(); } + +void File::setDate(const QDateTime &date) { ensureD()->date = date; } + +void File::setDescription(const QString &desc) { ensureD()->desc = desc; } + +void File::addHash(const Hash &hash) { ensureD()->hashes.append(hash); } + +void File::setHashes(const QList &hashes) { ensureD()->hashes = hashes; } + +void File::setMediaType(const QString &mediaType) { ensureD()->mediaType = mediaType; } + +void File::setName(const QString &name) { ensureD()->name = name; } + +void File::setSize(std::uint64_t size) +{ + ensureD()->size = size; + d->hasSize = true; +} + +void File::setRange(const Range &range) +{ + ensureD()->range = range; + d->rangeSupported = true; +} + +void File::setThumbnail(const Thumbnail &thumb) { ensureD()->thumbnail = thumb; } + +void File::setAmplitudes(const QByteArray &litudes) { d->amplitudes = amplitudes; } + +File::Private *File::ensureD() +{ + if (!d) { + d = new Private; + } + return d.data(); +} + +//---------------------------------------------------------------------------- +// FileHasher +//---------------------------------------------------------------------------- +class FileHasher::Private { +public: + QThread thread; + StreamHash streamHash; + Hash result; + + Private(Hash::Type hashType) : streamHash(hashType) { } +}; + +FileHasher::FileHasher(Hash::Type type) : d(new Private(type)) +{ + QSemaphore sem; + moveToThread(&d->thread); + QObject::connect(&d->thread, &QThread::started, this, [&sem]() { sem.release(); }); + d->thread.start(); + sem.acquire(); +} + +FileHasher::~FileHasher() +{ + if (d->thread.isRunning()) { + addData(); // ensure exit called + d->thread.wait(); + } +} + +void FileHasher::addData(const QByteArray &data) +{ + QTimer::singleShot(0, this, [data, this]() { + // executed in a thread + d->streamHash.addData(data); + if (data.isEmpty()) { + d->result = d->streamHash.final(); + thread()->exit(); + } + }); + if (data.isEmpty()) + d->thread.wait(); +} + +Hash FileHasher::result() +{ + if (d->thread.isRunning()) { + addData(); // ensure exit called + d->thread.wait(); + } + return d->result; +} + +} diff --git a/src/xmpp/xmpp-im/jingle-file.h b/src/xmpp/xmpp-im/jingle-file.h new file mode 100644 index 00000000..9bcb0dfd --- /dev/null +++ b/src/xmpp/xmpp-im/jingle-file.h @@ -0,0 +1,103 @@ +/* + * jignle-file.h - Jingle file usually used in file transfer + * Copyright (C) 2019 Sergey Ilinykh + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library. If not, see . + * + */ + +#ifndef XMPP_JINGLE_FILETRANSFER_FILE_H +#define XMPP_JINGLE_FILETRANSFER_FILE_H + +#include "xmpp_hash.h" +#include "xmpp_thumbs.h" + +#include +#include + +namespace XMPP::Jingle::FileTransfer { +struct Range { + std::int64_t offset = 0; + std::int64_t length = 0; // 0 - from offset to the end of the file + QList hashes; + + inline Range() { } + inline Range(std::int64_t offset, std::int64_t length) : offset(offset), length(length) { } + inline bool isValid() const { return hashes.size() || offset || length; } + inline operator bool() const { return isValid(); } + QDomElement toXml(QDomDocument *doc) const; +}; + +class File { +public: + File(); + File(const File &other); + File(const QDomElement &file); + ~File(); + File &operator=(const File &other); + inline bool isValid() const { return d != nullptr; } + QDomElement toXml(QDomDocument *doc) const; + bool merge(const File &other); + bool hasComputedHashes() const; + bool hasSize() const; + + QDateTime date() const; + QString description() const; + QList hashes() const; + QList computedHashes() const; + Hash hash(Hash::Type t = Hash::Unknown) const; + QString mediaType() const; + QString name() const; + uint64_t size() const; + Range range() const; + Thumbnail thumbnail() const; + QByteArray amplitudes() const; + + void setDate(const QDateTime &date); + void setDescription(const QString &desc); + void addHash(const Hash &hash); + void setHashes(const QList &hashes); + void setMediaType(const QString &mediaType); + void setName(const QString &name); + void setSize(uint64_t size); + void setRange(const Range &range = Range()); // default empty just to indicate it's supported + void setThumbnail(const Thumbnail &thumb); + void setAmplitudes(const QByteArray &litudes); + +private: + class Private; + Private *ensureD(); + QSharedDataPointer d; +}; + +class FileHasher : public QObject { + Q_OBJECT +public: + FileHasher(Hash::Type type); + ~FileHasher(); + + /** + * @brief addData add next portion of data for hash computation. + * @param data to be added to hash function. if empty it will signal hashing thread to exit + */ + void addData(const QByteArray &data = QByteArray()); + Hash result(); + +private: + class Private; + std::unique_ptr d; +}; +} + +#endif // XMPP_JINGLE_FILETRANSFER_FILE_H diff --git a/src/xmpp/xmpp-im/jingle-ft.cpp b/src/xmpp/xmpp-im/jingle-ft.cpp index db291459..b63a60b7 100644 --- a/src/xmpp/xmpp-im/jingle-ft.cpp +++ b/src/xmpp/xmpp-im/jingle-ft.cpp @@ -18,615 +18,741 @@ */ #include "jingle-ft.h" +#include "jingle-nstransportslist.h" +#include "jingle-session.h" + #include "xmpp_client.h" -#include "xmpp_thumbs.h" #include "xmpp_hash.h" +#include "xmpp_thumbs.h" #include "xmpp_xmlcommon.h" -namespace XMPP { -namespace Jingle { +#if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0) +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace std::chrono_literals; + +namespace XMPP { namespace Jingle { namespace FileTransfer { + + const QString NS = QStringLiteral("urn:xmpp:jingle:apps:file-transfer:5"); + constexpr auto FINALIZE_TIMEOUT = 30s; + + // tags + static const QString CHECKSUM_TAG = QStringLiteral("checksum"); + static const QString RECEIVED_TAG = QStringLiteral("received"); + + //---------------------------------------------------------------------------- + // Checksum + //---------------------------------------------------------------------------- + Checksum::Checksum(const QDomElement &cs) : ContentBase(cs) + { + file = File(cs.firstChildElement(QLatin1String("file"))); + } -namespace FileTransfer { + bool Checksum::isValid() const { return ContentBase::isValid() && file.isValid(); } -const QString NS = QStringLiteral("urn:xmpp:jingle:apps:file-transfer:5"); + QDomElement Checksum::toXml(QDomDocument *doc) const + { + auto el = ContentBase::toXml(doc, "checksum", NS); + if (!el.isNull()) { + el.appendChild(file.toXml(doc)); + } + return el; + } + + //---------------------------------------------------------------------------- + // Received + //---------------------------------------------------------------------------- + QDomElement Received::toXml(QDomDocument *doc) const { return ContentBase::toXml(doc, RECEIVED_TAG, NS); } + //---------------------------------------------------------------------------- + // ApplicationManager + //---------------------------------------------------------------------------- + Manager::Manager(QObject *parent) : XMPP::Jingle::ApplicationManager(parent) { } -QDomElement Range::toXml(QDomDocument *doc) const -{ - auto r = doc->createElement(QStringLiteral("range")); - if (length) { - r.setAttribute(QStringLiteral("length"), QString::number(length)); + Manager::~Manager() + { + if (jingleManager) + jingleManager->unregisterApp(NS); } - if (offset) { - r.setAttribute(QStringLiteral("length"), QString::number(length)); + + void Manager::setJingleManager(XMPP::Jingle::Manager *jm) { jingleManager = jm; } + + Application *Manager::startApplication(const ApplicationManagerPad::Ptr &pad, const QString &contentName, + Origin creator, Origin senders) + { + if (!(contentName.size() > 0 && (senders == Origin::Initiator || senders == Origin::Responder))) { + qDebug("Invalid Jignle FT App start parameters"); + return nullptr; + } + return new Application(pad.staticCast(), contentName, creator, senders); // ContentOrigin::Remote } - auto h = hash.toXml(doc); - if (!h.isNull()) { - r.appendChild(h); + + ApplicationManagerPad *Manager::pad(Session *session) { return new Pad(this, session); } + + void Manager::closeAll(const QString &) { } + + QStringList Manager::discoFeatures() const { return { NS }; } + + Client *Manager::client() + { + if (jingleManager) { + return jingleManager->client(); + } + return nullptr; } - return r; -} - -//---------------------------------------------------------------------------- -// File -//---------------------------------------------------------------------------- -class File::Private : public QSharedData -{ -public: - QDateTime date; - QString mediaType; - QString name; - QString desc; - quint64 size = 0; - Range range; - bool rangeSupported = false; - Hash hash; - Thumbnail thumbnail; -}; - -File::File() -{ - -} - -File::~File() -{ - -} - -File::File(const File &other) : - d(other.d) -{ - -} - -File::File(const QDomElement &file) -{ - QDateTime date; - QString mediaType; - QString name; - QString desc; - size_t size = 0; - bool rangeSupported = false; - Range range; - Hash hash; - Thumbnail thumbnail; - - bool ok; - - for(QDomElement ce = file.firstChildElement(); - !ce.isNull(); ce = ce.nextSiblingElement()) { - - if (ce.tagName() == QLatin1String("date")) { - date = QDateTime::fromString(ce.text().left(19), Qt::ISODate); - if (!date.isValid()) { - return; + + QStringList Manager::availableTransports() const + { + return jingleManager->availableTransports(TransportFeature::Reliable | TransportFeature::Ordered + | TransportFeature::DataOriented); + } + + //---------------------------------------------------------------------------- + // Application + //---------------------------------------------------------------------------- + class Application::Private { + public: + struct TransportDesc { + Origin creator = Origin::None; + State state = State::Created; + QSharedPointer transport; + }; + + Application *q = nullptr; + + Reason updateReason; + // Action updateToSend = Action::NoAction; + bool closeDeviceOnFinish = true; + bool streamingMode = false; + bool endlessRange = false; // where range in accepted file doesn't have end + bool outgoingReceived = false; + File file; + File acceptFile; // as it comes with "accept" response + XMPP::Stanza::Error lastError; + Reason lastReason; + Connection::Ptr connection; + QIODevice *device = nullptr; + qint64 bytesLeft = 0; + QList outgoingChecksum; + QList incomingChecksum; + QTimer *finalizeTimer = nullptr; + FileHasher *hasher = nullptr; + + void setState(State s) + { + q->_state = s; + if (s == State::Finished) { + if (device && closeDeviceOnFinish) { + device->close(); + } + if (connection) { + connection->close(); + } + if (q->transport()) + q->disconnect(q->transport().data(), &Transport::updated, q, nullptr); + } + if (s >= State::Finishing && q->transport()) { + q->disconnect(q->transport().data(), &Transport::failed, q, nullptr); + // we can still try to send transport updates } + emit q->stateChanged(s); + } + + void onReceived() + { + lastReason = Reason(Reason::Condition::Success); + setState(State::Finished); + } - } else if (ce.tagName() == QLatin1String("media-type")) { - mediaType = ce.text(); + void handleStreamFail() + { + lastReason = Reason(Reason::Condition::FailedApplication, QString::fromLatin1("stream failed")); + setState(State::Finished); + } - } else if (ce.tagName() == QLatin1String("name")) { - name = ce.text(); + void expectReceived() + { + qDebug("waiting for "); + expectFinalize([this]() { + qDebug("Waiting for timed out. But likely succeeded anyway"); + onReceived(); + }); + } - } else if (ce.tagName() == QLatin1String("size")) { - size = ce.text().toULongLong(&ok); - if (!ok) { + void expectFinalize(std::function &&timeoutCallback) + { + if (finalizeTimer || q->state() == State::Finished) return; + finalizeTimer = new QTimer(q); + finalizeTimer->setSingleShot(true); + finalizeTimer->setInterval(FINALIZE_TIMEOUT); + q->connect(finalizeTimer, &QTimer::timeout, q, timeoutCallback); + } + + void setDevice(QIODevice *dev, bool closeOnFinish) + { + device = dev; + closeDeviceOnFinish = closeOnFinish; + if (file.hash().isValid() && file.hash().data().isEmpty() && file.range().hashes.isEmpty()) { + // no precomputated hashes + hasher = new FileHasher(file.hash().type()); } + if (q->senders() == q->pad()->session()->role()) { + writeNextBlockToTransport(); + } else { + readNextBlockFromTransport(); + } + } - } else if (ce.tagName() == QLatin1String("range")) { - if (ce.hasAttribute(QLatin1String("offset"))) { - range.offset = ce.attribute(QLatin1String("offset")).toULongLong(&ok); - if (!ok) { - return; + void writeNextBlockToTransport() + { + if (!(endlessRange || bytesLeft)) { + if (hasher) { + auto hash = hasher->result(); + if (hash.isValid()) { + outgoingChecksum << hash; + emit q->updated(); + return; + } } + expectReceived(); + return; // everything is written } - if (ce.hasAttribute(QLatin1String("length"))) { - range.offset = ce.attribute(QLatin1String("length")).toULongLong(&ok); - if (!ok) { - return; + auto sz = qint64(connection->blockSize()); + sz = sz ? sz : 8192; + if (!endlessRange && sz > bytesLeft) { + sz = bytesLeft; + } + QByteArray data; + if (device->isSequential()) { + if (!device->bytesAvailable()) + return; // we will come back on readyRead + data = device->read(qMin(qint64(sz), device->bytesAvailable())); + } else { + data = device->read(sz); + } + if (data.isEmpty()) { + if (endlessRange) { + lastReason = Reason(Reason::Condition::Success); + if (hasher) { + auto hash = hasher->result(); + if (hash.isValid()) { + outgoingChecksum << hash; + emit q->updated(); + return; + } + } + setState(State::Finished); + } else { + handleStreamFail(); } + return; + } + // qDebug("JINGLE-FT write %d bytes to connection", data.size()); + if (hasher) { + hasher->addData(data); } - QDomElement hashEl = ce.firstChildElement(QLatin1String("hash")); - if (hashEl.attribute(QStringLiteral("xmlns")) == QLatin1String("urn:xmpp:hashes:2")) { - range.hash = Hash(hashEl); - if (range.hash.type() == Hash::Type::Unknown) { + if (connection->features() & TransportFeature::MessageOriented) { + if (!connection->writeDatagram(data)) { + handleStreamFail(); + return; + } + } else { + if (connection->write(data) == -1) { + handleStreamFail(); return; } } - rangeSupported = true; - - } else if (ce.tagName() == QLatin1String("desc")) { - desc = ce.text(); + emit q->progress(device->pos()); + bytesLeft -= data.size(); + } - } else if (ce.tagName() == QLatin1String("hash")) { - if (ce.attribute(QStringLiteral("xmlns")) == QLatin1String(XMPP_HASH_NS)) { - hash = Hash(ce); - if (hash.type() == Hash::Type::Unknown) { + void readNextBlockFromTransport() + { + qint64 bytesAvail; + while (bytesLeft && ((bytesAvail = connection->bytesAvailable()) || (connection->hasPendingDatagrams()))) { + QByteArray data; + if (connection->features() & TransportFeature::MessageOriented) { + data = connection->readDatagram().data(); + } else { + qint64 sz = 65536; // shall we respect transport->blockSize() ? + if (sz > bytesLeft) { + sz = bytesLeft; + } + if (sz > bytesAvail) { + sz = bytesAvail; + } + data = connection->read(sz); + } + // qDebug("JINGLE-FT read %d bytes from connection", data.size()); + if (data.isEmpty()) { + handleStreamFail(); + return; + } + if (hasher) { + hasher->addData(data); + } + if (device->write(data) == -1) { + handleStreamFail(); return; } + emit q->progress(device->pos()); + bytesLeft -= data.size(); + } + if (!bytesLeft) { + tryFinalizeIncoming(); } + } + + bool amISender() const { return q->senders() == q->pad()->session()->role(); } + bool amIReceiver() const { return q->senders() != q->pad()->session()->role(); } + + void onConnectionConnected(Connection::Ptr newConnection) + { + qDebug("jingle-ft: connected. ready to send user data"); + connection = newConnection; + lastReason = Reason(); + lastError.reset(); - } else if (ce.tagName() == QLatin1String("hash-used")) { - if (ce.attribute(QStringLiteral("xmlns")) == QLatin1String(XMPP_HASH_NS)) { - hash = Hash(ce); - if (hash.type() == Hash::Type::Unknown) { + if (streamingMode) { + setState(State::Active); + emit q->connectionReady(); + return; + } + + connect(connection.data(), &Connection::readyRead, q, [this]() { + if (!device) { return; } + if (q->pad()->session()->role() != q->senders()) { + readNextBlockFromTransport(); + } + }); + connect( + connection.data(), &Connection::bytesWritten, q, + [this](qint64 bytes) { + Q_UNUSED(bytes) + if (q->pad()->session()->role() == q->senders() && !connection->bytesToWrite()) { + writeNextBlockToTransport(); + } + }, + Qt::QueuedConnection); + + if (amIReceiver()) { + connect(connection.data(), &Connection::disconnected, q, [this]() { tryFinalizeIncoming(); }); } - } else if (ce.tagName() == QLatin1String("thumbnail")) { - thumbnail = Thumbnail(ce); + setState(State::Active); + if (acceptFile.range().isValid()) { + bytesLeft = acceptFile.range().length; + if (!bytesLeft) + endlessRange = true; + emit q->deviceRequested(acceptFile.range().offset, bytesLeft); + } else { + bytesLeft = acceptFile.size(); + emit q->deviceRequested(0, bytesLeft); + } } - } - auto p = new Private; - p->date = date; - p->mediaType = mediaType; - p->name = name; - p->desc = desc; - p->size = size; - p->rangeSupported = rangeSupported; - p->range = range; - p->hash = hash; - p->thumbnail = thumbnail; - - d = p; -} - -QDomElement File::toXml(QDomDocument *doc) const -{ - if (!isValid()) { - return QDomElement(); + void tryFinalizeIncoming() + { + if (q->_state == State::Finished || outgoingReceived || streamingMode) + return; + if (connection->isOpen() && bytesLeft) + return; + + // data read finished. check other stuff + if (hasher && incomingChecksum.isEmpty()) { + qDebug("waiting for "); + expectFinalize([this]() { + qDebug("Waiting for timed out. But likely succeeded anyway"); + lastReason = Reason(Reason::Condition::Success); + setState(State::Finished); + }); + return; + } + if (hasher) { + auto expectedHash = hasher->result(); + bool found = false; + for (auto const &h : std::as_const(incomingChecksum)) { + if (h.type() != expectedHash.type()) + continue; + if (h == expectedHash) { + qDebug("hurray! checksum matched!"); + lastReason = Reason(Reason::Condition::Success); + } else { + qDebug("failure! checksum mismatch! expected %s != %s", qPrintable(expectedHash.toString()), + qPrintable(h.toString())); + q->remove(Reason::Condition::MediaError, "checksum mismatch"); + return; + } + found = true; + break; + } + if (!found) + qDebug("haven't found %s checksum within received checksums", + qPrintable(expectedHash.stringType())); + } + outgoingReceived = true; + emit q->updated(); + } + }; + + Application::Application(const QSharedPointer &pad, const QString &contentName, Origin creator, + Origin senders) : d(new Private) + { + d->q = this; + _pad = pad; + _contentName = contentName; + _creator = creator; + _senders = senders; + _transportSelector.reset( + new NSTransportsList(pad->session(), static_cast(pad->manager())->availableTransports())); } - QDomElement el = doc->createElement(QStringLiteral("file")); - if (d->date.isValid()) { - el.appendChild(XMLHelper::textTag(*doc, QStringLiteral("date"), d->date.toString(Qt::ISODate))); + + Application::~Application() + { + delete d->hasher; + qDebug("jingle-ft: destroyed"); } - if (d->desc.size()) { - el.appendChild(XMLHelper::textTag(*doc, QStringLiteral("desc"), d->desc)); + + void Application::setState(State state) { d->setState(state); } + + Stanza::Error Application::lastError() const { return d->lastError; } + + Reason Application::lastReason() const { return d->lastReason; } + + static Application::SetDescError parseDescription(const QDomElement &description, File &file) + { + auto el = description.firstChildElement("file"); + if (el.isNull()) + return Application::Unparsed; + + auto f = File(el); + if (!f.isValid()) + return Application::IncompatibleParameters; + + file = f; + return Application::Ok; } - if (d->hash.isValid()) { - el.appendChild(d->hash.toXml(doc)); + + Application::SetDescError Application::setRemoteOffer(const QDomElement &description) + { + File f; + auto ret = parseDescription(description, f); + if (ret == Application::Ok) + d->file = f; + return ret; } - if (d->mediaType.size()) { - el.appendChild(XMLHelper::textTag(*doc, QStringLiteral("media-type"), d->mediaType)); + + Application::SetDescError Application::setRemoteAnswer(const QDomElement &description) + { + File f; + auto ret = parseDescription(description, f); + if (ret == Application::Ok) { + d->acceptFile = f; + setState(State::Accepted); + } + return ret; } - if (d->name.size()) { - el.appendChild(XMLHelper::textTag(*doc, QStringLiteral("name"), d->name)); + + void Application::prepareThumbnail(File &file) + { + if (file.thumbnail().data.size()) { + auto client = _pad->session()->manager()->client(); + auto thumb = file.thumbnail(); + auto bm = client->bobManager(); + BoBData data = bm->append(thumb.data, thumb.mimeType); + thumb.uri = QLatin1String("cid:") + data.cid(); + d->file.setThumbnail(thumb); + } } - if (d->size) { - el.appendChild(XMLHelper::textTag(*doc, QStringLiteral("size"), QString::number(d->size))); + + QDomElement Application::makeLocalOffer() + { + if (!d->file.isValid()) { + return QDomElement(); + } + auto doc = _pad->doc(); + auto el = doc->createElementNS(NS, "description"); + + prepareThumbnail(d->file); + el.appendChild(d->file.toXml(doc)); + return el; } - if (d->rangeSupported || d->range.isValid()) { - el.appendChild(d->range.toXml(doc)); + + QDomElement Application::makeLocalAnswer() + { + if (!d->file.isValid()) { + return QDomElement(); + } + if (!d->acceptFile.isValid()) { + d->acceptFile = d->file; + } + auto doc = _pad->doc(); + auto el = doc->createElementNS(NS, "description"); + el.appendChild(d->acceptFile.toXml(doc)); + return el; } - if (d->thumbnail.isValid()) { - el.appendChild(d->thumbnail.toXml(doc)); + + void Application::setFile(const File &file) { d->file = file; } + + void Application::setFile(const QFileInfo &fi, const QString &description, const Thumbnail &thumb) + { + QMimeDatabase mimeDb; + + auto hash = Hash::fastestHash(pad()->session()->peerFeatures()); + if (hash.isValid() && fi.size() < 10e6) { // compute hash dynamically (in a thread) for large files + QFile f(fi.absoluteFilePath()); + f.open(QIODevice::ReadOnly); + hash.compute(&f); + f.close(); + } + + File file; + file.setDate(fi.lastModified()); + file.setDescription(description); + file.addHash(hash); + file.setMediaType(mimeDb.mimeTypeForFile(fi).name()); + file.setName(fi.fileName()); + file.setRange(); // indicate range support + file.setSize(quint64(fi.size())); + file.setThumbnail(thumb); + d->file = file; } - return el; -} - -QDateTime File::date() const -{ - return d? d->date : QDateTime(); -} - -QString File::description() const -{ - return d? d->desc : QString(); -} - -Hash File::hash() const -{ - return d? d->hash : Hash(); -} - -QString File::mediaType() const -{ - return d? d->mediaType : QString(); -} - -QString File::name() const -{ - return d? d->name : QString(); -} - -quint64 File::size() const -{ - return d? d->size : 0; -} - -Range File::range() const -{ - return d? d->range : Range(); -} - -Thumbnail File::thumbnail() const -{ - return d? d->thumbnail: Thumbnail(); -} - -void File::setDate(const QDateTime &date) -{ - ensureD()->date = date; -} - -void File::setDescription(const QString &desc) -{ - ensureD()->desc = desc; -} - -void File::setHash(const Hash &hash) -{ - ensureD()->hash = hash; -} - -void File::setMediaType(const QString &mediaType) -{ - ensureD()->mediaType = mediaType; -} - -void File::setName(const QString &name) -{ - ensureD()->name = name; -} - -void File::setSize(quint64 size) -{ - ensureD()->size = size; -} - -void File::setRange(const Range &range) -{ - ensureD()->range = range; - d->rangeSupported = true; -} - -void File::setThumbnail(const Thumbnail &thumb) -{ - ensureD()->thumbnail = thumb; -} - -File::Private *File::ensureD() -{ - if (!d) { - d = new Private; + + File Application::file() const { return d->file; } + + File Application::acceptFile() const { return d->acceptFile; } + + bool Application::isTransportReplaceEnabled() const { return _state < State::Active; } + + void Application::prepareTransport() + { + expectSingleConnection(TransportFeature::Reliable | TransportFeature::DataOriented | TransportFeature::Ordered, + [this](Connection::Ptr connection) { d->onConnectionConnected(connection); }); + _transport->prepare(); } - return d.data(); -} - -//---------------------------------------------------------------------------- -// Checksum -//---------------------------------------------------------------------------- -Checksum::Checksum(const QDomElement &cs) : - ContentBase(cs) -{ - file = File(cs.firstChildElement(QLatin1String("file"))); -} - -bool Checksum::isValid() const -{ - return ContentBase::isValid() && file.isValid(); -} - -QDomElement Checksum::toXml(QDomDocument *doc) const -{ - auto el = ContentBase::toXml(doc, "checksum"); - if (!el.isNull()) { - el.appendChild(file.toXml(doc)); + + void Application::setStreamingMode(bool mode) + { + Q_ASSERT(_senders != _pad->session()->role()); + if (_senders == _pad->session()->role()) { + qCritical("streaming mode is implemented only for receiving, not sending"); + remove(Reason::GeneralError, "unsupported file sender streaming mode"); + return; + } + if (_state <= State::Connecting) { + d->streamingMode = mode; + } } - return el; -} - -//---------------------------------------------------------------------------- -// Received -//---------------------------------------------------------------------------- -QDomElement Received::toXml(QDomDocument *doc) const -{ - return ContentBase::toXml(doc, "received"); -} - -//---------------------------------------------------------------------------- -// ApplicationManager -//---------------------------------------------------------------------------- -Manager::Manager(QObject *parent): - XMPP::Jingle::ApplicationManager(parent) -{ - -} - -void Manager::setJingleManager(XMPP::Jingle::Manager *jm) -{ - jingleManager = jm; -} - -Application* Manager::startApplication(const ApplicationManagerPad::Ptr &pad, const QString &contentName, Origin creator, Origin senders) -{ - if (!(contentName.size() > 0 && (senders == Origin::Initiator || senders == Origin::Responder))) { - qDebug("Invalid Jignle FT App start parameters"); - return nullptr; + + XMPP::Jingle::Application::Update Application::evaluateOutgoingUpdate() + { + if (!isValid()) { + _update = { Action::NoAction, Reason() }; + return _update; + } + + if (_state == State::Active && (d->outgoingChecksum.size() > 0 || d->outgoingReceived)) + _update = { Action::SessionInfo, Reason() }; + else + return XMPP::Jingle::Application::evaluateOutgoingUpdate(); + return _update; } - return new Application(pad.staticCast(), contentName, creator, senders); // ContentOrigin::Remote -} -ApplicationManagerPad* Manager::pad(Session *session) -{ - return new Pad(this, session); -} + OutgoingUpdate Application::takeOutgoingUpdate() + { + qDebug("jingle-ft: take outgoing update"); + if (_update.action == Action::NoAction) { + return OutgoingUpdate(); + } + + auto client = _pad->session()->manager()->client(); + auto doc = client->doc(); -void Manager::closeAll() -{ + if (_update.action == Action::SessionInfo && (d->outgoingChecksum.size() > 0 || d->outgoingReceived)) { + if (d->outgoingReceived) { + d->outgoingReceived = false; + Received received(creator(), _contentName); + return OutgoingUpdate { QList() << received.toXml(doc), + [this](bool) { d->setState(State::Finished); } }; + } + if (!d->outgoingChecksum.isEmpty()) { + ContentBase cb(_pad->session()->role(), _contentName); + File f; + if (d->file.range().isValid()) { + Range r = d->file.range(); + r.hashes = d->outgoingChecksum; + f.setRange(r); + } else { + f.setHashes(d->outgoingChecksum); + } + auto el = cb.toXml(doc, "checksum", NS); + el.appendChild(f.toXml(doc)); + d->outgoingChecksum.clear(); + return OutgoingUpdate { QList() << el, [this](bool) { d->expectReceived(); } }; + } + } + if (_update.action == Action::ContentAdd && _creator == _pad->session()->role()) { + // we are doing outgoing file transfer request. so need thumbnail + } -} + return XMPP::Jingle::Application::takeOutgoingUpdate(); + } -Client *Manager::client() -{ - if (jingleManager) { - return jingleManager->client(); + void Application::prepare() + { + if (!_transport) { + selectNextTransport(); + } + if (_transport) { + d->setState(State::ApprovedToSend); + prepareTransport(); + } } - return nullptr; -} - -QStringList Manager::availableTransports() const -{ - return jingleManager->availableTransports(Transport::Reliable); -} - -//---------------------------------------------------------------------------- -// Application -//---------------------------------------------------------------------------- -class Application::Private -{ -public: - - State state = State::Created; - QSharedPointer pad; - QString contentName; - File file; - Origin creator; - Origin senders; - QSharedPointer transport; - QStringList availableTransports; -}; - -Application::Application(const QSharedPointer &pad, const QString &contentName, Origin creator, Origin senders) : - d(new Private) -{ - d->pad = pad; - d->contentName = contentName; - d->creator = creator; - d->senders = senders; - d->availableTransports = static_cast(pad->manager())->availableTransports(); -} - -Application::~Application() -{ - -} - -ApplicationManagerPad::Ptr Application::pad() const -{ - return d->pad.staticCast(); -} - -State Application::state() const -{ - return d->state; -} - -void Application::setState(State state) -{ - d->state = state; -} - -QString Application::contentName() const -{ - return d->contentName; -} - -Origin Application::creator() const -{ - return d->creator; -} - -Origin Application::senders() const -{ - return d->senders; -} - -Application::SetDescError Application::setDescription(const QDomElement &description) -{ - d->file = File(description.firstChildElement("file")); - //d->state = State::Pending; // basically it's incomming content. so if we parsed it it's pending. if not parsed if will rejected anyway. - return d->file.isValid()? Ok: Unparsed; -} - -void Application::setFile(const File &file) -{ - d->file = file; -} - -File Application::file() const -{ - return d->file; -} - -// incoming one? or we have to check real direction -bool Application::setTransport(const QSharedPointer &transport) -{ - if (transport->features() & Transport::Reliable) { - int nsIndex = d->availableTransports.indexOf(transport->pad()->ns()); - if (nsIndex == -1) { - return false; - } - d->availableTransports.removeAt(nsIndex); - d->transport = transport; - d->transport->setApplication(this); - d->state = State::Pending; - return true; + + void Application::start() + { + if (_transport) { + d->setState(State::Connecting); + _transport->start(); + } + // TODO we need QIODevice somewhere here } - return false; -} - -QSharedPointer Application::transport() const -{ - return d->transport; -} - -Action Application::outgoingUpdateType() const -{ - switch (d->state) { - case State::Created: - break; - case State::PrepareLocalOffer: - if (!d->transport && !d->availableTransports.size()) { - break; // not yet ready - } - return Action::ContentAdd; - case State::Connecting: - case State::Active: - return d->transport->outgoingUpdateType(); - case State::Pending: - default: - break; + + void Application::remove(Reason::Condition cond, const QString &comment) + { + if (_state >= State::Finishing) + return; + + _terminationReason = Reason(cond, comment); + if (_transport) { + _transport->disconnect(this); + _transport->stop(); + } + + if (_creator == _pad->session()->role() && _state <= State::ApprovedToSend) { + // local content, not yet sent to remote + setState(State::Finished); + return; + } + + emit updated(); } - return Action::NoAction; // TODO -} -OutgoingUpdate Application::takeOutgoingUpdate() -{ - if (!isValid() || d->state == State::Created) { - return OutgoingUpdate(); + void Application::incomingRemove(const Reason &r) + { + d->lastReason = r; + d->setState(State::Finished); } - if (d->state == State::Connecting || d->state == State::Active) { - return d->transport->takeOutgoingUpdate(); + + bool Application::isValid() const + { + return d->file.isValid() && _contentName.size() > 0 + && (_senders == Origin::Initiator || _senders == Origin::Responder); } - if (d->state == State::PrepareLocalOffer) { // basically when we come to this function Created is possible only for outgoing content - if (!d->transport && d->availableTransports.size()) { - selectNextTransport(); - } - if (!d->transport || d->transport->outgoingUpdateType() == Action::NoAction) { // failed to select next transport. can't continue - return OutgoingUpdate(); - } - auto client = d->pad->session()->manager()->client(); - if (d->file.thumbnail().data.size()) { - auto thumb = d->file.thumbnail(); - auto bm = client->bobManager(); - BoBData data = bm->append(thumb.data, thumb.mimeType); - thumb.uri = QLatin1String("cid:") + data.cid(); - d->file.setThumbnail(thumb); + + void Application::setDevice(QIODevice *dev, bool closeOnFinish) + { + if (!dev) { // failed to provide proper device + _terminationReason + = Reason(Reason::Condition::FailedApplication, QString::fromLatin1("No destination device")); + emit updated(); + return; } - auto doc = client->doc(); - ContentBase cb(d->pad->session()->role(), d->contentName); - cb.senders = d->senders; - auto cel = cb.toXml(doc, "content"); - cel.appendChild(doc->createElementNS(NS, "description")).appendChild(d->file.toXml(doc)); - QDomElement tel; - OutgoingUpdateCB trCallback; - std::tie(tel, trCallback) = d->transport->takeOutgoingUpdate(); - cel.appendChild(tel); - - d->state = State::Unacked; - return OutgoingUpdate{cel, [this, trCallback](){ - if (trCallback) { - trCallback(); - } - d->state = d->pad->session()->role() == Origin::Initiator? State::Pending : State::Active; - }}; + d->setDevice(dev, closeOnFinish); } - return OutgoingUpdate(); // TODO -} - -bool Application::wantBetterTransport(const QSharedPointer &t) const -{ - Q_UNUSED(t) - return true; // TODO check -} - -bool Application::selectNextTransport() -{ - if (d->availableTransports.size()) { - QString ns = d->availableTransports.takeFirst(); - d->transport = d->pad->session()->newOutgoingTransport(ns); - d->transport->setApplication(this); - return true; + + Connection::Ptr Application::connection() const { return d->connection.staticCast(); } + + void Application::incomingChecksum(const QList &hashes) + { + qDebug("got checksum: %s", qPrintable(hashes.value(0).toString())); + if (!d->hasher || _senders != _pad->session()->peerRole()) { + qDebug("unexpected incoming checksum. was it negotiated?"); + return; + } + d->incomingChecksum = hashes; + d->tryFinalizeIncoming(); } - return false; -} -void Application::prepare() -{ - if (!d->transport) { - selectNextTransport(); + void Application::incomingReceived() + { + qDebug("got received"); + d->onReceived(); } - if (d->transport) { - d->state = State::PrepareLocalOffer; - d->transport->prepare(); + + Pad::Pad(Manager *manager, Session *session) : _manager(manager), _session(session) { } + + QDomElement Pad::takeOutgoingSessionInfoUpdate() + { + return QDomElement(); // TODO } -} -void Application::start() -{ - if (d->transport) { - d->state = State::Active; - d->transport->start(); + QString Pad::ns() const { return NS; } + + Session *Pad::session() const { return _session; } + + ApplicationManager *Pad::manager() const { return _manager; } + + QString Pad::generateContentName(Origin senders) + { + QString prefix = senders == _session->role() ? "fileoffer" : "filereq"; + QString name; + do { +#if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0) + name = prefix + QString("_%1").arg(QRandomGenerator::global()->generate() & 0xffff, 4, 16, QChar('0')); +#else + name = prefix + QString("_%1").arg(qrand() & 0xffff, 4, 16, QChar('0')); +#endif + } while (_session->content(name, _session->role())); + return name; } - // TODO we nedd QIODevice somewhere here -} - -bool Application::isValid() const -{ - return d->file.isValid() && d->transport && d->contentName.size() > 0 && - (d->senders == Origin::Initiator || d->senders == Origin::Responder); -} - -Pad::Pad(Manager *manager, Session *session) : - _manager(manager), - _session(session) -{ - -} - -QDomElement Pad::takeOutgoingSessionInfoUpdate() -{ - return QDomElement(); // TODO -} - -QString Pad::ns() const -{ - return NS; -} - -Session *Pad::session() const -{ - return _session; -} - -ApplicationManager *Pad::manager() const -{ - return _manager; -} - -QString Pad::generateContentName(Origin senders) -{ - QString prefix = senders == _session->role()? "fileoffer" : "filereq"; - QString name; - do { - name = prefix + QString("_%1").arg(qrand() & 0xffff, 4, 16, QChar('0')); - } while (_session->content(name, _session->role())); - return name; -} - -void Pad::addOutgoingOffer(const File &file) -{ - auto selfp = _session->applicationPad(NS); - auto app = _manager->startApplication(selfp, "ft", _session->role(), _session->role()); - app->setFile(file); -} + bool Pad::incomingSessionInfo(const QDomElement &jingle) + { + for (auto el = jingle.firstChildElement(); !el.isNull(); el = el.nextSiblingElement()) { + if (el.tagName() == CHECKSUM_TAG) { + Checksum checksum(el); + auto app = session()->content(checksum.name, checksum.creator); + if (app) { + static_cast(app)->incomingChecksum(checksum.file.hashes()); + } + return true; + } else if (el.tagName() == RECEIVED_TAG) { + Received received(el); + auto app = session()->content(received.name, received.creator); + if (app) { + static_cast(app)->incomingReceived(); + } + return true; + } else { + // TODO report actual error + qDebug("unknown session-info: %s", qPrintable(el.tagName())); + } + } + return false; + } + void Pad::addOutgoingOffer(const File &file) + { + auto selfp = _session->applicationPad(NS); + auto app = _manager->startApplication(selfp, "ft", _session->role(), _session->role()); + app->setFile(file); + } } // namespace FileTransfer } // namespace Jingle diff --git a/src/xmpp/xmpp-im/jingle-ft.h b/src/xmpp/xmpp-im/jingle-ft.h index 0a23a00a..589622cb 100644 --- a/src/xmpp/xmpp-im/jingle-ft.h +++ b/src/xmpp/xmpp-im/jingle-ft.h @@ -20,145 +20,142 @@ #ifndef JINGLEFT_H #define JINGLEFT_H +#include "jingle-application.h" +#include "jingle-file.h" #include "jingle.h" +#include "xmpp_hash.h" namespace XMPP { - class Client; class Thumbnail; - -namespace Jingle { -namespace FileTransfer { - -extern const QString NS; -class Manager; - -struct Range { - quint64 offset = 0; - quint64 length = 0; - Hash hash; - - inline bool isValid() const { return hash.isValid() || offset || length; } - QDomElement toXml(QDomDocument *doc) const; -}; - -class File -{ -public: - File(); - File(const File &other); - File(const QDomElement &file); - ~File(); - inline bool isValid() const { return d != nullptr; } - QDomElement toXml(QDomDocument *doc) const; - - QDateTime date() const; - QString description() const; - Hash hash() const; - QString mediaType() const; - QString name() const; - quint64 size() const; - Range range() const; - Thumbnail thumbnail() const; - - void setDate(const QDateTime &date); - void setDescription(const QString &desc); - void setHash(const Hash &hash); - void setMediaType(const QString &mediaType); - void setName(const QString &name); - void setSize(quint64 size); - void setRange(const Range &range = Range()); // default empty just to indicate it's supported - void setThumbnail(const Thumbnail &thumb); -private: - class Private; - Private *ensureD(); - QSharedDataPointer d; -}; - -class Checksum : public ContentBase -{ - inline Checksum(){} - Checksum(const QDomElement &file); - bool isValid() const; - QDomElement toXml(QDomDocument *doc) const; -private: - File file; -}; - -class Received : public ContentBase -{ - using ContentBase::ContentBase; - QDomElement toXml(QDomDocument *doc) const; -}; - -class Pad : public ApplicationManagerPad -{ - Q_OBJECT - // TODO -public: - Pad(Manager *manager, Session *session); - QDomElement takeOutgoingSessionInfoUpdate() override; - QString ns() const override; - Session *session() const override; - ApplicationManager *manager() const override; - QString generateContentName(Origin senders) override; - - void addOutgoingOffer(const File &file); -private: - Manager *_manager; - Session *_session; -}; - -class Application : public XMPP::Jingle::Application -{ - Q_OBJECT -public: - Application(const QSharedPointer &pad, const QString &contentName, Origin creator, Origin senders); - ~Application(); - - ApplicationManagerPad::Ptr pad() const override; - State state() const override; - void setState(State state) override; - - QString contentName() const override; - Origin creator() const override; - Origin senders() const override; - SetDescError setDescription(const QDomElement &description) override; - void setFile(const File &file); - File file() const; - bool setTransport(const QSharedPointer &transport) override; - QSharedPointer transport() const override; - - Action outgoingUpdateType() const override; - OutgoingUpdate takeOutgoingUpdate() override; - bool wantBetterTransport(const QSharedPointer &) const override; - bool selectNextTransport() override; - void prepare() override; - void start() override; - - bool isValid() const; - - -private: - class Private; - QScopedPointer d; -}; - -class Manager : public XMPP::Jingle::ApplicationManager -{ - Q_OBJECT -public: - Manager(QObject *parent = nullptr); - void setJingleManager(XMPP::Jingle::Manager *jm); - Application *startApplication(const ApplicationManagerPad::Ptr &pad, const QString &contentName, Origin creator, Origin senders); - ApplicationManagerPad *pad(Session *session); // pad factory - void closeAll(); - Client* client(); - - QStringList availableTransports() const; -private: - XMPP::Jingle::Manager *jingleManager = nullptr; -}; +} + +namespace XMPP { namespace Jingle { namespace FileTransfer { + + extern const QString NS; + class Manager; + + class Checksum : public ContentBase { + public: + inline Checksum() { } + Checksum(const QDomElement &file); + bool isValid() const; + QDomElement toXml(QDomDocument *doc) const; + + File file; + }; + + class Received : public ContentBase { + public: + using ContentBase::ContentBase; + QDomElement toXml(QDomDocument *doc) const; + }; + + class Pad : public ApplicationManagerPad { + Q_OBJECT + // TODO + public: + Pad(Manager *manager, Session *session); + QDomElement takeOutgoingSessionInfoUpdate() override; + QString ns() const override; + Session *session() const override; + ApplicationManager *manager() const override; + QString generateContentName(Origin senders) override; + bool incomingSessionInfo(const QDomElement &el) override; + + void addOutgoingOffer(const File &file); + + private: + Manager *_manager; + Session *_session; + }; + + class Application : public XMPP::Jingle::Application { + Q_OBJECT + public: + Application(const QSharedPointer &pad, const QString &contentName, Origin creator, Origin senders); + ~Application() override; + + void setState(State state) override; + XMPP::Stanza::Error lastError() const override; + Reason lastReason() const override; + + SetDescError setRemoteOffer(const QDomElement &description) override; + SetDescError setRemoteAnswer(const QDomElement &description) override; + QDomElement makeLocalOffer() override; + QDomElement makeLocalAnswer() override; + + bool isTransportReplaceEnabled() const override; + void remove(Reason::Condition cond = Reason::Success, const QString &comment = QString()) override; + + XMPP::Jingle::Application::Update evaluateOutgoingUpdate() override; + OutgoingUpdate takeOutgoingUpdate() override; + void prepare() override; + void start() override; + + void setFile(const File &file); + void setFile(const QFileInfo &fi, const QString &description, const Thumbnail &thumb); + File file() const; + File acceptFile() const; + + /** + * @brief setStreamingMode enables external download control. + * So Jingle-FT won't request output device but instead underlying established + * connection will be emitted (see connectionReady() signal). + * The connection is an XMPP::Jingle::Connection::Ptr instance. + * When the connection is not needed anymore, one can just destroy jingle + * session or remove the Application from the session. + * Make sure to set the mode before connection is established. + * @param mode + */ + void setStreamingMode(bool mode = true); + bool isValid() const; + + void setDevice(QIODevice *dev, bool closeOnFinish = true); + Connection::Ptr connection() const; + + void incomingChecksum(const QList &hashes); + void incomingReceived(); + + protected: + void incomingRemove(const Reason &r) override; + void prepareTransport() override; + + private: + void prepareThumbnail(File &file); + + signals: + void connectionReady(); // streaming mode only + + // if size = 0 then it's reamaining part of the file (non-streaming mode only) + void deviceRequested(qint64 offset, qint64 size); + void progress(qint64 offset); + + private: + class Private; + std::unique_ptr d; + }; + + class Manager : public XMPP::Jingle::ApplicationManager { + Q_OBJECT + public: + Manager(QObject *parent = nullptr); + ~Manager(); + void setJingleManager(XMPP::Jingle::Manager *jm) override; + Application *startApplication(const ApplicationManagerPad::Ptr &pad, const QString &contentName, Origin creator, + Origin senders) override; + ApplicationManagerPad *pad(Session *session) override; // pad factory + void closeAll(const QString &ns = QString()) override; + + QStringList discoFeatures() const override; + + Client *client(); + + QStringList availableTransports() const; + + private: + XMPP::Jingle::Manager *jingleManager = nullptr; + }; } // namespace FileTransfer } // namespace Jingle diff --git a/src/xmpp/xmpp-im/jingle-ibb.cpp b/src/xmpp/xmpp-im/jingle-ibb.cpp new file mode 100644 index 00000000..3b9b5661 --- /dev/null +++ b/src/xmpp/xmpp-im/jingle-ibb.cpp @@ -0,0 +1,486 @@ +/* + * jignle-ibb.cpp - Jingle In-Band Bytestream transport + * Copyright (C) 2019 Sergey Ilinykh + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library. If not, see . + * + */ + +#include "jingle-ibb.h" +#include "jingle-session.h" + +#include "xmpp/jid/jid.h" +#include "xmpp_client.h" +#include "xmpp_ibb.h" + +#include +#if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0) +#include +#endif + +template constexpr std::add_const_t &as_const(T &t) noexcept { return t; } + +namespace XMPP { namespace Jingle { namespace IBB { + const QString NS(QStringLiteral("urn:xmpp:jingle:transports:ibb:1")); + + class Connection : public XMPP::Jingle::Connection { + Q_OBJECT + + public: + Client *client; + Jid peer; + QString sid; + size_t _blockSize; + IBBConnection *connection = nullptr; + State state = State::Created; + Origin creator = Origin::None; + + // bool offerSent = false; + // bool offerReceived = false; + // bool closing = false; + // bool finished = false; + + Connection(Client *client, const Jid &jid, const QString &sid, size_t blockSize) : + client(client), peer(jid), sid(sid), _blockSize(blockSize) + { + } + + void setConnection(IBBConnection *c) + { + c->setParent(this); + connection = c; + connect(c, &IBBConnection::readyRead, this, &Connection::readyRead); + connect(c, &IBBConnection::bytesWritten, this, &Connection::bytesWritten); + connect(c, &IBBConnection::connectionClosed, this, &Connection::handleIBBClosed); + connect(c, &IBBConnection::delayedCloseFinished, this, &Connection::handleIBBClosed); + connect(c, &IBBConnection::aboutToClose, this, &Connection::aboutToClose); + connect(c, &IBBConnection::connected, this, &Connection::handleConnnected); + } + + void handleConnnected() + { + state = State::Active; + setOpenMode(connection->openMode()); + emit connected(); + } + + TransportFeatures features() const + { + return TransportFeature::DataOriented | TransportFeature::StreamOriented | TransportFeature::Ordered + | TransportFeature::Reliable; + } + + size_t blockSize() const { return _blockSize; } + + qint64 bytesAvailable() const + { + return XMPP::Jingle::Connection::bytesAvailable() + (connection ? connection->bytesAvailable() : 0); + } + + qint64 bytesToWrite() const + { + return XMPP::Jingle::Connection::bytesToWrite() + (connection ? connection->bytesToWrite() : 0); + } + + void close() + { + if (connection) { + connection->close(); + setOpenMode(connection->openMode()); + } else { + XMPP::Jingle::Connection::close(); + emit connectionClosed(); + } + state = State::Finished; + } + + protected: + qint64 writeData(const char *data, qint64 maxSize) { return connection->write(data, maxSize); } + + qint64 readData(char *data, qint64 maxSize) + { + qint64 ret = connection->read(data, maxSize); + if (state == State::Finishing && !bytesAvailable()) { + postCloseAllDataRead(); + } + return ret; + } + + private: + void handleIBBClosed() + { + state = State::Finishing; + if (bytesAvailable()) + setOpenMode(QIODevice::ReadOnly); + else + postCloseAllDataRead(); + } + + void postCloseAllDataRead() + { + state = State::Finished; + connection->deleteLater(); + connection = nullptr; + setOpenMode(QIODevice::NotOpen); + emit connectionClosed(); + } + }; + + struct Transport::Private { + Transport *q = nullptr; + QMap> connections; + size_t defaultBlockSize = 4096; + bool started = false; + + ~Private() { qDebug("destroying ibb private"); } + + void checkAndStartConnection(const QSharedPointer &c) + { + if (c->connection || c->state != State::Accepted) + return; + + c->state = State::Connecting; + if (q->_pad->session()->role() == Origin::Initiator) { + auto con = q->_pad->session()->manager()->client()->ibbManager()->createConnection(); + auto ibbcon = static_cast(con); + ibbcon->setPacketSize(int(c->blockSize())); + c->setConnection(ibbcon); + ibbcon->connectToJid(q->_pad->session()->peer(), c->sid); + } // else we are waiting for incoming open + } + + QSharedPointer newStream(const QString &sid, std::size_t blockSize, Origin creator) + { + auto conn = q->_pad.staticCast()->makeConnection(sid, blockSize); + auto ibbConn = conn.staticCast(); + if (!ibbConn) + return ibbConn; + ibbConn->creator = creator; + QObject::connect(ibbConn.data(), &Connection::connected, q, [this]() { + if (q->_state == State::Connecting) { + q->setState(State::Active); + } + }); + QObject::connect(ibbConn.data(), &Connection::connectionClosed, q, [this]() { + Connection *c = static_cast(q->sender()); + connections.remove(c->sid); + }); + + return ibbConn; + } + }; + + Transport::Transport(const TransportManagerPad::Ptr &pad, Origin creator) : + XMPP::Jingle::Transport(pad, creator), d(new Private) + { + d->q = this; + connect(pad->manager(), &TransportManager::abortAllRequested, this, [this]() { + while (d->connections.size()) { + d->connections.first()->close(); + } + // d->aborted = true; + onFinish(Reason::Cancel); + }); + } + + Transport::~Transport() + { + // we have to mark all of them as finished just in case they are captured somewhere else + qDebug("jingle-ibb: destroy"); + auto connections = d->connections.values(); + for (auto &c : connections) { + c->close(); + } + } + + void Transport::prepare() + { + setState(State::ApprovedToSend); + auto it = d->connections.begin(); + while (it != d->connections.end()) { + auto &c = it.value(); + if (c->isRemote() && !notifyIncomingConnection(c)) { + it = d->connections.erase(it); + continue; + } + c->state = State::ApprovedToSend; + ++it; + } + if (d->connections.isEmpty()) { + onFinish(Reason::GeneralError, QLatin1String("no connections registered")); + } else + emit updated(); + } + + void Transport::start() + { + setState(State::Connecting); + + for (auto &c : d->connections) { + d->checkAndStartConnection(c); + } + } + + bool Transport::update(const QDomElement &transportEl) + { + if (_state == State::Finished) { + qWarning("The IBB transport has finished already"); + return false; + } + + QString sid = transportEl.attribute(QString::fromLatin1("sid")); + if (sid.isEmpty()) { + qWarning("empty SID"); + return false; + } + + size_t bs_final = d->defaultBlockSize; + auto bs = transportEl.attribute(QString::fromLatin1("block-size")); + if (!bs.isEmpty()) { + size_t bsn = bs.toULongLong(); + if (bsn && bsn <= bs_final) { + bs_final = bsn; + } + } + + auto it = d->connections.find(sid); + if (it == d->connections.end()) { // new sid = new stream according to xep + auto c = d->newStream(sid, bs_final, _pad->session()->peerRole()); + if (!c) { + qWarning("failed to create IBB connection"); + return false; + } + c->setRemote(true); + c->state = State::Pending; + if (_state == State::Created && isRemote()) { + // seems like we are just initing remote transport + setState(State::Pending); + d->connections.insert(sid, c); + } else if (!wasAccepted() || notifyIncomingConnection(c)) + d->connections.insert(sid, c); + } else { + if ((*it)->creator != _pad->session()->role() || (*it)->state != State::Pending) { + if ((*it)->state >= State::Accepted && (*it)->state <= State::Active) { + qWarning("Ignoring IBB transport in state: %d", int((*it)->state)); + return true; + } + qWarning("Unexpected IBB answer"); + return false; // out of order or something like this + } + + if (bs_final < (*it)->_blockSize) { + (*it)->_blockSize = bs_final; + } + if (_creator == _pad->session()->role()) { + setState(State::Accepted); + } + (*it)->state = State::Accepted; + } + + if (_state >= State::Connecting) { + auto c = it.value(); + QTimer::singleShot(0, this, [this, c]() mutable { d->checkAndStartConnection(c); }); + } + return true; + } + + bool Transport::hasUpdates() const + { + for (auto &c : d->connections) { + if (c->state == State::ApprovedToSend) { + return true; + } + } + return false; + } + + OutgoingTransportInfoUpdate Transport::takeOutgoingUpdate(bool ensureTransportElement) + { + OutgoingTransportInfoUpdate upd; + if (!isValid()) { + return upd; + } + + auto doc = _pad->session()->manager()->client()->doc(); + auto it = std::find_if(d->connections.begin(), d->connections.end(), + [](auto &c) { return c->state == State::ApprovedToSend; }); + + if (it == d->connections.end()) { + if (ensureTransportElement) { + // a really dirty workaround here which ignore the fact IBB may have more then one transport for + // a single content + it = std::find_if(d->connections.begin(), d->connections.end(), + [](auto &c) { return c->state > State::ApprovedToSend; }); + if (it == d->connections.end()) { + return upd; + } + QDomElement tel = doc->createElementNS(NS, "transport"); + tel.setAttribute(QStringLiteral("sid"), it.value()->sid); + tel.setAttribute(QString::fromLatin1("block-size"), qulonglong(it.value()->_blockSize)); + std::get<0>(upd) = tel; + } + return upd; + } + + auto connection = it.value(); + connection->state = State::Unacked; + + QDomElement tel = doc->createElementNS(NS, "transport"); + tel.setAttribute(QStringLiteral("sid"), connection->sid); + tel.setAttribute(QString::fromLatin1("block-size"), qulonglong(connection->_blockSize)); + + if (_state == State::ApprovedToSend) { + setState(State::Unacked); + } + upd = OutgoingTransportInfoUpdate { tel, [this, connection](bool success) mutable { + if (!success || connection->state != State::Unacked) + return; + + if (connection->creator == _pad->session()->role()) { + connection->state = State::Pending; + } else { + connection->state = State::Accepted; + } + + if (_state == State::Unacked) { + setState(_creator == _pad->session()->role() ? State::Pending + : State::Accepted); + } + if (_state >= State::Connecting) + d->checkAndStartConnection(connection); + } }; + + return upd; + } + + bool Transport::isValid() const { return bool(d); } + + TransportFeatures Transport::features() const + { + return TransportFeature::AlwaysConnect | TransportFeature::Reliable | TransportFeature::StreamOriented + | TransportFeature::DataOriented; + } + + int Transport::maxSupportedChannelsPerComponent(TransportFeatures) const { return -1; } + + Connection::Ptr Transport::addChannel(TransportFeatures features, const QString &id, int) + { + if (features & TransportFeature::LiveOriented) + return {}; + auto ibbConn = d->newStream(QString(), d->defaultBlockSize, _pad->session()->role()); + ibbConn->setId(id); + d->connections.insert(ibbConn->sid, ibbConn); + return ibbConn; + } + + QList Transport::channels() const + { + QList ret; + ret.reserve(d->connections.size()); + for (auto const &v : as_const(d->connections)) { + ret.append(v); + } + return ret; + } + + Pad::Pad(Manager *manager, Session *session) + { + _manager = manager; + _session = session; + } + + QString Pad::ns() const { return NS; } + + Session *Pad::session() const { return _session; } + + TransportManager *Pad::manager() const { return _manager; } + + Connection::Ptr Pad::makeConnection(const QString &sid, size_t blockSize) + { + return _manager->makeConnection(_session->peer(), sid, blockSize); + } + + struct Manager::Private { + QHash, QSharedPointer> connections; + XMPP::Jingle::Manager *jingleManager = nullptr; + }; + + Manager::Manager(QObject *parent) : TransportManager(parent), d(new Private) { } + + Manager::~Manager() + { + if (d->jingleManager) + d->jingleManager->unregisterTransport(NS); + } + + TransportFeatures Manager::features() const + { + return TransportFeature::AlwaysConnect | TransportFeature::Reliable | TransportFeature::Ordered + | TransportFeature::DataOriented; + } + + void Manager::setJingleManager(XMPP::Jingle::Manager *jm) { d->jingleManager = jm; } + + QSharedPointer Manager::newTransport(const TransportManagerPad::Ptr &pad, Origin creator) + { + return QSharedPointer::create(pad, creator).staticCast(); + } + + TransportManagerPad *Manager::pad(Session *session) { return new Pad(this, session); } + + QStringList Manager::discoFeatures() const { return { NS }; } + + XMPP::Jingle::Connection::Ptr Manager::makeConnection(const Jid &peer, const QString &sid, size_t blockSize) + { + if (!sid.isEmpty() && d->connections.contains(qMakePair(peer, sid))) { + qWarning("sid %s was already registered for %s", qPrintable(sid), qPrintable(peer.full())); + return Connection::Ptr(); + } + QString s(sid); + if (s.isEmpty()) { + QPair key; + do { +#if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0) + s = QString("ibb_%1").arg(QRandomGenerator::global()->generate() & 0xffff, 4, 16, QChar('0')); +#else + s = QString("ibb_%1").arg(qrand() & 0xffff, 4, 16, QChar('0')); +#endif + key = qMakePair(peer, s); + } while (d->connections.contains(key)); + } + auto conn = QSharedPointer::create(d->jingleManager->client(), peer, s, blockSize); + d->connections.insert(qMakePair(peer, s), conn); + connect(conn.data(), &Connection::connectionClosed, this, [this]() { + Connection *c = static_cast(sender()); + d->connections.remove(qMakePair(c->peer, c->sid)); + }); + + return conn.staticCast(); + } + + bool Manager::handleIncoming(IBBConnection *c) + { + auto conn = d->connections.value(qMakePair(c->peer(), c->sid())); + if (conn) { + conn->setConnection(c); + QTimer::singleShot(0, c, &IBBConnection::accept); + return true; + } + return false; + } +} // namespace IBB +} // namespace Jingle +} // namespace XMPP + +#include "jingle-ibb.moc" diff --git a/src/xmpp/xmpp-im/jingle-ibb.h b/src/xmpp/xmpp-im/jingle-ibb.h new file mode 100644 index 00000000..6dbdba13 --- /dev/null +++ b/src/xmpp/xmpp-im/jingle-ibb.h @@ -0,0 +1,100 @@ +/* + * jignle-ibb.h - Jingle In-Band Bytestream transport + * Copyright (C) 2019 Sergey Ilinykh + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library. If not, see . + * + */ + +#ifndef JINGLEIBB_H +#define JINGLEIBB_H + +#include "jingle-transport.h" + +namespace XMPP { +class IBBConnection; + +namespace Jingle { namespace IBB { + extern const QString NS; + + class Transport : public XMPP::Jingle::Transport { + Q_OBJECT + public: + Transport(const TransportManagerPad::Ptr &pad, Origin creator); + ~Transport() override; + + void prepare() override; + void start() override; + bool update(const QDomElement &transportEl) override; + bool hasUpdates() const override; + OutgoingTransportInfoUpdate takeOutgoingUpdate(bool ensureTransportElement = false) override; + bool isValid() const override; + TransportFeatures features() const override; + int maxSupportedChannelsPerComponent(TransportFeatures features) const override; + + Connection::Ptr addChannel(TransportFeatures features, const QString &id, int component = 0) override; + QList channels() const override; + + private: + friend class Manager; + + struct Private; + std::unique_ptr d; + }; + + class Manager; + class Pad : public TransportManagerPad { + Q_OBJECT + // TODO + public: + typedef QSharedPointer Ptr; + + Pad(Manager *manager, Session *session); + QString ns() const override; + Session *session() const override; + TransportManager *manager() const override; + + Connection::Ptr makeConnection(const QString &sid, size_t blockSize); + + private: + Manager *_manager = nullptr; + Session *_session = nullptr; + }; + + class Manager : public TransportManager { + Q_OBJECT + public: + Manager(QObject *parent = nullptr); + ~Manager() override; + + XMPP::Jingle::TransportFeatures features() const override; + void setJingleManager(XMPP::Jingle::Manager *jm) override; + QSharedPointer newTransport(const TransportManagerPad::Ptr &pad, + Origin creator) override; + TransportManagerPad *pad(Session *session) override; + + QStringList discoFeatures() const override; + + Connection::Ptr makeConnection(const Jid &peer, const QString &sid, size_t blockSize); + bool handleIncoming(IBBConnection *c); + + private: + struct Private; + std::unique_ptr d; + }; +} // namespace IBB +} // namespace Jingle +} // namespace XMPP + +#endif // JINGLEIBB_H diff --git a/src/xmpp/xmpp-im/jingle-ice.cpp b/src/xmpp/xmpp-im/jingle-ice.cpp new file mode 100644 index 00000000..f60a49f4 --- /dev/null +++ b/src/xmpp/xmpp-im/jingle-ice.cpp @@ -0,0 +1,1268 @@ +/* + * jignle-ice.cpp - Jingle ICE transport + * Copyright (C) 2020 Sergey Ilinykh + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library. If not, see . + * + */ + +#ifdef JINGLE_SCTP +#include "jingle-sctp.h" //Do not move to avoid warnings with MinGW +#endif + +#include "jingle-ice.h" + +#include "dtls.h" +#include "ice176.h" +#include "jingle-session.h" +#include "netnames.h" +#include "stundisco.h" +#include "udpportreserver.h" +#include "xmpp/jid/jid.h" +#include "xmpp_client.h" +#include "xmpp_externalservicediscovery.h" +#include "xmpp_serverinfomanager.h" + +#include + +#include +#include +#include + +template constexpr std::add_const_t &as_const(T &t) noexcept { return t; } + +namespace XMPP { namespace Jingle { namespace ICE { + const QString NS(QStringLiteral("urn:xmpp:jingle:transports:ice:0")); + const QString NS_DTLS(QStringLiteral("urn:xmpp:jingle:apps:dtls:0")); + + static XMPP::Ice176::Candidate elementToCandidate(const QDomElement &e) + { + if (e.tagName() != "candidate") + return XMPP::Ice176::Candidate(); + + XMPP::Ice176::Candidate c; + c.component = e.attribute("component").toInt(); + c.foundation = e.attribute("foundation"); + c.generation = e.attribute("generation").toInt(); + c.id = e.attribute("id"); + c.ip = QHostAddress(e.attribute("ip")); + c.network = e.attribute("network").toInt(); + c.port = e.attribute("port").toInt(); + c.priority = e.attribute("priority").toInt(); + c.protocol = e.attribute("protocol"); + c.rel_addr = QHostAddress(e.attribute("rel-addr")); + c.rel_port = e.attribute("rel-port").toInt(); + // TODO: remove this? + // c.rem_addr = QHostAddress(e.attribute("rem-addr")); + // c.rem_port = e.attribute("rem-port").toInt(); + c.type = e.attribute("type"); + + // TODO tcptype + + return c; + } + + static QDomElement candidateToElement(QDomDocument *doc, const XMPP::Ice176::Candidate &c) + { + QDomElement e = doc->createElement("candidate"); + e.setAttribute("component", QString::number(c.component)); + e.setAttribute("foundation", c.foundation); + e.setAttribute("generation", QString::number(c.generation)); + if (!c.id.isEmpty()) + e.setAttribute("id", c.id); + e.setAttribute("ip", c.ip.toString()); + // e.setAttribute("ip", "192.168.0.99"); + if (c.network != -1) + e.setAttribute("network", QString::number(c.network)); + else // weird? + e.setAttribute("network", QString::number(0)); + e.setAttribute("port", QString::number(c.port)); + e.setAttribute("priority", QString::number(c.priority)); + e.setAttribute("protocol", c.protocol); + if (!c.rel_addr.isNull()) + e.setAttribute("rel-addr", c.rel_addr.toString()); + if (c.rel_port != -1) + e.setAttribute("rel-port", QString::number(c.rel_port)); + // TODO: remove this? + // if(!c.rem_addr.isNull()) + // e.setAttribute("rem-addr", c.rem_addr.toString()); + // if(c.rem_port != -1) + // e.setAttribute("rem-port", QString::number(c.rem_port)); + e.setAttribute("type", c.type); + return e; + } + + QDomElement remoteCandidateToElement(QDomDocument *doc, const Ice176::SelectedCandidate &c) + { + auto e = doc->createElement("remote-candidate"); + e.setAttribute(QLatin1String("component"), c.componentId); + e.setAttribute(QLatin1String("ip"), c.ip.toString()); + e.setAttribute(QLatin1String("port"), c.port); + return e; + } + + Ice176::SelectedCandidate elementToRemoteCandidate(const QDomElement &el, bool *ok) + { + Ice176::SelectedCandidate c; + quint16 tmp; + tmp = el.attribute(QLatin1String("component")).toUInt(ok); + *ok = *ok && tmp < 256; + if (!*ok) + return c; + c.componentId = tmp; + c.ip = QHostAddress(el.attribute(QLatin1String("ip"))); + c.port = el.attribute(QLatin1String("port")).toUShort(ok); + *ok = *ok && !c.ip.isNull(); + return c; + } + + struct Element { + QString pwd; + QString ufrag; + Dtls::FingerPrint fingerprint; +#ifdef JINGLE_SCTP + SCTP::MapElement sctpMap; + QList sctpChannels; +#endif + QList candidates; + QList remoteCandidates; + bool gatheringComplete = false; + + void cleanupICE() + { + candidates.clear(); + remoteCandidates.clear(); + gatheringComplete = false; + } + + QDomElement toXml(QDomDocument *doc) const + { + QDomElement tel = doc->createElementNS(NS, "transport"); + if (!pwd.isEmpty()) + tel.setAttribute(QLatin1String("pwd"), pwd); + if (!ufrag.isEmpty()) + tel.setAttribute(QLatin1String("ufrag"), ufrag); + if (fingerprint.isValid()) + tel.appendChild(fingerprint.toXml(doc)); +#ifdef JINGLE_SCTP + if (sctpMap.isValid()) + tel.appendChild(sctpMap.toXml(doc)); + for (auto const &c : sctpChannels) + tel.appendChild(c.toXml(doc)); +#endif + for (auto const &c : candidates) + tel.appendChild(candidateToElement(doc, c)); + for (auto const &c : remoteCandidates) { + tel.appendChild(remoteCandidateToElement(doc, c)); + } + if (gatheringComplete) + tel.appendChild(doc->createElement(QLatin1String("gathering-complete"))); + + return tel; + } + + void parse(const QDomElement &el) + { + ufrag = el.attribute(QLatin1String("ufrag")); + pwd = el.attribute(QLatin1String("pwd")); + auto e = el.firstChildElement(QLatin1String("gathering-complete")); + gatheringComplete = !e.isNull(); + + e = el.firstChildElement(QLatin1String("fingerprint")); + if (!e.isNull() && !fingerprint.parse(e)) + throw std::runtime_error("invalid fingerprint"); + + if (fingerprint.isValid() && !Dtls::isSupported()) + qWarning("Remote requested DTLS but it's not supported by used crypto libraries."); +#ifdef JINGLE_SCTP + e = el.firstChildElement(QLatin1String("sctpmap")); + if (!e.isNull() && !sctpMap.parse(e)) + throw std::runtime_error("invalid sctpmap"); + + QString chTag(QStringLiteral("channel")); + for (e = el.firstChildElement(chTag); !e.isNull(); e = e.nextSiblingElement(chTag)) { + SCTP::ChannelElement channel; + if (!channel.parse(e)) + throw std::runtime_error("invalid sctp channel"); + ; + sctpChannels.append(channel); + } +#endif + QString candTag(QStringLiteral("candidate")); + for (e = el.firstChildElement(candTag); !e.isNull(); e = e.nextSiblingElement(candTag)) { + auto c = elementToCandidate(e); + if (!c.component || c.type.isEmpty()) + throw std::runtime_error("invalid candidate"); + ; + candidates.append(c); + } + if (!candidates.isEmpty()) { + if (ufrag.isEmpty() || pwd.isEmpty()) + throw std::runtime_error("user fragment or password can't be empty"); + } + + QString rcTag(QStringLiteral("remote-candidate")); + for (e = el.firstChildElement(rcTag); !e.isNull(); e = e.nextSiblingElement(rcTag)) { + bool ok; + Ice176::SelectedCandidate rc = elementToRemoteCandidate(e, &ok); + if (!ok) + throw std::runtime_error("invalid remote candidate"); + ; + remoteCandidates.append(rc); + } + } + }; + + class Resolver : public QObject { + Q_OBJECT + using QObject::QObject; + + int counter; + std::function callback; + + void onOneFinished() + { + if (!--counter) { + callback(); + deleteLater(); + } + } + + public: + using ResolveList = std::list>>; + + static void resolve(QObject *parent, ResolveList list, std::function &&callback) + { + auto resolver = new Resolver(parent); // will be deleted when all finished. see onOneFinished + resolver->counter = int(list.size()); + resolver->callback = callback; + for (auto &item : list) { + // FIXME hosts may dup in the list. needs optimization + auto *dns = new NameResolver(parent); + + connect(dns, &NameResolver::resultsReady, resolver, + [result = item.second, resolver](const QList &records) { + result.get() = records.first().address(); + resolver->onOneFinished(); + }); + connect(dns, &NameResolver::error, resolver, + [resolver](XMPP::NameResolver::Error) { resolver->onOneFinished(); }); + + dns->start(item.first.toLatin1()); + } + } + }; + + class IceStopper : public QObject { + Q_OBJECT + + public: + QTimer t; + XMPP::UdpPortReserver *portReserver; + QList left; + + IceStopper(QObject *parent = nullptr) : QObject(parent), t(this), portReserver(nullptr) + { + connect(&t, SIGNAL(timeout()), SLOT(t_timeout())); + t.setSingleShot(true); + } + + ~IceStopper() + { + qDeleteAll(left); + delete portReserver; + printf("IceStopper done\n"); + } + + void start(XMPP::UdpPortReserver *_portReserver, const QList iceList) + { + if (_portReserver) { + portReserver = _portReserver; + portReserver->setParent(this); + } + left = iceList; + + for (Ice176 *ice : std::as_const(left)) { + ice->setParent(this); + + // TODO: error() also? + connect(ice, &Ice176::stopped, this, &IceStopper::ice_stopped); + connect(ice, &Ice176::error, this, &IceStopper::ice_error); + ice->stop(); + } + + t.start(3000); + } + + private slots: + void ice_stopped() + { + XMPP::Ice176 *ice = static_cast(sender()); + ice->disconnect(this); + ice->setParent(nullptr); + ice->deleteLater(); + left.removeAll(ice); + if (left.isEmpty()) + deleteLater(); + } + + void ice_error(XMPP::Ice176::Error e) + { + Q_UNUSED(e) + + ice_stopped(); + } + + void t_timeout() { deleteLater(); } + }; + + class Manager::Private { + public: + XMPP::Jingle::Manager *jingleManager = nullptr; + + int basePort = -1; + QString extHost; + QHostAddress selfAddr; + + QString stunBindHost; + int stunBindPort; + QString stunRelayUdpHost; + int stunRelayUdpPort; + QString stunRelayUdpUser; + QString stunRelayUdpPass; + QString stunRelayTcpHost; + int stunRelayTcpPort; + QString stunRelayTcpUser; + QString stunRelayTcpPass; + + XMPP::TurnClient::Proxy stunProxy; + + // FIMME it's reuiqred to split transports by direction otherwise we gonna hit conflicts. + // jid,transport-sid -> transport mapping + // QSet> sids; + // QHash key2transport; + // Jid proxy; + }; + + class RawConnection : public XMPP::Jingle::Connection { + Q_OBJECT + public: + using Ptr = QSharedPointer; + + enum DisconnectReason { None, DtlsClosed }; + + QList datagrams; + DisconnectReason disconnectReason = None; + quint8 componentIndex = 0; + + RawConnection(quint8 componentIndex) : componentIndex(componentIndex) {}; + + int component() const override { return componentIndex; } + + TransportFeatures features() const override + { + return TransportFeature::Fast | TransportFeature::MessageOriented | TransportFeature::HighProbableConnect + | TransportFeature::Unreliable; + } + + bool hasPendingDatagrams() const override { return datagrams.size() > 0; } + + NetworkDatagram readDatagram(qint64 maxSize = -1) override + { + Q_UNUSED(maxSize) // TODO or not? + return datagrams.size() ? datagrams.takeFirst() : NetworkDatagram(); + } + + qint64 bytesAvailable() const override { return 0; } + + qint64 bytesToWrite() const override { return 0; /*client->bytesToWrite();*/ } + + void close() override { XMPP::Jingle::Connection::close(); } + + private: + friend class Transport; + + void onConnected() + { + qDebug("jingle-ice: channel connected!"); + emit connected(); + } + + void onError(QAbstractSocket::SocketError error) { qDebug("jingle-ice: channel failed: %d", error); } + + void onDisconnected(DisconnectReason reason) + { + if (!isOpen()) + return; + disconnectReason = reason; + setOpenMode(QIODevice::ReadOnly); + emit disconnected(); + } + + void enqueueIncomingUDP(const QByteArray &data) + { + datagrams.append(NetworkDatagram { data }); + emit readyRead(); + } + }; + + struct Component { + int componentIndex = 0; + bool initialized = false; + bool lowOverhead = false; + bool needDatachannel = false; + Dtls *dtls = nullptr; +#ifdef JINGLE_SCTP + SCTP::Association *sctp = nullptr; +#endif + QSharedPointer rawConnection; + // QHash dataChannels; + }; + + class Transport::Private { + public: + enum PendingActions { + NewCandidate = 1, + RemoteCandidate = 2, + GatheringComplete = 4, + NewFingerprint = 8, + NewSctpAssociation = 16 + }; + + Transport *q = nullptr; + bool offerSent = false; + bool aborted = false; + bool initialOfferReady = false; + bool remoteAcceptedFingerprint = false; + quint16 pendingActions = 0; + int proxiesInDiscoCount = 0; + QVector components; + QList pendingLocalCandidates; // cid to candidate mapping + std::unique_ptr remoteState; + + // QString sid; + // Transport::Mode mode = Transport::Tcp; + // QTimer probingTimer; + // QTimer negotiationFinishTimer; + // QElapsedTimer lastConnectionStart; + // size_t blockSize = 8192; + TcpPortDiscoverer *disco = nullptr; + UdpPortReserver *portReserver = nullptr; + Resolver resolver; + XMPP::Ice176 *ice = nullptr; + + Dtls::Setup localDtlsRole = Dtls::ActPass; + Dtls::Setup remoteDtlsRole = Dtls::ActPass; +#ifdef JINGLE_SCTP + SCTP::MapElement sctp; +#endif + + QHostAddress extAddr; + QHostAddress stunBindAddr, stunRelayUdpAddr, stunRelayTcpAddr; + int stunBindPort; + int stunRelayUdpPort; + int stunRelayTcpPort; + QString stunRelayUdpUser; + QString stunRelayUdpPass; + QString stunRelayTcpUser; + QString stunRelayTcpPass; + // QString + + // udp stuff + bool udpInitialized; + quint16 udpPort; + QHostAddress udpAddress; + + ~Private() + { + if (ice) { + ice->disconnect(q); + auto stopper = new IceStopper; + stopper->start(portReserver, QList() << ice); + } + } + + inline Jid remoteJid() const { return q->_pad->session()->peer(); } + + Component &addComponent() + { + components.append(Component {}); + auto &c = components.last(); + c.componentIndex = components.size() - 1; + return c; + } + + void setupDtls(int componentIndex) + { + qDebug("Setup DTLS"); + Q_ASSERT(componentIndex < components.length()); + if (components[componentIndex].dtls) + return; + components[componentIndex].dtls + = new Dtls(q, q->pad()->session()->me().full(), q->pad()->session()->peer().full()); + + auto dtls = components[componentIndex].dtls; + if (q->isLocal()) { + dtls->initOutgoing(); + } else { + dtls->setRemoteFingerprint(remoteState->fingerprint); + dtls->acceptIncoming(); + } + + if (componentIndex == 0) { // for other components it's the same but we don't need multiple fingerprints + dtls->connect( + dtls, &Dtls::needRestart, q, + [this, componentIndex]() { + pendingActions |= NewFingerprint; + remoteAcceptedFingerprint = false; + emit q->updated(); + }, + Qt::QueuedConnection); + pendingActions |= NewFingerprint; + } + dtls->connect(dtls, &Dtls::readyRead, q, [this, componentIndex]() { + auto &component = components[componentIndex]; + auto d = component.dtls->readDatagram(); +#ifdef JINGLE_SCTP + if (component.sctp) { + component.sctp->writeIncoming(d); + } +#endif + }); + dtls->connect(dtls, &Dtls::readyReadOutgoing, q, [this, componentIndex]() { + ice->writeDatagram(componentIndex, components[componentIndex].dtls->readOutgoingDatagram()); + }); + dtls->connect(dtls, &Dtls::connected, q, [this, componentIndex, dtls]() { + auto &c = components[componentIndex]; +#ifdef JINGLE_SCTP + if (c.sctp) { + // see rfc8864 (6.1) and rfc8832 (6) + c.sctp->setIdSelector(dtls->localFingerprint().setup == Dtls::Active ? SCTP::IdSelector::Even + : SCTP::IdSelector::Odd); + c.sctp->onTransportConnected(); + } +#endif + if (c.rawConnection) + c.rawConnection->onConnected(); + }); + dtls->connect(dtls, &Dtls::errorOccurred, q, [this, componentIndex](QAbstractSocket::SocketError error) { + qDebug("dtls failed for component %d", componentIndex); + auto &c = components[componentIndex]; +#ifdef JINGLE_SCTP + if (c.sctp) + c.sctp->onTransportError(error); +#endif + if (c.rawConnection) + c.rawConnection->onError(error); + }); + dtls->connect(dtls, &Dtls::closed, q, [this, componentIndex]() { + qDebug("dtls closed for component %d", componentIndex); + auto &c = components[componentIndex]; + if (c.rawConnection) + c.rawConnection->onDisconnected(RawConnection::DtlsClosed); +#ifdef JINGLE_SCTP + if (c.sctp) + c.sctp->onTransportClosed(); +#endif + }); + } + + void findStunAndTurn() + { + auto extDisco = q->pad()->session()->manager()->client()->externalServiceDiscovery(); + using namespace std::chrono_literals; + if (extDisco->isSupported()) { + extDisco->services(q, + [this](const ExternalServiceList &services) { + ExternalService::Ptr stun; + ExternalService::Ptr turnUdp; + ExternalService::Ptr turnTcp; + for (auto const &s : std::as_const(services)) { + if (s->type == QLatin1String("stun") + && (s->transport.isEmpty() || s->transport == QLatin1String("udp"))) + stun = s; + else if (s->type == QLatin1String("turn")) { + if (s->transport == QLatin1String("tcp")) + turnTcp = s; + else + turnUdp = s; + } + } + Resolver::ResolveList resList; + if (stun) { + stunBindAddr.setAddress(stun->host); + stunBindPort = stun->port; + if (stunBindAddr.isNull()) + resList.emplace_back(stun->host, std::ref(stunBindAddr)); + } + if (turnTcp) { + stunRelayTcpAddr.setAddress(turnTcp->host); + stunRelayTcpPort = turnTcp->port; + stunRelayTcpUser = turnTcp->username; + stunRelayTcpPass = turnTcp->password; + if (stunRelayTcpAddr.isNull()) + resList.emplace_back(turnTcp->host, std::ref(stunRelayTcpAddr)); + } + if (turnUdp) { + stunRelayUdpAddr.setAddress(turnUdp->host); + stunRelayUdpPort = turnUdp->port; + stunRelayUdpUser = turnUdp->username; + stunRelayUdpPass = turnUdp->password; + if (stunRelayUdpAddr.isNull()) + resList.emplace_back(turnUdp->host, std::ref(stunRelayUdpAddr)); + } + if (resList.empty()) { + startIce(); + } else { + Resolver::resolve(q, resList, [this]() { + qDebug("resolver finished"); + startIce(); + }); + } + }, + 5min, { "stun", "turn" }); + return; + } + + auto manager = dynamic_cast(q->pad()->manager())->d.get(); + stunBindPort = manager->stunBindPort; + stunRelayUdpPort = manager->stunRelayUdpPort; + stunRelayTcpPort = manager->stunRelayTcpPort; + stunRelayUdpUser = manager->stunRelayUdpUser; + stunRelayUdpPass = manager->stunRelayUdpPass; + stunRelayTcpUser = manager->stunRelayTcpUser; + stunRelayTcpPass = manager->stunRelayTcpPass; + Resolver::resolve(q, + { { manager->extHost, std::ref(extAddr) }, + { manager->stunBindHost, std::ref(stunBindAddr) }, + { manager->stunRelayUdpHost, std::ref(stunRelayUdpAddr) }, + { manager->stunRelayTcpHost, std::ref(stunRelayTcpAddr) } }, + [this]() { + qDebug("resolver finished"); + startIce(); + }); + } + + void startIce() + { + auto manager = dynamic_cast(q->_pad->manager())->d.get(); + + if (!stunBindAddr.isNull() && stunBindPort > 0) + qDebug("STUN service: %s;%d", qPrintable(stunBindAddr.toString()), stunBindPort); + if (!stunRelayUdpAddr.isNull() && stunRelayUdpPort > 0 && !stunRelayUdpUser.isEmpty()) + qDebug("TURN w/ UDP service: %s;%d", qPrintable(stunRelayUdpAddr.toString()), stunRelayUdpPort); + if (!stunRelayTcpAddr.isNull() && stunRelayTcpPort > 0 && !stunRelayTcpUser.isEmpty()) + qDebug("TURN w/ TCP service: %s;%d", qPrintable(stunRelayTcpAddr.toString()), stunRelayTcpPort); + + auto listenAddrs = Ice176::availableNetworkAddresses(); + + QList localAddrs; + + QStringList strList; + for (const QHostAddress &h : as_const(listenAddrs)) { + XMPP::Ice176::LocalAddress addr; + addr.addr = h; + localAddrs += addr; + strList += h.toString(); + } + + if (manager->basePort != -1) { + portReserver = new XMPP::UdpPortReserver(q); + portReserver->setAddresses(listenAddrs); + portReserver->setPorts(manager->basePort, 4); + } + + if (!strList.isEmpty()) { + printf("Host addresses:\n"); + for (const QString &s : as_const(strList)) + printf(" %s\n", qPrintable(s)); + } + + ice = new Ice176(q); + + q->connect(ice, &XMPP::Ice176::started, q, [this]() { + for (auto const &c : as_const(components)) { + if (c.lowOverhead) + ice->flagComponentAsLowOverhead(c.componentIndex); + } + }); + q->connect(ice, &XMPP::Ice176::error, q, [this](XMPP::Ice176::Error err) { + q->onFinish(Reason::Condition::ConnectivityError, QString("ICE failed: %1").arg(err)); + }); + q->connect(ice, &XMPP::Ice176::localCandidatesReady, q, + [this](const QList &candidates) { + pendingActions |= NewCandidate; + pendingLocalCandidates += candidates; + qDebug("discovered %lld local candidates", qsizetype(candidates.size())); + for (auto const &c : candidates) { + qDebug(" - %s:%d", qPrintable(c.ip.toString()), c.port); + } + emit q->updated(); + }); + q->connect(ice, &XMPP::Ice176::localGatheringComplete, q, [this]() { + pendingActions |= GatheringComplete; + emit q->updated(); + }); + q->connect( + ice, &XMPP::Ice176::readyToSendMedia, q, + [this]() { + qDebug("ICE reported ready to send media!"); + if (!components[0].dtls) // if empty + notifyRawConnected(); + else if (remoteAcceptedFingerprint) + for (const auto &c : as_const(components)) + c.dtls->onRemoteAcceptedFingerprint(); + }, + Qt::QueuedConnection); // signal is not DOR-SS + q->connect(ice, &Ice176::readyRead, q, [this](int componentIndex) { + // qDebug("ICE readyRead"); + auto buf = ice->readDatagram(componentIndex); + auto &component = components[componentIndex]; + if (component.dtls) { + component.dtls->writeIncomingDatagram(buf); + } else if (component.rawConnection) { + component.rawConnection->enqueueIncomingUDP(buf); + } + }); + + ice->setProxy(manager->stunProxy); + if (portReserver) + ice->setPortReserver(portReserver); + + // QList localAddrs; + // XMPP::Ice176::LocalAddress addr; + + // FIXME: the following is not true, a local address is not + // required, for example if you use TURN with TCP only + + // a local address is required to use ice. however, if + // we don't have a local address, we won't handle it as + // an error here. instead, we'll start Ice176 anyway, + // which should immediately error back at us. + /*if(manager->selfAddr.isNull()) + { + printf("no self address to use. this will fail.\n"); + return; + } + + addr.addr = manager->selfAddr; + localAddrs += addr;*/ + ice->setLocalAddresses(localAddrs); + + // if an external address is manually provided, then apply + // it only to the selfAddr. FIXME: maybe we should apply + // it to all local addresses? + if (!extAddr.isNull()) { + QList extAddrs; + /*XMPP::Ice176::ExternalAddress eaddr; + eaddr.base = addr; + eaddr.addr = extAddr; + extAddrs += eaddr;*/ + for (const XMPP::Ice176::LocalAddress &la : as_const(localAddrs)) { + XMPP::Ice176::ExternalAddress ea; + ea.base = la; + ea.addr = extAddr; + extAddrs += ea; + } + ice->setExternalAddresses(extAddrs); + } + + if (!stunBindAddr.isNull() && stunBindPort > 0) + ice->setStunBindService(stunBindAddr, stunBindPort); + if (!stunRelayUdpAddr.isNull() && !stunRelayUdpUser.isEmpty()) + ice->setStunRelayUdpService(stunRelayUdpAddr, stunRelayUdpPort, stunRelayUdpUser, + stunRelayUdpPass.toUtf8()); + if (!stunRelayTcpAddr.isNull() && !stunRelayTcpUser.isEmpty()) + ice->setStunRelayTcpService(stunRelayTcpAddr, stunRelayTcpPort, stunRelayTcpUser, + stunRelayTcpPass.toUtf8()); + ice->setStunDiscoverer(q->pad()->session()->manager()->client()->stunDiscoManager()->createMonitor()); + + ice->setComponentCount(components.count()); + ice->setLocalFeatures(Ice176::Trickle); + + setupRemoteICE(*remoteState); + remoteState->cleanupICE(); + + auto mode = q->creator() == q->pad()->session()->role() ? XMPP::Ice176::Initiator : XMPP::Ice176::Responder; + ice->start(mode); + } + + void setupRemoteICE(const Element &e) + { + Q_ASSERT(ice != nullptr); + if (!e.candidates.isEmpty()) { + ice->setRemoteCredentials(e.ufrag, e.pwd); + ice->addRemoteCandidates(e.candidates); + } + if (e.gatheringComplete) { + ice->setRemoteGatheringComplete(); + } + if (e.remoteCandidates.size()) { + ice->setRemoteSelectedCandidadates(e.remoteCandidates); + } + } + + void handleRemoteUpdate(const Element &e) + { + if (q->state() == State::Finished) + return; + + if (ice) { + setupRemoteICE(e); + } else { + if (!e.candidates.isEmpty() || !e.ufrag.isEmpty()) { + remoteState->ufrag = e.ufrag; + remoteState->pwd = e.pwd; + } + + if (e.gatheringComplete) { + remoteState->gatheringComplete = true; + } + remoteState->candidates += e.candidates; + if (e.remoteCandidates.size()) + remoteState->remoteCandidates = e.remoteCandidates; + } + if (e.fingerprint.isValid()) { + remoteState->fingerprint = e.fingerprint; + if (q->isLocal()) { + // dtls already created for local transport on remote accept + for (auto &c : components) { + if (c.dtls) + c.dtls->setRemoteFingerprint(e.fingerprint); + } + } + } +#ifdef JINGLE_SCTP + if (e.sctpMap.isValid()) { + remoteState->sctpMap = e.sctpMap; + remoteState->sctpChannels += e.sctpChannels; + } +#endif + if (q->state() == State::Created && q->isRemote()) { + // initial incoming transport + q->setState(State::Pending); + } + if (q->state() == State::Pending && q->isLocal()) { + // initial acceptance by remote of the local transport + q->setState(State::Accepted); + } + } + + bool isDataChannelSuppported() const { return Dtls::isSupported(); } + + void notifyRawConnected() + { + auto const &acceptors = q->acceptors(); + for (auto const &acceptor : acceptors) { + if (!(acceptor.features & TransportFeature::DataOriented)) + ensureRawConnection(acceptor.componentIndex); // TODO signals + } + for (auto &c : components) { + if (c.rawConnection) + c.rawConnection->onConnected(); + } + } + + /** + * @brief ensureComponentExist adds an ICE component if missed and it's allowed atm for a channel + * with given featuers. + * @param componentIndex - desired component index (-1 for auto) + * @return actually allocated component index (e.g. when -1 passed). + * returns -1 if something went wrong. + */ + int ensureComponentExist(int componentIndex, bool lowOverhead = false) + { + if (componentIndex == -1) { + for (auto &c : components) + if (!c.initialized) { + componentIndex = c.componentIndex; + break; + } + if (componentIndex < 0) + componentIndex = components.count(); + } + + if (componentIndex >= components.size()) { + if (ice) { + qWarning("Adding channel after negotiation start is not yet supported"); + return -1; + } + for (int i = components.size(); i < componentIndex + 1; i++) { + addComponent(); + } + } + auto &c = components[componentIndex]; + c.initialized = true; + if (lowOverhead) + c.lowOverhead = true; + return componentIndex; + } + + void ensureRawConnection(int componentIndex) + { + auto index = quint8(componentIndex); + if (components[index].rawConnection) + return; + components[index].rawConnection = RawConnection::Ptr::create(index); + if (q->isRemote() && !q->notifyIncomingConnection(components[index].rawConnection)) + components[index].rawConnection.reset(); + // Do we need anything else to do here? connect signals for example? + } +#ifdef JINGLE_SCTP + void initSctpAssociation(int componentIndex) + { + auto &c = components[componentIndex]; + Q_ASSERT(c.sctp == nullptr); + c.sctp = new SCTP::Association(q); + pendingActions |= NewSctpAssociation; + if (q->wasAccepted() && q->state() != State::ApprovedToSend) // like we already sent our decision + emit q->updated(); + if (remoteState->sctpMap.isValid()) { + // TODO if we already have associations params try to ruse them instead of making new one + } + q->connect(c.sctp, &SCTP::Association::readyReadOutgoing, q, [this, componentIndex]() { + auto &c = components[componentIndex]; + auto buf = c.sctp->readOutgoing(); + c.dtls->writeDatagram(buf); + }); + q->connect(c.sctp, &SCTP::Association::newIncomingChannel, q, [this, componentIndex]() { + qDebug("new incoming sctp channel"); + auto assoc = components[componentIndex].sctp; + auto channel = assoc->nextChannel(); + if (!q->notifyIncomingConnection(channel)) { + channel->close(); + } + }); + } + + Connection::Ptr addDataChannel(TransportFeatures channelFeatures, const QString &label, int &componentIndex) + { + if (componentIndex == -1) + componentIndex = 0; + + if (!Dtls::isSupported()) { + qWarning("An attempt to add a data channel while DTLS is not supported by current configuration"); + return {}; + } + componentIndex = ensureComponentExist(componentIndex, channelFeatures & TransportFeature::LowOverhead); + if (componentIndex < 0) + return {}; // failed to add component for the features + + auto &c = components[componentIndex]; + if (!c.sctp) { + // basically we can't accept remote transport with own stcp if it wasn't offered. + // but we can add new associations later with transport-info (undocumented in xep) + if (q->isRemote() && !q->wasAccepted() && !(remoteState && remoteState->sctpMap.isValid())) { + qWarning("remote hasn't negotiated sctp association"); + return {}; + } + initSctpAssociation(componentIndex); + } + Q_UNUSED(channelFeatures); // TODO + return c.sctp->newChannel(SCTP::Reliable, true, 0, 256, label); + } +#endif + }; + + Transport::Transport(const TransportManagerPad::Ptr &pad, Origin creator) : + XMPP::Jingle::Transport(pad, creator), d(new Private) + { + d->q = this; + d->ensureComponentExist(0); + d->remoteState.reset(new Element {}); + connect(_pad->manager(), &TransportManager::abortAllRequested, this, [this]() { + d->aborted = true; + onFinish(Reason::Cancel); + }); + } + + Transport::~Transport() + { + for (auto &c : d->components) { +#ifdef JINGLE_SCTP + delete c.sctp; +#endif + delete c.dtls; + } + qDebug("jingle-ice: destroyed"); + } + + void Transport::prepare() + { + qDebug("Prepare local offer"); + setState(State::ApprovedToSend); + auto const &a = acceptors(); + for (auto const &acceptor : a) { + int ci = acceptor.componentIndex < 0 ? 0 : acceptor.componentIndex; + d->ensureComponentExist(ci, acceptor.features & TransportFeature::LowOverhead); // it won't fail + if (acceptor.features & TransportFeature::DataOriented) + d->components[ci].needDatachannel = true; + } + + if (Dtls::isSupported() + && ((isLocal() && _pad->session()->checkPeerCaps(Dtls::FingerPrint::ns())) + || (isRemote() && d->remoteState->fingerprint.isValid()))) { + qDebug("initialize DTLS"); + + for (auto &c : d->components) { + d->setupDtls(c.componentIndex); +#ifdef JINGLE_SCTP + if (isRemote() && c.needDatachannel && !c.sctp) { + d->initSctpAssociation(c.componentIndex); + } +#endif + } + } + + d->findStunAndTurn(); + emit updated(); + } + + // we got content acceptance from any side and now can connect + void Transport::start() + { + qDebug("Starting connecting"); + setState(State::Connecting); + d->ice->startChecks(); + } + + bool Transport::update(const QDomElement &transportEl) + { + try { + Element e; + e.parse(transportEl); + QTimer::singleShot(0, this, [this, e]() { d->handleRemoteUpdate(e); }); + return true; + } catch (std::runtime_error &e) { + qWarning("Transport update failed: %s", e.what()); + return false; + } + } + + bool Transport::hasUpdates() const + { + return isValid() && d->pendingActions && d->ice && _state >= State::ApprovedToSend + && !(isRemote() && _state == State::Pending) + && (d->ice->isLocalGatheringComplete() || d->pendingLocalCandidates.size()); + } + + OutgoingTransportInfoUpdate Transport::takeOutgoingUpdate([[maybe_unused]] bool ensureTransportElement = false) + { + if (!hasUpdates()) { + return {}; + } + + qDebug("jingle-ice: taking outgoing update"); + Element e; + e.ufrag = d->ice->localUfrag(); + e.pwd = d->ice->localPassword(); + + bool hasFingerprint = d->pendingActions & Private::NewFingerprint; + if (hasFingerprint && d->components[0].dtls) { + e.fingerprint = d->components[0].dtls->localFingerprint(); + } + e.candidates = d->pendingLocalCandidates; + e.gatheringComplete = d->pendingActions & Private::GatheringComplete; + if (d->pendingActions & Private::RemoteCandidate) + e.remoteCandidates = d->ice->selectedCandidates(); + // TODO sctp + + d->pendingLocalCandidates.clear(); + d->pendingActions = 0; + + return OutgoingTransportInfoUpdate { e.toXml(_pad.staticCast()->session()->manager()->client()->doc()), + [this, trptr = QPointer(d->q), hasFingerprint](bool success) { + if (!success || !trptr) + return; + // if we send our fingerprint as a response to remotely initiated dtls + // then on response we are sure remote server started dtls server and + // we can connect now. + if (hasFingerprint) { + d->remoteAcceptedFingerprint = true; + } + if (hasFingerprint && d->ice && d->ice->canSendMedia()) + for (auto &c : d->components) + c.dtls->onRemoteAcceptedFingerprint(); + } }; + } + + bool Transport::isValid() const { return d != nullptr; } + + TransportFeatures Transport::features() const { return _pad->manager()->features(); } + + int Transport::maxSupportedChannelsPerComponent(TransportFeatures features) const + { + return features & TransportFeature::DataOriented ? 65536 : 1; + }; + + void Transport::setComponentsCount(int count) + { + if (_state >= State::ApprovedToSend) { + qWarning("adding component after ICE started is not supported"); + return; + } + for (int i = d->components.size(); i < count; i++) { + d->addComponent(); + } + } + + // adding ice channels/components (for rtp, rtcp, datachannel etc) + Connection::Ptr Transport::addChannel(TransportFeatures features, const QString &id, int componentIndex) + { +#ifdef JINGLE_SCTP + if (features & TransportFeature::DataOriented) + return d->addDataChannel(features, id, componentIndex); +#endif + componentIndex = d->ensureComponentExist(componentIndex, features & TransportFeature::LowOverhead); + if (componentIndex < 0) + return {}; // failed to add component for the features + d->ensureRawConnection(componentIndex); + auto &channel = d->components[componentIndex].rawConnection; + channel->setId(id); + return channel; + } + + QList Transport::channels() const + { + QList ret; + for (auto &c : d->components) { + if (c.rawConnection) + ret.append(c.rawConnection.staticCast()); +#ifdef JINGLE_SCTP + if (c.sctp) + ret += c.sctp->channels(); +#endif + } + return ret; + } + + //---------------------------------------------------------------- + // Manager + //---------------------------------------------------------------- + Manager::Manager(QObject *parent) : TransportManager(parent), d(new Private) { } + + Manager::~Manager() + { + if (d->jingleManager) { + d->jingleManager->unregisterTransport(NS); + } + } + + TransportFeatures Manager::features() const + { + return TransportFeature::HighProbableConnect | TransportFeature::Reliable | TransportFeature::Unreliable + | TransportFeature::MessageOriented | TransportFeature::LiveOriented +#ifdef JINGLE_SCTP + | (Dtls::isSupported() ? (TransportFeature::DataOriented | TransportFeature::Ordered) : TransportFeature(0)) +#endif + ; + } + + void Manager::setJingleManager(XMPP::Jingle::Manager *jm) { d->jingleManager = jm; } + + QSharedPointer Manager::newTransport(const TransportManagerPad::Ptr &pad, Origin creator) + { + return QSharedPointer::create(pad, creator).staticCast(); + } + + TransportManagerPad *Manager::pad(Session *session) { return new Pad(this, session); } + + QStringList Manager::ns() const { return { NS }; } + QStringList Manager::discoFeatures() const + { + return { NS, NS_DTLS +#ifdef JINGLE_SCTP + , + SCTP::ns() +#endif + }; + } + + void Manager::setBasePort(int port) { d->basePort = port; } + + void Manager::setExternalAddress(const QString &host) { d->extHost = host; } + + void Manager::setSelfAddress(const QHostAddress &addr) { d->selfAddr = addr; } + + void Manager::setStunBindService(const QString &host, int port) + { + d->stunBindHost = host; + d->stunBindPort = port; + } + + void Manager::setStunRelayUdpService(const QString &host, int port, const QString &user, const QString &pass) + { + d->stunRelayUdpHost = host; + d->stunRelayUdpPort = port; + d->stunRelayUdpUser = user; + d->stunRelayUdpPass = pass; + } + + void Manager::setStunRelayTcpService(const QString &host, int port, const XMPP::AdvancedConnector::Proxy &proxy, + const QString &user, const QString &pass) + { + d->stunRelayTcpHost = host; + d->stunRelayTcpPort = port; + d->stunRelayTcpUser = user; + d->stunRelayTcpPass = pass; + + XMPP::TurnClient::Proxy tproxy; + + if (proxy.type() == XMPP::AdvancedConnector::Proxy::HttpConnect) { + tproxy.setHttpConnect(proxy.host(), proxy.port()); + tproxy.setUserPass(proxy.user(), proxy.pass()); + } else if (proxy.type() == XMPP::AdvancedConnector::Proxy::Socks) { + tproxy.setSocks(proxy.host(), proxy.port()); + tproxy.setUserPass(proxy.user(), proxy.pass()); + } + + d->stunProxy = tproxy; + } + + //---------------------------------------------------------------- + // Pad + //---------------------------------------------------------------- + Pad::Pad(Manager *manager, Session *session) : _manager(manager), _session(session) + { + auto reserver = _session->manager()->client()->tcpPortReserver(); + _discoScope = reserver->scope(QString::fromLatin1("ice")); + } + + QString Pad::ns() const { return NS; } + + Session *Pad::session() const { return _session; } + + TransportManager *Pad::manager() const { return _manager; } + + void Pad::onLocalAccepted() + { + if (!_session->isGroupingAllowed() && _session->role() != Origin::Initiator) + return; + QStringList bundle; + for (auto app : session()->contentList()) { + auto transport = app->transport(); + if (transport && transport.dynamicCast()) { + // do grouping stuff + bundle.append(app->contentName()); + } + } + if (bundle.size() > 1) + _session->setGrouping(QLatin1String("BUNDLE"), bundle); + } + +} // namespace Ice +} // namespace Jingle +} // namespace XMPP + +#include "jingle-ice.moc" diff --git a/src/xmpp/xmpp-im/jingle-ice.h b/src/xmpp/xmpp-im/jingle-ice.h new file mode 100644 index 00000000..990d8b08 --- /dev/null +++ b/src/xmpp/xmpp-im/jingle-ice.h @@ -0,0 +1,138 @@ +/* + * jignle-ice.h - Jingle SOCKS5 transport + * Copyright (C) 2019 Sergey Ilinykh + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library. If not, see . + * + */ + +#ifndef JINGLE_ICE_H +#define JINGLE_ICE_H + +#include "iris/tcpportreserver.h" +#include "iris/xmpp.h" +#include "jingle-transport.h" + +class QHostAddress; + +namespace XMPP { +class Client; + +namespace Jingle { namespace ICE { + extern const QString NS; + + class Transport; + + class Manager; + class Transport : public XMPP::Jingle::Transport { + Q_OBJECT + public: + enum Mode { Tcp, Udp }; + + Transport(const TransportManagerPad::Ptr &pad, Origin creator); + ~Transport() override; + + void prepare() override; + void start() override; + bool update(const QDomElement &transportEl) override; + bool hasUpdates() const override; + OutgoingTransportInfoUpdate takeOutgoingUpdate(bool ensureTransportElement) override; + bool isValid() const override; + TransportFeatures features() const override; + int maxSupportedChannelsPerComponent(TransportFeatures features) const override; + + void setComponentsCount(int count) override; + Connection::Ptr addChannel(TransportFeatures features, const QString &id, int component = -1) override; + QList channels() const override; + + private: + friend class Manager; + + class Private; + std::unique_ptr d; + }; + + class Pad : public TransportManagerPad { + Q_OBJECT + // TODO + public: + typedef QSharedPointer Ptr; + + Pad(Manager *manager, Session *session); + QString ns() const override; + Session *session() const override; + TransportManager *manager() const override; + void onLocalAccepted() override; + + inline TcpPortScope *discoScope() const { return _discoScope; } + + private: + Manager *_manager; + Session *_session; + TcpPortScope *_discoScope; + bool _allowGrouping = false; + }; + + class Manager : public TransportManager { + Q_OBJECT + public: + Manager(QObject *parent = nullptr); + ~Manager() override; + + XMPP::Jingle::TransportFeatures features() const override; + void setJingleManager(XMPP::Jingle::Manager *jm) override; + QSharedPointer newTransport(const TransportManagerPad::Ptr &pad, + Origin creator) override; + TransportManagerPad *pad(Session *session) override; + + QStringList ns() const override; + QStringList discoFeatures() const override; + + // TODO reimplement closeAll to support old protocols + + /** + * @brief userProxy returns custom (set by user) SOCKS proxy JID + * @return + */ + // Jid userProxy() const; + // void setUserProxy(const Jid &jid); + + /** + * @brief addKeyMapping sets mapping between key/socks hostname used for direct connection and transport. + * The key is sha1(sid, initiator full jid, responder full jid) + * @param key + * @param transport + */ + void addKeyMapping(const QString &key, Transport *transport); + void removeKeyMapping(const QString &key); + + void setBasePort(int port); + void setExternalAddress(const QString &host); + void setSelfAddress(const QHostAddress &addr); + void setStunBindService(const QString &host, int port); + void setStunRelayUdpService(const QString &host, int port, const QString &user, const QString &pass); + void setStunRelayTcpService(const QString &host, int port, const XMPP::AdvancedConnector::Proxy &proxy, + const QString &user, const QString &pass); + // stunProxy() const; + + private: + friend class Transport; + class Private; + std::unique_ptr d; + }; +} // namespace Ice +} // namespace Jingle +} // namespace XMPP + +#endif // JINGLE_ICE_H diff --git a/src/xmpp/xmpp-im/jingle-nstransportslist.cpp b/src/xmpp/xmpp-im/jingle-nstransportslist.cpp new file mode 100644 index 00000000..257192c3 --- /dev/null +++ b/src/xmpp/xmpp-im/jingle-nstransportslist.cpp @@ -0,0 +1,86 @@ +/* + * jignle-nstransportslist.cpp - Simple transport selector based on sorted namespaces list + * Copyright (C) 2020 Sergey Ilinykh + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library. If not, see . + * + */ + +#include "jingle-nstransportslist.h" +#include "jingle-session.h" + +namespace XMPP { namespace Jingle { + + QSharedPointer NSTransportsList::getNextTransport() { return getNextNSTransport(); } + + QSharedPointer NSTransportsList::getAlikeTransport(QSharedPointer alike) + { + return getNextNSTransport(alike->pad()->ns()); + } + + QSharedPointer NSTransportsList::getNextNSTransport(const QString &preferredNS) + { + if (_transports.isEmpty()) { + return QSharedPointer(); + } + + int idx = _transports.indexOf(preferredNS); + if (idx == -1) + idx = _transports.size() - 1; + + do { + auto t = _session->newOutgoingTransport(_transports[idx]); + if (t) { + auto const discoFeatures = t->pad()->manager()->discoFeatures(); + // FIXME next if is quite stupid. instead need to check a minimal set of features of desired connection. + if (std::all_of(discoFeatures.begin(), discoFeatures.end(), + [this](auto const &f) { return _session->checkPeerCaps(f); })) + return t; + } + _transports.removeAt(idx); + idx = _transports.size() - 1; + } while (idx != -1); + + return QSharedPointer(); + } + + void NSTransportsList::backupTransport(QSharedPointer tr) { _transports += tr->pad()->ns(); } + + bool NSTransportsList::hasMoreTransports() const { return !_transports.isEmpty(); } + + bool NSTransportsList::hasTransport(QSharedPointer t) const + { + return t && _transports.indexOf(t->pad()->ns()) != -1; + } + + int NSTransportsList::compare(QSharedPointer a, QSharedPointer b) const + { + int idxA = -1; + if (a) + idxA = _transports.indexOf(a->pad()->ns()); + int idxB = -1; + if (b) + idxB = _transports.indexOf(b->pad()->ns()); + return idxA < idxB ? -1 : idxA > idxB ? 1 : 0; + } + + bool NSTransportsList::replace(QSharedPointer old, QSharedPointer newer) + { + if (!newer || !canReplace(old, newer)) + return false; + _transports.removeAll(newer->pad()->ns()); + return true; + } + +}} diff --git a/src/xmpp/xmpp-im/jingle-nstransportslist.h b/src/xmpp/xmpp-im/jingle-nstransportslist.h new file mode 100644 index 00000000..cc7c5363 --- /dev/null +++ b/src/xmpp/xmpp-im/jingle-nstransportslist.h @@ -0,0 +1,52 @@ +/* + * jignle-nstransportslist.h - Simple transport selector based on sorted namespaces list + * Copyright (C) 2020 Sergey Ilinykh + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library. If not, see . + * + */ + +#ifndef JINGLENSTRANSPORTSLIST_H +#define JINGLENSTRANSPORTSLIST_H + +#include "jingle-transport.h" + +namespace XMPP { namespace Jingle { + + class Session; + class NSTransportsList : public TransportSelector { + public: + inline NSTransportsList(Session *session, const QStringList &transports) : + _session(session), _transports(transports) + { + } + + QSharedPointer getNextTransport() override; + QSharedPointer getAlikeTransport(QSharedPointer alike) override; + + QSharedPointer getNextNSTransport(const QString &preferredNS = QString()); + + void backupTransport(QSharedPointer) override; + bool hasMoreTransports() const override; + bool hasTransport(QSharedPointer) const override; + int compare(QSharedPointer a, QSharedPointer b) const override; + bool replace(QSharedPointer old, QSharedPointer newer) override; + + private: + Session *_session = nullptr; + QStringList _transports; + }; + +}} +#endif diff --git a/src/xmpp/xmpp-im/jingle-s5b.cpp b/src/xmpp/xmpp-im/jingle-s5b.cpp index c895022d..929041bf 100644 --- a/src/xmpp/xmpp-im/jingle-s5b.cpp +++ b/src/xmpp/xmpp-im/jingle-s5b.cpp @@ -1,4 +1,4 @@ -/* +/* * jignle-s5b.cpp - Jingle SOCKS5 transport * Copyright (C) 2019 Sergey Ilinykh * @@ -18,791 +18,1792 @@ */ #include "jingle-s5b.h" + +#include "ice176.h" +#include "jingle-session.h" +#include "jingle.h" #include "s5b.h" +#include "socks.h" #include "xmpp/jid/jid.h" #include "xmpp_client.h" #include "xmpp_serverinfomanager.h" +#include +#include #include +#if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0) +#include +#endif +#include -namespace XMPP { -namespace Jingle { -namespace S5B { - -const QString NS(QStringLiteral("urn:xmpp:jingle:transports:s5b:1")); - -class Candidate::Private : public QSharedData { -public: - QString cid; - QString host; - Jid jid; - quint16 port; - quint32 priority; - Candidate::Type type = Candidate::Direct; - Candidate::State state = Candidate::New; -}; - -Candidate::Candidate() -{ - -} - -Candidate::Candidate(const QDomElement &el) -{ - bool ok; - QString host(el.attribute(QStringLiteral("host"))); - Jid jid(el.attribute(QStringLiteral("jid"))); - auto portStr = el.attribute(QStringLiteral("port")); - quint16 port = 0; - if (!portStr.isEmpty()) { - port = portStr.toUShort(&ok); - if (!ok) { - return; // make the whole candidate invalid - } - } - auto priorityStr = el.attribute(QStringLiteral("priority")); - if (priorityStr.isEmpty()) { - return; - } - quint32 priority = priorityStr.toUInt(&ok); - if (!ok) { - return; // make the whole candidate invalid - } - QString cid = el.attribute(QStringLiteral("cid")); - if (cid.isEmpty()) { - return; - } +namespace XMPP { namespace Jingle { namespace S5B { + const QString NS(QStringLiteral("urn:xmpp:jingle:transports:s5b:1")); - QString ct = el.attribute(QStringLiteral("type")); - if (ct.isEmpty()) { - ct = QStringLiteral("direct"); - } - static QMap types{{QStringLiteral("assisted"), Assisted}, - {QStringLiteral("direct"), Direct}, - {QStringLiteral("proxy"), Proxy}, - {QStringLiteral("tunnel"), Tunnel} - }; - auto candidateType = types.value(ct); - if (ct.isEmpty() || candidateType == None) { - return; + static QString makeKey(const QString &sid, const Jid &j1, const Jid &j2) + { + auto data = QString::fromLatin1( + QCryptographicHash::hash((sid + j1.full() + j2.full()).toUtf8(), QCryptographicHash::Sha1).toHex()); + qDebug() << "Generated key from:" << sid << j1.full() << j2.full() << " = " << data; + return data; } - auto d = new Private; - d->cid = cid; - d->host = host; - d->jid = jid; - d->port = port; - d->priority = priority; - d->type = candidateType; - d->state = New; - this->d = d; -} - -Candidate::Candidate(const Candidate &other) : - d(other.d) -{ - -} - -Candidate::Candidate(const Jid &proxy, const QString &cid, quint16 localPreference) : - d(new Private) -{ - d->cid = cid; - d->jid = proxy; - d->priority = (ProxyPreference << 16) + localPreference; - d->type = Proxy; - d->state = Probing; -} - -Candidate::Candidate(const QString &host, quint16 port, const QString &cid, Type type, quint16 localPreference) : - d(new Private) -{ - d->cid = cid; - d->host = host; - d->port = port; - d->type = type; - static const int priorities[] = {0, ProxyPreference, TunnelPreference, AssistedPreference, DirectPreference}; - if (type >= Type(0) && type <= Direct) { - d->priority = (priorities[type] << 16) + localPreference; - } else { - d->priority = 0; - } - d->state = New; -} - -Candidate::~Candidate() -{ - -} - -Candidate::Type Candidate::type() const -{ - return d->type; -} - -QString Candidate::cid() const -{ - return d->cid; -} - -Jid Candidate::jid() const -{ - return d->jid; -} - -QString Candidate::host() const -{ - return d->host; -} - -void Candidate::setHost(const QString &host) -{ - d->host = host; -} - -quint16 Candidate::port() const -{ - return d->port; -} - -void Candidate::setPort(quint16 port) -{ - d->port = port; -} - -Candidate::State Candidate::state() const -{ - return d->state; -} - -void Candidate::setState(Candidate::State s) -{ - d->state = s; -} - -quint32 Candidate::priority() const -{ - return d->priority; -} - -QDomElement Candidate::toXml(QDomDocument *doc) const -{ - auto e = doc->createElement(QStringLiteral("candidate")); - e.setAttribute(QStringLiteral("cid"), d->cid); - if (d->type == Proxy) { - e.setAttribute(QStringLiteral("jid"), d->jid.full()); - } - if (!d->host.isEmpty() && d->port) { - e.setAttribute(QStringLiteral("host"), d->host); - e.setAttribute(QStringLiteral("port"), d->port); - } - e.setAttribute(QStringLiteral("priority"), d->priority); + class Connection : public XMPP::Jingle::Connection { + Q_OBJECT - static const char *types[] = {"proxy", "tunnel", "assisted"}; // same order as in enum - if (d->type && d->type < Direct) { - e.setAttribute(QStringLiteral("type"), QLatin1String(types[d->type - 1])); - } - return e; -} - -class Transport::Private : public QSharedData { -public: - enum PendingActions { - NewCandidate, - CandidateUsed, - CandidateError, - Activated, - ProxyError + QList datagrams; + SocksClient *client = nullptr; + Transport::Mode mode = Transport::Tcp; + + public: + void setSocksClient(SocksClient *client, Transport::Mode mode) + { + if (!client || !client->isOpen()) { + qCritical("Failed to set closed SockClient connection %p", client); + return; + } + + this->client = client; + this->mode = mode; + + connect(client, &SocksClient::readyRead, this, &Connection::readyRead); + connect(client, &SocksClient::bytesWritten, this, &Connection::bytesWritten); + connect(client, &SocksClient::aboutToClose, this, &Connection::aboutToClose); + setOpenMode(client->openMode()); + emit connected(); + } + + TransportFeatures features() const + { + return TransportFeature::Fast | TransportFeature::DataOriented | TransportFeature::StreamOriented + | TransportFeature::Ordered | TransportFeature::Reliable; + } + + bool hasPendingDatagrams() const { return datagrams.size() > 0; } + + NetworkDatagram readDatagram(qint64 maxSize = -1) + { + Q_UNUSED(maxSize) // TODO or not? + return datagrams.size() ? datagrams.takeFirst() : NetworkDatagram(); + } + + qint64 bytesAvailable() const + { + if (client) + return client->bytesAvailable(); + else + return 0; + } + + qint64 bytesToWrite() const { return client ? client->bytesToWrite() : 0; } + + void close() + { + if (!client) { + // was never opened + return; + } + client->disconnect(this); + XMPP::Jingle::Connection::close(); + client->deleteLater(); + client = nullptr; + } + + protected: + qint64 writeData(const char *data, qint64 maxSize) + { + if (mode == Transport::Tcp) + return client->write(data, maxSize); + return 0; + } + + qint64 readData(char *data, qint64 maxSize) + { + if (client) + return client->read(data, maxSize); + else + return 0; + } + + private: + friend class Transport; + void enqueueIncomingUDP(const QByteArray &data) + { + datagrams.append(NetworkDatagram { data }); + emit readyRead(); + } }; - Transport *q = NULL; - Pad::Ptr pad; - bool aborted = false; - bool signalNegotiated = false; - bool remoteReportedCandidateError = false; - bool localReportedCandidateError = false; - bool proxyDiscoveryInProgress = false; - quint16 pendingActions; - int proxiesInDiscoCount = 0; - quint32 minimalPriority = 0; - Application *application = nullptr; - QMap localCandidates; // cid to candidate mapping - QMap remoteCandidates; - QSet> signalingCandidates; // origin here is session role. so for remote it's != session->role - Candidate localUsedCandidate; // we received "candidate-used" for this candidate - Candidate remoteUsedCandidate; - QString dstaddr; - QString sid; - Transport::Mode mode = Transport::Tcp; - - bool amISender() const - { - Q_ASSERT(application); - auto senders = application->senders(); - return senders == Origin::Both || senders == application->creator(); - } + class V6LinkLocalSocksConnector : public QObject { + Q_OBJECT - bool amIReceiver() const - { - Q_ASSERT(application); - auto senders = application->senders(); - return senders == Origin::Both || senders == negateOrigin(application->creator()); - } + QMap clients; + SocksClient *client = nullptr; - inline Jid remoteJid() const - { - return pad->session()->peer(); - } + public: + using QObject::QObject; - QString generateCid() const - { - QString cid; - do { - cid = QString("%1").arg(qrand() & 0xffff, 4, 16, QChar('0')); - } while (localCandidates.contains(cid) || remoteCandidates.contains(cid)); - return cid; - } + void connectToHost(const QHostAddress &proxyHost, int proxyPort, const QString &host, bool udpMode) + { + auto const interfaces = QNetworkInterface::allInterfaces(); + for (const QNetworkInterface &ni : interfaces) { + if (!(ni.flags() & (QNetworkInterface::IsUp | QNetworkInterface::IsRunning))) { + continue; + } + if (ni.flags() & QNetworkInterface::IsLoopBack) { + continue; + } + QList entries = ni.addressEntries(); + for (const QNetworkAddressEntry &na : entries) { + QHostAddress ha = na.ip(); + if (ha.protocol() == QAbstractSocket::IPv6Protocol && +#if QT_VERSION >= QT_VERSION_CHECK(5, 11, 0) + ha.isLinkLocal() +#else + XMPP::Ice176::isIPv6LinkLocalAddress(ha) +#endif + ) // && proxyHost.isInSubnet(ha, na.prefixLength()) + { + auto client = new SocksClient(this); + clients.insert(ni.name(), client); + break; + } + } + } - bool isDup(const Candidate &c) const - { - for (auto const &rc: remoteCandidates) { - if (c.host() == rc.host() && c.port() == rc.port()) { - return true; + if (!clients.size()) { + emit ready(); + return; + } + + QHostAddress ph(proxyHost); + for (auto it = clients.begin(); it != clients.end(); ++it) { + QString name = it.key(); + SocksClient *client = it.value(); + connect(client, &SocksClient::connected, this, [this, name, client]() { + this->client = client; + clients.remove(name); + qDeleteAll(clients); + emit ready(); + }); + connect( + client, &SocksClient::error, this, + [this, name, client](int error) { + Q_UNUSED(error) + clients.remove(name); + delete client; + if (clients.isEmpty()) { + emit ready(); + } + }, + Qt::QueuedConnection); + ph.setScopeId(name); + client->connectToHost(ph.toString(), proxyPort, host, 0, udpMode); } } - return false; - } - void updateSelfState() - { - // TODO code below is from handler of "candidate-used". it has to be updated - bool hasMoreCandidates = false; - for (auto &c: localCandidates) { - auto s = c.state(); - if (s < Candidate::Pending && c.priority() > localUsedCandidate.priority()) { - hasMoreCandidates = true; - continue; // we have more high priority candidates to be handled by remote + SocksClient *takeClient() + { + auto c = client; + if (c) { + c->setParent(nullptr); + client = nullptr; } - c.setState(Candidate::Discarded); + return c; } - if (hasMoreCandidates) { - return; + signals: + void ready(); + }; + + class Candidate::Private : public QObject, public QSharedData { + Q_OBJECT + public: + ~Private() + { + if (server && transport) { + server->unregisterKey(transport->directAddr()); + } + delete socksClient; } - // let's check remote candidates too before we decide to use this local candidate - for (auto &c: remoteCandidates) { - auto s = c.state(); - if (c.priority() > localUsedCandidate.priority() && (s == Candidate::Pending || - s == Candidate::Probing || - s == Candidate::New)) { - hasMoreCandidates = true; - continue; // we have more high priority candidates to be handled by remote - } else if (c.priority() == localUsedCandidate.priority() && s == Candidate::Unacked && - pad->session()->role() == Origin::Initiator) { - hasMoreCandidates = true; - continue; // see 2.4 Completing the Negotiation (p.4) + QPointer transport; + QString cid; + QString host; + Jid jid; + quint16 port = 0; + quint32 priority = 0; + Candidate::Type type = Candidate::Direct; + Candidate::State state = Candidate::New; + + QSharedPointer server; + SocksClient *socksClient = nullptr; + + QString toString() const + { + QString extra; + if (type == Tunnel || type == Assisted || type == Direct) { + extra = QString("host=%1:%2").arg(host, QString::number(port)); + } else if (type == Proxy) { + extra = QString("proxy=%1 host=%2:%3").arg(jid.full(), host, QString::number(port)); } - c.setState(Candidate::Discarded); // TODO stop any probing as well + return QString("Cadidate(type=%1 cid=%2 state=%3 %4)") + .arg(typeText(type), cid, Candidate::stateText(state), extra); } - if (hasMoreCandidates) { + void connectToHost(const QString &key, State successState, QObject *callbackContext, + std::function callback, bool isUdp) + { + QHostAddress ha(host); + if (!ha.isNull() && ha.protocol() == QAbstractSocket::IPv6Protocol && ha.scopeId().isEmpty() && +#if QT_VERSION >= QT_VERSION_CHECK(5, 11, 0) + ha.isLinkLocal() +#else + XMPP::Ice176::isIPv6LinkLocalAddress(ha) +#endif + ) { + qDebug() << "connect to " << toString() << "with key=" << key << "using V6LinkLocalSocksConnector"; + // we have link local address without scope. We have to enumerate all possible scopes. + auto v6llConnector = new V6LinkLocalSocksConnector(this); + connect(v6llConnector, &V6LinkLocalSocksConnector::ready, callbackContext, + [this, v6llConnector, callback, successState]() { + socksClient = v6llConnector->takeClient(); + delete v6llConnector; + if (state == Candidate::Discarded) { + return; + } + + if (socksClient) { + state = successState; + qDebug() << "connected: " << toString() << "socks client (ipv6)" << socksClient; + callback(true); + return; + } + state = Candidate::Discarded; + qDebug() << "failed to connect: " << toString() << "no socks client (ipv6)"; + callback(false); + }); + v6llConnector->connectToHost(ha, port, key, isUdp); + } else { + socksClient = new SocksClient; + qDebug() << "connect to " << toString() << "with key=" << key << " and socks client" << socksClient; + connect(socksClient, &SocksClient::connected, callbackContext, [this, callback, successState]() { + if (state == Candidate::Discarded) { + return; + } + state = successState; + qDebug() << "connected: " << toString() << "socks client" << socksClient; + callback(true); + }); + connect(socksClient, &SocksClient::error, callbackContext, [this, callback](int error) { + Q_UNUSED(error) + if (state == Candidate::Discarded) { + return; + } + state = Candidate::Discarded; + qDebug() << "failed to connect: " << toString() << "socks client" << socksClient; + callback(false); + }); + // connect(&t, SIGNAL(timeout()), SLOT(trySendUDP())); + + socksClient->connectToHost(host, port, key, 0, isUdp); + } + } + + void setupIncomingSocksClient() + { + connect(socksClient, &SocksClient::error, this, [this](int error) { + Q_UNUSED(error) + state = Candidate::Discarded; + }); + } + }; + + Candidate::Candidate() { } + + Candidate::Candidate(Transport *transport, const QDomElement &el) + { + bool ok; + QString host(el.attribute(QStringLiteral("host"))); + Jid jid(el.attribute(QStringLiteral("jid"))); + auto portStr = el.attribute(QStringLiteral("port")); + quint16 port = 0; + if (!portStr.isEmpty()) { + port = portStr.toUShort(&ok); + if (!ok) { + return; // make the whole candidate invalid + } + } + auto priorityStr = el.attribute(QStringLiteral("priority")); + if (priorityStr.isEmpty()) { + return; + } + quint32 priority = priorityStr.toUInt(&ok); + if (!ok) { + return; // make the whole candidate invalid + } + QString cid = el.attribute(QStringLiteral("cid")); + if (cid.isEmpty()) { return; } - // seems like we don't have better candidates, - // so we are going to use the d->localUsedCandidate - signalNegotiated = true; + QString ct = el.attribute(QStringLiteral("type")); + if (ct.isEmpty()) { + ct = QStringLiteral("direct"); + } + static QMap types { { QStringLiteral("assisted"), Assisted }, + { QStringLiteral("direct"), Direct }, + { QStringLiteral("proxy"), Proxy }, + { QStringLiteral("tunnel"), Tunnel } }; + auto candidateType = types.value(ct); + if (ct.isEmpty() || candidateType == None) { + return; + } + + if ((candidateType == Proxy && !jid.isValid()) || (candidateType != Proxy && (host.isEmpty() || !port))) { + return; + } + + auto d = new Private; + d->transport = transport; + d->cid = cid; + d->host = host; + d->jid = jid; + d->port = port; + d->priority = priority; + d->type = candidateType; + d->state = New; + this->d = d; + } + + Candidate::Candidate(const Candidate &other) : d(other.d) { } + + Candidate::Candidate(Transport *transport, const Jid &proxy, const QString &cid, quint16 localPreference) : + d(new Private) + { + d->transport = transport; + d->cid = cid; + d->jid = proxy; + d->priority = (ProxyPreference << 16) + localPreference; + d->type = Proxy; + d->state = Probing; // it's probing because it's a local side proxy and host and port are unknown } - void tryConnectToRemoteCandidate() + Candidate::Candidate(Transport *transport, const TcpPortServer::Ptr &server, const QString &cid, + quint16 localPreference) : d(new Private) { - if (application->state() != State::Connecting) { - return; // will come back later + Type type = None; + switch (server->portType()) { + case TcpPortServer::Direct: + type = Candidate::Direct; + break; + case TcpPortServer::NatAssited: + type = Candidate::Assisted; + break; + case TcpPortServer::Tunneled: + type = Candidate::Tunnel; + break; + case TcpPortServer::NoType: + break; } - quint64 priority = 0; - Candidate candidate; - for (auto &c: remoteCandidates) { - if ((c.state() == Candidate::New || c.state() == Candidate::Probing) && c.priority() > priority) { - candidate = c; - priority = c.priority(); - } + + if (type == None) { + d.reset(); + return; } - if (candidate && candidate.state() == Candidate::Probing) { - return; // already trying connection + + d->transport = transport; + d->server = server.staticCast(); + d->cid = cid; + d->host = server->publishHost(); + d->port = server->publishPort(); + d->type = type; + static const quint32 priorities[] + = { 0, ProxyPreference, TunnelPreference, AssistedPreference, DirectPreference }; + if (type >= Type(0) && type <= Direct) { + d->priority = (priorities[type] << 16) + localPreference; + } else { + d->priority = 0; } - // TODO start connecting to the candidate here + + d->state = New; } -}; - -Transport::Transport(const TransportManagerPad::Ptr &pad) : - d(new Private) -{ - d->q = this; - d->pad = pad.staticCast(); - connect(pad->manager(), &TransportManager::abortAllRequested, this, [this](){ - d->aborted = true; - emit failed(); - }); -} - -Transport::Transport(const TransportManagerPad::Ptr &pad, const QDomElement &transportEl) : - Transport::Transport(pad) -{ - d->dstaddr = transportEl.attribute(QStringLiteral("dstaddr")); - d->sid = transportEl.attribute(QStringLiteral("sid")); - if (d->sid.isEmpty() || !update(transportEl)) { - d.reset(); - return; + + Candidate::~Candidate() { } + + Candidate::Type Candidate::type() const { return d->type; } + + const char *Candidate::typeText(Candidate::Type t) + { + switch (t) { + case None: + return "Unibitialized"; + case Proxy: + return "Proxy"; + case Tunnel: + return "Tunnel"; + case Assisted: + return "Assisted"; + case Direct: + return "Direct"; + } + return "Unknown"; } -} -Transport::~Transport() -{ + QString Candidate::cid() const { return d->cid; } + + Jid Candidate::jid() const { return d->jid; } + + QString Candidate::host() const { return d->host; } + + void Candidate::setHost(const QString &host) { d->host = host; } + + quint16 Candidate::port() const { return d->port; } + + void Candidate::setPort(quint16 port) { d->port = port; } -} + quint16 Candidate::localPort() const { return quint16(d->server ? d->server->serverPort() : 0); } -TransportManagerPad::Ptr Transport::pad() const -{ - return d->pad.staticCast(); -} + QHostAddress Candidate::localAddress() const { return d->server ? d->server->serverAddress() : QHostAddress(); } -void Transport::setApplication(Application *app) -{ - d->application = app; - if (app->creator() == d->pad->session()->role()) { // I'm a creator - d->sid = d->pad->generateSid(); + Candidate::State Candidate::state() const { return d->state; } + + void Candidate::setState(Candidate::State s) + { + // don't close sockets here since pending events may change state machine or remote side and closed socket + // may break it + d->state = s; } - d->pad->registerSid(d->sid); -} -void Transport::prepare() -{ - auto m = static_cast(d->pad->manager()); + const char *Candidate::stateText(Candidate::State s) + { + switch (s) { + case New: + return "New"; + case Probing: + return "Probing"; + case Pending: + return "Pending"; + case Unacked: + return "Unacked"; + case Accepted: + return "Accepted"; + case Activating: + return "Activating"; + case Active: + return "Active"; + case Discarded: + return "Discarded"; + } + return nullptr; + } - auto serv = m->socksServ(); - if (serv) { - for(auto const &h: serv->hostList()) { - Candidate c(h, serv->port(), d->generateCid(), Candidate::Direct); - if (!d->isDup(c)) { - d->localCandidates.insert(c.cid(), c); - } + quint32 Candidate::priority() const { return d->priority; } + + QDomElement Candidate::toXml(QDomDocument *doc) const + { + auto e = doc->createElement(QStringLiteral("candidate")); + e.setAttribute(QStringLiteral("cid"), d->cid); + if (d->type == Proxy) { + e.setAttribute(QStringLiteral("jid"), d->jid.full()); + } + if (!d->host.isEmpty() && d->port) { + e.setAttribute(QStringLiteral("host"), d->host); + e.setAttribute(QStringLiteral("port"), d->port); + } + e.setAttribute(QStringLiteral("priority"), d->priority); + + static const char *types[] = { "proxy", "tunnel", "assisted" }; // same order as in enum + if (d->type && d->type < Direct) { + e.setAttribute(QStringLiteral("type"), QLatin1String(types[d->type - 1])); } + return e; + } + + QString Candidate::toString() const + { + if (d) { + return d->toString(); + } else + return QString("Candidate(null)"); } - Jid proxy = m->userProxy(); - if (proxy.isValid()) { - Candidate c(proxy, d->generateCid()); - if (!d->isDup(c)) { - d->localCandidates.insert(c.cid(), c); + // connect to the host and sets successState on success or discards the cadidate. + // If the candidate was discarded before the connection is finished, then the passed callback won't be called. + void Candidate::connectToHost(const QString &key, State successState, QObject *callbackContext, + std::function callback, bool isUdp) + { + d->connectToHost(key, successState, callbackContext, callback, isUdp); + } + + bool Candidate::incomingConnection(SocksClient *sc) + { + qDebug() << "incoming connection on" << d->cid << "candidate with socks client" << sc; + if (d->socksClient) { + return false; } + d->socksClient = sc; + d->setupIncomingSocksClient(); + return true; } - d->proxyDiscoveryInProgress = true; - QList> featureOptions = {{"http://jabber.org/protocol/bytestreams"}}; - d->pad->session()->manager()->client()->serverInfoManager()-> - queryServiceInfo(QStringLiteral("proxy"), - QStringLiteral("bytestreams"), - featureOptions, - QRegExp("proxy.*|socks.*|stream.*|s5b.*"), - ServerInfoManager::SQ_CheckAllOnNoMatch, - [this](const QList &items) + SocksClient *Candidate::takeSocksClient() { - if (d->minimalPriority >= (Candidate::TunnelPreference << 16)) { - // seems like we have successful connection via higher priority channel. so nobody cares about proxy - d->proxyDiscoveryInProgress = false; - return; + qDebug() << "taking socks client" << d->socksClient << "from " << d->cid << "candidate"; + if (!d->socksClient) { + return nullptr; + } + auto c = d->socksClient; + d->socksClient = nullptr; + d->disconnect(c); + return c; + } + + void Candidate::deleteSocksClient() + { + if (d->socksClient) { + qDebug("deleting socks client %p", d->socksClient); + d->socksClient->disconnect(); + delete d->socksClient; + d->socksClient = nullptr; + } + } + + TcpPortServer::Ptr Candidate::server() const { return d->server.staticCast(); } + + bool Candidate::isConnected() const { return d->socksClient != nullptr; } + + bool Candidate::operator==(const Candidate &other) const { return d.data() == other.d.data(); } + + // ------------------------------------------------------------------ + // Transport::Private + // ------------------------------------------------------------------ + class Transport::Private { + public: + enum PendingActions { NewCandidate = 1, CandidateUsed = 2, CandidateError = 4, Activated = 8, ProxyError = 16 }; + + Transport *q = nullptr; + bool p2pAllowed = true; + bool offerSent = false; + bool waitingAck = true; + bool aborted = false; + bool remoteReportedCandidateError = false; + bool localReportedCandidateError = false; + bool proxyDiscoveryInProgress = false; // if we have valid proxy requests + quint16 pendingActions = 0; + int proxiesInDiscoCount = 0; + QStringList localCandidatesTrack; + std::map localCandidates; // cid to candidate mapping + std::map remoteCandidates; + Candidate localUsedCandidate; // we received "candidate-used" for this candidate from localCandidates list + Candidate remoteUsedCandidate; // we sent "candidate-used" for this candidate from remoteCandidates list + QString dstaddr; // an address for xmpp proxy as it comes from remote. each side calculates it like sha1(sid + // + local jid + remote jid) + QString directAddr; // like dstaddr but for direct connection. Basically it's sha1(sid + initiator jid + + // responder jid) + QString sid; + Transport::Mode mode = Transport::Tcp; + QTimer probingTimer; + QTimer negotiationFinishTimer; + QElapsedTimer lastConnectionStart; + size_t blockSize = 8192; + TcpPortDiscoverer *disco = nullptr; + + QSharedPointer connection; + + // udp stuff + bool udpInitialized; + quint16 udpPort; + QHostAddress udpAddress; + + Private() : connection(QSharedPointer::create()) { } + + inline Jid remoteJid() const { return q->_pad->session()->peer(); } + + QString generateCid() const + { + QString cid; + do { +#if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0) + cid = QString("%1").arg(QRandomGenerator::global()->generate() & 0xffff, 4, 16, QChar('0')); +#else + cid = QString("%1").arg(qrand() & 0xffff, 4, 16, QChar('0')); +#endif + } while (localCandidates.count(cid) || remoteCandidates.count(cid)); + return cid; + } + + bool isDup(const Candidate &c) const + { + for (auto const &[_, rc] : remoteCandidates) { + if (c.host() == rc.host() && c.port() == rc.port()) { + return true; + } + } + return false; } - auto m = static_cast(d->pad->manager()); - Jid userProxy = m->userProxy(); // queries proxy's host/port and sends the candidate to remote - auto queryProxy = [this](const Jid &j, const QString &cid) { - d->proxiesInDiscoCount++; - auto query = new JT_S5B(d->pad->session()->manager()->client()->rootTask()); - connect(query, &JT_S5B::finished, this, [this,query,cid](){ + void queryS5BProxy(const Jid &j, const QString &cid) + { + proxiesInDiscoCount++; + auto query = new JT_S5B(q->_pad->session()->manager()->client()->rootTask()); + connect(query, &JT_S5B::finished, q, [this, query, cid]() { + if (!proxyDiscoveryInProgress) { + return; + } bool candidateUpdated = false; - if (query->success()) { - auto sh = query->proxyInfo(); - auto c = d->localCandidates.value(cid); - if (c && c.state() == Candidate::Probing) { // it can discarded by this moment. so we have to check. + auto it = localCandidates.find(cid); + if (it != localCandidates.end() && it->second.state() == Candidate::Probing) { + auto &c = it->second; + auto sh = query->proxyInfo(); + if (query->success() && !sh.host().isEmpty() && sh.port()) { + // it can be discarded by this moment (e.g. got success on a higher priority + // candidate). so we have to check. c.setHost(sh.host()); - c.setPort(sh.port()); + c.setPort(quint16(sh.port())); c.setState(Candidate::New); candidateUpdated = true; + pendingActions |= Private::NewCandidate; + } else { + c.setState(Candidate::Discarded); } } - d->proxiesInDiscoCount--; - if (!d->proxiesInDiscoCount) { - d->proxyDiscoveryInProgress = false; + proxiesInDiscoCount--; + if (!proxiesInDiscoCount) { + proxyDiscoveryInProgress = false; } if (candidateUpdated) { - d->pendingActions |= Private::NewCandidate; - emit updated(); + emit q->updated(); + } else if (!proxiesInDiscoCount) { + // it's possible it was our last hope and probaby we have to send candidate-error now. + checkAndFinishNegotiation(); } }); query->requestProxyInfo(j); query->go(true); - }; + } - bool userProxyFound = !userProxy.isValid(); - for (const auto i: items) { - int localPref = 0; - if (!userProxyFound && i.jid() == userProxy) { - localPref = 1; - userProxyFound = true; - } - Candidate c(i.jid(), d->generateCid(), localPref); - d->localCandidates.insert(c.cid(), c); - - queryProxy(i.jid(), c.cid()); - } - if (!userProxyFound) { - Candidate c(userProxy, d->generateCid(), 1); - d->localCandidates.insert(c.cid(), c); - queryProxy(userProxy, c.cid()); - } else if (items.count() == 0) { - // seems like we don't have any proxy - d->proxyDiscoveryInProgress = false; - // but it's possible it was our last hope and probaby we have to send candidate-error now. - // so the situation: we discarded all remote candidates (failed to connect) - // and all our candidates were already sent to remote - // if we send candidate-error while we have unsent candidates this may trigger transport failure. - // So for candidate-error two conditions have to be met 1) all remote failed 2) all local were sent no more - // local candidates are expected to be discovered - for (const auto &c: d->remoteCandidates) { - if (c.state() != Candidate::Discarded) { - // we have other cadidates to handle. so we don't need candidate-error to be sent to remote yet - return; + void discoS5BProxy() + { + auto m = static_cast(q->_pad->manager()); + Jid proxy = m->userProxy(); + if (proxy.isValid()) { + Candidate c(q, proxy, generateCid()); + if (!isDup(c)) { + qDebug("new local candidate: %s", qPrintable(c.toString())); + localCandidates.emplace(c.cid(), c); + queryS5BProxy(c.jid(), c.cid()); + } + } + + proxyDiscoveryInProgress = true; + QList> featureOptions = { { "http://jabber.org/protocol/bytestreams" } }; + q->_pad->session()->manager()->client()->serverInfoManager()->queryServiceInfo( + QStringLiteral("proxy"), QStringLiteral("bytestreams"), featureOptions, + QRegularExpression("proxy.*|socks.*|stream.*|s5b.*"), ServerInfoManager::SQ_CheckAllOnNoMatch, + [this](const QList &items) { + if (!proxyDiscoveryInProgress) { // check if new results are ever/still expected + // seems like we have successful connection via higher priority channel. so nobody cares + // about proxy + return; + } + auto m = static_cast(q->_pad->manager()); + Jid userProxy = m->userProxy(); + + bool userProxyFound = !userProxy.isValid(); + for (const auto &i : items) { + quint16 localPref = 0; + if (!userProxyFound && i.jid() == userProxy) { + localPref = 1; + userProxyFound = true; + continue; + } + Candidate c(q, i.jid(), generateCid(), localPref); + localCandidates.emplace(c.cid(), c); + qDebug("new local candidate: %s", qPrintable(c.toString())); + queryS5BProxy(i.jid(), c.cid()); + } + if (!userProxyFound) { + Candidate c(q, userProxy, generateCid(), 1); + localCandidates.emplace(c.cid(), c); + qDebug("new local candidate: %s", qPrintable(c.toString())); + queryS5BProxy(userProxy, c.cid()); + } else if (items.count() == 0) { + // seems like we don't have any proxy + proxyDiscoveryInProgress = false; + checkAndFinishNegotiation(); + } + }); + } + + void tryConnectToRemoteCandidate() + { + if (q->_state < State::Accepted) { + return; // will come back later + } + quint64 maxProbingPrio = 0; + quint64 maxNewPrio = 0; + Candidate maxProbing; + QList maxNew; // keeps highest (same) priority New candidates + + /* + We have to find highest-priority already connecting candidate and highest-priority new candidate. + If already-connecting is not found then start connecting to new if it's found. + If both already-connecting and new are found then + if new candidate has higher priority or the same priority then start connecting + else ensure the new candidate starts connecting in 200ms after previous connection attempt + (if it's in future then reschedule this call for future) + In all the other cases just return and wait for events. + */ + + qDebug("tryConnectToRemoteCandidate()"); + for (auto &[cid, c] : remoteCandidates) { + if (c.state() == Candidate::New) { + if (c.priority() > maxNewPrio) { + maxNew = QList(); + maxNew.append(c); + maxNewPrio = c.priority(); + } else if (c.priority() == maxNewPrio) { + maxNew.append(c); + } + } + if (c.state() == Candidate::Probing && c.priority() > maxProbingPrio) { + maxProbing = c; + maxProbingPrio = c.priority(); + } + } + if (maxNew.isEmpty()) { + qDebug(" tryConnectToRemoteCandidate() no maxNew candidates"); + return; // nowhere to connect + } + + // check if we have to hang on for a little if a higher priority candidate is Probing + if (maxProbing) { + if (maxNewPrio < maxProbing.priority()) { + if (probingTimer.isActive()) { + qDebug(" tryConnectToRemoteCandidate() timer is already active. let's wait"); + return; // we will come back here soon + } + qint64 msToFuture = 200 - lastConnectionStart.elapsed(); + if (msToFuture > 0) { // seems like we have to rescheduler for future + probingTimer.start(int(msToFuture)); + qDebug(" tryConnectToRemoteCandidate() too early. timer started. let's wait"); + return; + } + } + } + probingTimer.start(200); // for the next candidate if any + + // now we have to connect to maxNew candidates + for (auto &mnc : maxNew) { + lastConnectionStart.start(); + QString key = mnc.type() == Candidate::Proxy ? dstaddr : directAddr; + mnc.setState(Candidate::Probing); + mnc.connectToHost( + key, Candidate::Pending, q, + [this, mnc](bool success) { + // candidate's status had to be changed by connectToHost, so we don't set it again + if (success) { + // let's reject candidates which are meaningless to try + bool hasUnckeckedNew = false; + for (auto &[cid, c] : remoteCandidates) { + if (c.state() == Candidate::New) { + if (c.priority() <= mnc.priority()) { + c.setState(Candidate::Discarded); + } else { + hasUnckeckedNew = true; + } + } + } + if (!hasUnckeckedNew) { + pendingActions &= ~Private::NewCandidate; // just if we had it for example after + // proxy discovery + } + setLocalProbingMinimalPreference(mnc.priority() >> 16); + updateMinimalPriorityOnConnected(); + } + checkAndFinishNegotiation(); + }, + mode == Transport::Udp); + } + } + + /** + * @brief limitTcpDiscoByMinimalPreference take upper part of candidate preference (type preference) + * and drops lower priority pending local servers disco + * @param preference + */ + void setLocalProbingMinimalPreference(quint32 preference) + { + if (proxyDiscoveryInProgress && preference > Candidate::ProxyPreference) { + proxyDiscoveryInProgress = false; // doesn't make sense anymore + } + + // and now local ports discoverer.. + if (!disco) { + return; + } + TcpPortServer::PortTypes types = TcpPortServer::NoType; + if (p2pAllowed) { + types |= TcpPortServer::Direct; + if (preference >= Candidate::AssistedPreference) { + types |= TcpPortServer::NatAssited; } + if (preference >= Candidate::TunnelPreference) { + types |= TcpPortServer::Tunneled; + } + } + disco->setTypeMask(types); + if (disco->isDepleted()) { + delete disco; + disco = nullptr; } + } + + bool hasUnaknowledgedLocalCandidates() const + { // now ensure all local were sent to remote and no hope left - for (const auto &c: d->localCandidates) { + if (proxyDiscoveryInProgress || (disco && !disco->isDepleted())) { + qDebug("still has: either s5b proxy or host candidates disco in progress"); + return true; + } + + // now local candidates + for (const auto &[_, c] : localCandidates) { auto s = c.state(); - if (s == Candidate::Probing || s == Candidate::New) { - return; + if (s == Candidate::Probing || s == Candidate::New || s == Candidate::Unacked) { + qDebug("still has: a local candidte cid=%s in %s state", qPrintable(c.cid()), + qPrintable(c.stateText(s))); + return true; } } - d->pendingActions |= Private::CandidateError; + + return false; } - }); - for (auto const &c: d->localCandidates) { - d->signalingCandidates.insert(QPair{c.cid(),d->pad->session()->role()}); - } + Candidate preferredUsedCandidate() const + { + if (localUsedCandidate) { + if (remoteUsedCandidate) { + if (localUsedCandidate.priority() == remoteUsedCandidate.priority()) { + if (q->_pad->session()->role() == Origin::Initiator) { + qDebug("Both sides have condidate-used with same priority. Our(inititator) selection is " + "preferred"); + return remoteUsedCandidate; + } + qDebug("Both sides have condidate-used with same priority. Remote(initiator) selection is " + "preferred"); + return localUsedCandidate; + } + return localUsedCandidate.priority() > remoteUsedCandidate.priority() ? localUsedCandidate + : remoteUsedCandidate; + } + return localUsedCandidate; + } + return remoteUsedCandidate; + } + + // We come here when both sides reported either candidate-used or candidate-error + void onBothSidesFinished() + { + // so remote seems to be finished too. + // tell application about it and it has to change its state immediatelly + auto c = preferredUsedCandidate(); + bool bothErrors = localReportedCandidateError && remoteReportedCandidateError; + if (!bothErrors && c) { + if (c.state() != Candidate::Active) { + if (c.type() == Candidate::Proxy) { // local proxy + // If it's proxy, first it has to be activated + if (c == localUsedCandidate) { + if (c.state() == Candidate::Activating) { + qDebug("The proxy cid=%s is still activating", qPrintable(c.cid())); + return; + } + // it's our side who offered proxy. so we have to connect to it and activate + auto key = makeKey(sid, q->_pad->session()->manager()->client()->jid(), + q->_pad->session()->peer()); + + qDebug("Connect to proxy offered by local side (cid=%s) and activate it", + qPrintable(c.cid())); + c.setState(Candidate::Activating); + c.connectToHost( + key, Candidate::Activating, q, + [this, c](bool success) { + if (!success) { + pendingActions |= Private::ProxyError; + emit q->updated(); + return; + } + + auto query = new JT_S5B(q->_pad->session()->manager()->client()->rootTask()); + connect(query, &JT_S5B::finished, q, [this, c, query]() { + if (c.state() != Candidate::Activating) { + qDebug("Proxy candidate cid=%s was changed state while we were " + "trying " + "to activate(activate) it. Ignore the result", + qPrintable(c.cid())); + return; + } + if (!query->success()) { + pendingActions |= Private::ProxyError; + emit q->updated(); + return; + } + pendingActions |= Private::Activated; + localUsedCandidate.setState(Candidate::Active); + emit q->updated(); + handleConnected(localUsedCandidate); + }); + query->requestActivation(localUsedCandidate.jid(), sid, q->_pad->session()->peer()); + query->go(true); + }, + mode == Transport::Udp); + } // else so it's remote proxy. let's just wait for from remote + } else { + c.setState(Candidate::Active); + } + } + if (c.state() == Candidate::Active) { + handleConnected(c); + } else + qDebug("checkAndFinishNegotiation not finished: preferred is not Active"); + } else { // both sides reported candidate error + q->onFinish(Reason::Condition::ConnectivityError, QLatin1String("both sides reported candidate error")); + } + } - // TODO nat-assisted candidates.. - emit updated(); -} - -void Transport::start() -{ - // TODO start connecting to remote candidates -} - -bool Transport::update(const QDomElement &transportEl) -{ - QString contentTag(QStringLiteral("candidate")); - bool candidatesAdded = false;; - for(QDomElement ce = transportEl.firstChildElement(contentTag); - !ce.isNull(); ce = ce.nextSiblingElement(contentTag)) { - Candidate c(ce); - if (!c) { + void sendCandidateUsedOrError() + { + qDebug("checkAndFinishNegotiation not finished: trying to send condidate-used/error if any"); + // if we are here then neither candidate-used nor candidate-error was sent to remote, + // but we can send it now. + // first let's check if we can send candidate-used + bool allRemoteDiscarded = true; + bool hasConnectedRemoteCandidate = false; + QString states; + for (const auto &[_, c] : remoteCandidates) { + auto s = c.state(); + states += QString("\n%1").arg(c.toString()); + if (s != Candidate::Discarded) { + allRemoteDiscarded = false; + } + if (s == Candidate::Pending) { // connected but not yet sent + hasConnectedRemoteCandidate = true; + } + } + qDebug().noquote() << "Candidates dump:" << states; + + // if we have connection to remote candidate it's time to send it + if (hasConnectedRemoteCandidate) { + pendingActions |= Private::CandidateUsed; + qDebug("sendCandidateUsedOrError: sending used"); + emit q->updated(); + return; + } + + if (allRemoteDiscarded) { + pendingActions |= Private::CandidateError; + qDebug("sendCandidateUsedOrError: sending error"); + emit q->updated(); + return; + } + + qDebug("checkAndFinishNegotiation not finished: there are more remote candidates to try"); + // apparently we haven't connected anywhere but there are more remote candidates to try + } + + void checkAndFinishNegotiation() + { + // Why we can't send candidate-used/error right when this happens: + // so the situation: we discarded all remote candidates (failed to connect) + // but we have some local candidates which are still in Probing state (upnp for example) + // if we send candidate-error while we have unsent candidates this may trigger transport failure. + // So for candidate-error two conditions have to be met 1) all remote failed 2) all local were sent no + // more local candidates are expected to be discovered + + if (q->_state != State::Connecting) { // if not started or already finished + qDebug("checkAndFinishNegotiation not finished: q->_state != State::Connecting"); + return; + } + + // sort out already handled states or states which will bring us here a little later + if (waitingAck || pendingActions || hasUnaknowledgedLocalCandidates()) { + // waitingAck some query waits for ack and in the callback this func will be called again + // pendingActions means we reported to app we have data to send but the app didn't take this data + // yet, but as soon as it's taken it will switch to waitingAck. And with unacknowledged local + // candidates we can't send used/error as well as report connected()/failure() until tried them all + qDebug("checkAndFinishNegotiation not finished: waitingAck=%d || pendingActions=%x || " + "hasUnaknowledgedLocalCandidates()=%d", + int(waitingAck), int(pendingActions), int(hasUnaknowledgedLocalCandidates())); + return; + } + + bool localFinished = localReportedCandidateError || remoteUsedCandidate; + bool remoteFinished = remoteReportedCandidateError || localUsedCandidate; + + if (!localFinished || !remoteFinished) { + qDebug("checkAndFinishNegotiation: local=%s remote=%s", localFinished ? "finished" : "in-progress", + remoteFinished ? "finished" : "in-progress"); + if (!localFinished) + sendCandidateUsedOrError(); + return; + } + + onBothSidesFinished(); + } + + // take used-candidate with highest priority and discard all with lower. also update used candidates + // themselves + void updateMinimalPriorityOnConnected() + { + quint32 prio = 0; + if (localUsedCandidate && localUsedCandidate.state() != Candidate::Discarded) { + prio = localUsedCandidate.priority(); + } + // find highest priority within connected remote candidates + for (const auto &[_, c] : remoteCandidates) { + if (c.state() != Candidate::Discarded && c.state() >= Candidate::Pending && c.priority() > prio) { + prio = c.priority(); + } + } + + for (auto &[_, c] : localCandidates) { + if (c.priority() < prio && c.state() != Candidate::Discarded) { + c.setState(Candidate::Discarded); + } + } + for (auto &[_, c] : remoteCandidates) { + if (c.priority() < prio && c.state() != Candidate::Discarded) { + c.setState(Candidate::Discarded); + } + } + prio >>= 16; + setLocalProbingMinimalPreference(prio); + // if we discarded "used" candidates then reset them to invalid + if (localUsedCandidate && localUsedCandidate.state() == Candidate::Discarded) { + localUsedCandidate = Candidate(); + } + if (remoteUsedCandidate && remoteUsedCandidate.state() == Candidate::Discarded) { + remoteUsedCandidate = Candidate(); + } + if (localUsedCandidate && remoteUsedCandidate) { + if (q->_pad->session()->role() == Origin::Initiator) { + // i'm initiator. see 2.4.4 + localUsedCandidate.setState(Candidate::Discarded); + localUsedCandidate = Candidate(); + remoteReportedCandidateError = true; // as a sign of completeness even if not true + } else { + remoteUsedCandidate.setState(Candidate::Discarded); + remoteUsedCandidate = Candidate(); + localReportedCandidateError = true; // as a sign of completeness even if not true + } + } + + // now check and reset NewCandidate pending action + bool haveNewCandidates = false; + for (auto &[_, c] : remoteCandidates) { + if (c.state() == Candidate::New) { + haveNewCandidates = true; + break; + } + } + if (!haveNewCandidates) { + pendingActions &= ~NewCandidate; + } + + negotiationFinishTimer.start(); + } + + void onLocalServerDiscovered() + { + bool hasNewCandidates = false; + const auto &servers = disco->takeServers(); + for (const auto &serv : servers) { + auto s5bserv = serv.staticCast(); + s5bserv->registerKey(directAddr); + Candidate c(q, serv, generateCid()); + if (c.isValid() && !isDup(c) && c.priority()) { + QObject::connect(s5bserv.data(), &S5BServer::incomingConnection, q, + [this, c](SocksClient *sc, const QString &key) mutable { + if (!connection->client && key == directAddr + && (c.state() == Candidate::Pending || c.state() == Candidate::Unacked)) { + c.incomingConnection(sc); + c.server().data()->disconnect(q); // drop this connection. + if (mode == Transport::Udp) + sc->grantUDPAssociate("", 0); + else + sc->grantConnect(); + return; + } + qDebug("Reject incoming socks5 connection with key %s (%s)", qPrintable(key), + connection->client ? "already has connection" : "key mismatch"); + sc->requestDeny(); + sc->deleteLater(); + }); + QObject::connect( + s5bserv.data(), &S5BServer::incomingUdp, q, + [this, c](bool isInit, const QHostAddress &addr, int sourcePort, const QString &key, + const QByteArray &data) { + if (mode != Transport::Mode::Udp || !connection->client) { + return false; + } + + if (isInit) { + // TODO probably we could create a Connection here and put all the params inside + if (udpInitialized) + return false; // only init once + + // lock on to this sender + udpAddress = addr; + udpPort = quint16(sourcePort); + udpInitialized = true; + + // reply that initialization was successful + q->_pad->session()->manager()->client()->s5bManager()->jtPush()->sendUDPSuccess( + q->_pad->session()->peer(), key); // TODO fix ->->-> + return true; + } + + // not initialized yet? something went wrong + if (!udpInitialized) + return false; + + // must come from same source as when initialized + if (addr != udpAddress || sourcePort != udpPort) + return false; + + connection->enqueueIncomingUDP(data); // man_udpReady + return true; + }); + localCandidates.emplace(c.cid(), c); + qDebug("new local candidate: %s", qPrintable(c.toString())); + pendingActions |= NewCandidate; + hasNewCandidates = true; + } + } + if (hasNewCandidates) { + emit q->updated(); + } + } + + void handleConnected(Candidate &connCand) + { + connection->setSocksClient(connCand.takeSocksClient(), mode); + probingTimer.stop(); + negotiationFinishTimer.stop(); + proxyDiscoveryInProgress = false; + for (auto &[_, rc] : remoteCandidates) { + if (rc != connCand && rc.state() == Candidate::Probing) { + rc.deleteSocksClient(); + } + } + QTimer::singleShot(0, q, [this]() { + for (const auto &[cid, candidate] : localCandidates) { + if (candidate.state() == Candidate::Discarded) + localCandidatesTrack.append(cid); + } + localCandidates.clear(); + remoteCandidates.clear(); + q->setState(State::Active); + }); + } + + void handleNegotiationTimeout() + { + // probingTimer.stop(); + proxyDiscoveryInProgress = false; + for (auto &[_, rc] : remoteCandidates) { + if (rc.state() <= Candidate::Probing) { + rc.setState(Candidate::Discarded); + } + } + for (auto &[_, rc] : localCandidates) { + if (rc.state() <= Candidate::Probing) { + rc.setState(Candidate::Discarded); + } + } + checkAndFinishNegotiation(); + } + + bool handleIncomingCandidate(const QDomElement &transportEl) + { + QString candidateTag(QStringLiteral("candidate")); + bool handled = false; + bool reallyAdded = false; + for (QDomElement ce = transportEl.firstChildElement(candidateTag); !ce.isNull(); + ce = ce.nextSiblingElement(candidateTag)) { + Candidate c(q, ce); + if (!c) { + throw Stanza::Error(Stanza::Error::Cancel, Stanza::Error::BadRequest); + } + if (!p2pAllowed && c.type() != Candidate::Proxy) { + qDebug("new remote candidate discarded with forbidden p2p: %s", qPrintable(c)); + } else { + qDebug("new remote candidate: %s", qPrintable(c.toString())); + remoteCandidates.emplace(c.cid(), c); // TODO check for collisions! + reallyAdded = true; + } + handled = true; + } + if (reallyAdded) { + pendingActions &= ~CandidateError; + localReportedCandidateError = false; + QTimer::singleShot(0, q, [this]() { tryConnectToRemoteCandidate(); }); + } + return handled; + } + + bool handleIncomingCandidateUsed(const QDomElement &transportEl) + { + QDomElement el = transportEl.firstChildElement(QStringLiteral("candidate-used")); + if (!el.isNull()) { + auto cid = QStringLiteral("cid"); + auto it = localCandidates.find(el.attribute(cid)); + if (it == localCandidates.end()) { + if (localCandidatesTrack.contains(cid)) + return true; // likely discarded as not needed anymore + throw Stanza::Error(Stanza::Error::Cancel, Stanza::Error::ItemNotFound, + QString("failed to find incoming candidate-used candidate %1").arg(cid)); + } + auto &cUsed = it->second; + if (cUsed.state() == Candidate::Pending) { + if (cUsed.type() != Candidate::Proxy && !cUsed.isConnected()) { + throw Stanza::Error( + Stanza::Error::Cancel, Stanza::Error::NotAcceptable, + QString("incoming candidate-used refers a candidate w/o active socks connection: %1") + .arg(QString(cUsed))); + } + cUsed.setState(Candidate::Accepted); + localUsedCandidate = cUsed; + updateMinimalPriorityOnConnected(); + QTimer::singleShot(0, q, [this]() { checkAndFinishNegotiation(); }); + } else { + // we already rejected the candidate and either remote side already knows about it or will soon + // it's possible for example if we were able to connect to higher priority candidate, so + // we have o pretend like remote couldn't select anything better but finished already, in other + // words like if it sent candidate-error. + localUsedCandidate = Candidate(); + remoteReportedCandidateError = true; + } + return true; + } return false; } - d->remoteCandidates.insert(c.cid(), c); // TODO check for collisions! - candidatesAdded = true; - } - if (candidatesAdded) { - d->localReportedCandidateError = false; - QTimer::singleShot(0, this, [this](){ - d->tryConnectToRemoteCandidate(); - }); - return true; - } - QDomElement el = transportEl.firstChildElement(QStringLiteral("candidate-used")); - if (!el.isNull()) { - auto cUsed = d->localCandidates.value(el.attribute(QStringLiteral("cid"))); - if (!cUsed) { + bool handleIncomingCandidateError(const QDomElement &transportEl) + { + auto el = transportEl.firstChildElement(QStringLiteral("candidate-error")); + if (!el.isNull()) { + remoteReportedCandidateError = true; + for (auto &[_, c] : localCandidates) { + if (c.state() == Candidate::Pending) { + c.setState(Candidate::Discarded); + } + } + qDebug("recv candidate-error: all local pending candidates were discarded"); + QTimer::singleShot(0, q, [this]() { checkAndFinishNegotiation(); }); + return true; + } return false; } - cUsed.setState(Candidate::Accepted); - d->localUsedCandidate = cUsed; - cUsed.setState(Candidate::Accepted); - QTimer::singleShot(0, this, [this](){ d->updateSelfState(); }); - } - el = transportEl.firstChildElement(QStringLiteral("candidate-error")); - if (!el.isNull()) { - d->remoteReportedCandidateError = true; - QTimer::singleShot(0, this, [this](){ d->updateSelfState(); }); - } + bool handleIncomingActivated(const QDomElement &transportEl) + { + auto el = transportEl.firstChildElement(QStringLiteral("activated")); + if (!el.isNull()) { + QString cid = el.attribute(QStringLiteral("cid")); + if (cid.isEmpty()) { + throw Stanza::Error(Stanza::Error::Cancel, Stanza::Error::ItemNotFound, + "failed to find incoming activated candidate"); + } + auto c = remoteUsedCandidate; + if (!(c.cid() == cid && c.type() == Candidate::Proxy && c.state() == Candidate::Accepted)) { + qDebug("Received on a candidate in an inappropriate state. Ignored."); + return true; + } + c.setState(Candidate::Active); + handleConnected(c); + return true; + } + return false; + } - el = transportEl.firstChildElement(QStringLiteral("activated")); - if (!el.isNull()) { - auto c = d->localCandidates.value(el.attribute(QStringLiteral("cid"))); - if (!c) { + bool handleIncomingProxyError(const QDomElement &transportEl) + { + auto el = transportEl.firstChildElement(QStringLiteral("proxy-error")); + if (!el.isNull()) { + auto it = localCandidates.find(el.attribute(QStringLiteral("cid"))); + if (it == localCandidates.end()) { + throw Stanza::Error(Stanza::Error::Cancel, Stanza::Error::ItemNotFound, + "failed to find incoming proxy-error candidate"); + } + auto &c = it->second; + if (c != localUsedCandidate || c.state() != Candidate::Accepted) { + qDebug("Received on a candidate in an inappropriate state. Ignored."); + return true; + } + + // if we got proxy-error then the transport has to be considered failed according to spec + // so never send proxy-error while we have unaknowledged local non-proxy candidates, + // but we have to follow the standard. + + // Discard everything + for (auto &[_, c] : localCandidates) { + c.setState(Candidate::Discarded); + } + for (auto &[_, c] : remoteCandidates) { + c.setState(Candidate::Discarded); + } + proxyDiscoveryInProgress = false; + delete disco; + + QTimer::singleShot(0, q, [this]() { + q->onFinish(Reason::Condition::ConnectivityError, QLatin1String("got proxy error from the peer")); + }); + return true; + } return false; } - c.setState(Candidate::Active); - QTimer::singleShot(0, this, [this](){ d->updateSelfState(); }); + }; + + Transport::Transport(const TransportManagerPad::Ptr &pad, Origin creator) : + XMPP::Jingle::Transport(pad, creator), d(new Private) + { + d->q = this; + d->probingTimer.setSingleShot(true); + d->negotiationFinishTimer.setSingleShot(true); + d->negotiationFinishTimer.setInterval(5000); // TODO select the value smart way + connect(&d->probingTimer, &QTimer::timeout, this, [this]() { d->tryConnectToRemoteCandidate(); }); + connect(&d->negotiationFinishTimer, &QTimer::timeout, this, [this]() { d->handleNegotiationTimeout(); }); + connect(_pad->manager(), &TransportManager::abortAllRequested, this, [this]() { + d->aborted = true; + onFinish(Reason::Condition::Cancel); + }); } - el = transportEl.firstChildElement(QStringLiteral("proxy-error")); - if (!el.isNull()) { - auto c = d->localCandidates.value(el.attribute(QStringLiteral("cid"))); - if (!c) { - return false; + Transport::~Transport() + { + if (d) { + // TODO unregister sid too + static_cast(_pad.staticCast()->manager())->removeKeyMapping(d->directAddr); + for (auto &[_, c] : d->remoteCandidates) { + c.deleteSocksClient(); + } + for (auto &[_, c] : d->remoteCandidates) { + auto srv = c.server(); + if (srv) { + srv.staticCast()->unregisterKey(d->directAddr); + } + } } - c.setState(Candidate::Discarded); - QTimer::singleShot(0, this, [this](){ d->updateSelfState(); }); } - return true; -} + void Transport::prepare() + { + qDebug("Prepare local offer"); + setState(State::ApprovedToSend); + auto m = static_cast(_pad.staticCast()->manager()); + if (_creator == _pad->session()->role()) { // I'm creator + d->sid = _pad.staticCast()->generateSid(); + } + _pad.staticCast()->registerSid(d->sid); + d->directAddr = makeKey(d->sid, _pad.staticCast()->session()->initiator(), + _pad.staticCast()->session()->responder()); + m->addKeyMapping(d->directAddr, this); -Action Transport::outgoingUpdateType() const -{ - if (isValid() && d->application) { - // if we are preparing local offer and have at least one candidate, we have to sent it. - // otherwise it's not first update to remote from this transport, so we have to send just signalling candidates - if ((d->application->state() == State::PrepareLocalOffer && d->localCandidates.size()) || - (d->application->state() > State::PrepareLocalOffer && d->application->state() < State::Finished && - !d->signalingCandidates.isEmpty())) - { - return Action::TransportInfo; + auto scope = _pad.staticCast()->discoScope(); + d->disco = scope->disco(); // FIXME store and handle signal. delete when not needed + + connect(d->disco, &TcpPortDiscoverer::portAvailable, this, [this]() { d->onLocalServerDiscovered(); }); + d->setLocalProbingMinimalPreference(0); // allow all on start + + d->discoS5BProxy(); + + if (isRemote() && !notifyIncomingConnection(d->connection)) { + // our the only connection wasn't accepted + onFinish(Reason::IncompatibleParameters, + QLatin1String("Application didn't accept the only incoming connection")); + return; } + emit updated(); } - return Action::NoAction; // TODO -} - -OutgoingUpdate Transport::takeOutgoingUpdate() -{ - OutgoingUpdate upd; - State appState; - if (!isValid() || !d->application || (appState = d->application->state()) == State::Finished) { - return upd; + + // we got content acceptance from any side and now can connect + void Transport::start() + { + qDebug("Starting connecting"); + setState(State::Connecting); + d->tryConnectToRemoteCandidate(); + // if there is no higher priority candidates than ours but they are already connected then + d->checkAndFinishNegotiation(); } - auto sessRole = d->pad->session()->role(); - auto doc = d->pad->session()->manager()->client()->doc(); + bool Transport::update(const QDomElement &transportEl) + { + // we can just on type of elements in transport-info + // so return as soon as any type handled. Though it leaves a room for remote to send invalid transport-info + auto bs = transportEl.attribute(QString::fromLatin1("block-size")); + if (!bs.isEmpty()) { + size_t bsn = bs.toULongLong(); + if (bsn && bsn <= d->blockSize) { + d->blockSize = bsn; + } + } + bool isNewIncoming = _state == State::Created && isRemote(); + if (isNewIncoming && d->sid.isEmpty()) { + d->sid = transportEl.attribute(QStringLiteral("sid")); + } + auto dstaddr = transportEl.attribute(QStringLiteral("dstaddr")); + if (!dstaddr.isEmpty()) { + d->dstaddr = dstaddr; + } + + try { + if (d->handleIncomingCandidate(transportEl) || d->handleIncomingCandidateUsed(transportEl) + || d->handleIncomingCandidateError(transportEl) || d->handleIncomingActivated(transportEl) + || d->handleIncomingProxyError(transportEl)) { + if (isNewIncoming) { + d->connection->setRemote(true); + setState(State::Pending); + } + if (_state == State::Pending && _creator == _pad->session()->role()) { + // initial acceptance by remote of the local transport + setState(State::Accepted); + } + return true; + } + } catch (XMPP::Stanza::Error &e) { + qWarning("Transport updated failed: %s", qPrintable(e.toString())); + _lastError = e; + return false; + } + + // Seems like we got an empty transport. It's still valid though. + QTimer::singleShot(0, this, [this]() { d->checkAndFinishNegotiation(); }); + + return true; + } + + bool Transport::hasUpdates() const { return isValid() && d->pendingActions; } + + OutgoingTransportInfoUpdate Transport::takeOutgoingUpdate(bool ensureTransportElement = false) + { + qDebug("taking outgoing update"); + OutgoingTransportInfoUpdate upd; + if (!isValid()) { + return upd; + } + + // check where we make initial offer + bool noPending = (d->localCandidates.empty() && !d->proxyDiscoveryInProgress + && !(d->disco && d->disco->inProgressPortTypes())); + bool initial = _state == State::ApprovedToSend && !d->offerSent + && ((!d->pendingActions && noPending) || d->pendingActions & Private::NewCandidate); + + auto makeUpdate = [this, initial](QDomElement tel, bool expectedSuccess = false, + std::function cb = std::function()) { + d->waitingAck = true; + if (initial) { + _state = State::Unacked; + } + return OutgoingTransportInfoUpdate { + tel, + [this, cb, expectedSuccess, trptr = QPointer(d->q), initial](Task *task) { + if (!trptr) + return; + d->waitingAck = false; + if (expectedSuccess && !task->success()) { + d->localCandidates.clear(); + d->remoteCandidates.clear(); + onFinish(Reason::Condition::FailedTransport, QLatin1String("iq error")); + } else if (cb) { + if (initial) { + _state = isLocal() ? State::Pending : State::Accepted; + } + cb(task); + } + } + }; + }; + + auto doc = _pad.staticCast()->session()->manager()->client()->doc(); - if (appState == State::PrepareLocalOffer && !d->localCandidates.isEmpty()) { QDomElement tel = doc->createElementNS(NS, "transport"); tel.setAttribute(QStringLiteral("sid"), d->sid); - if (d->mode != Tcp) { - tel.setAttribute(QStringLiteral("mode"), "udp"); - } - bool useProxy = false; - QList candidatesToSend; - for (auto &c: d->localCandidates) { - if (c.type() == Candidate::Proxy) { - useProxy = true; + + if (initial) { + if (_creator == _pad->session()->role() && d->mode != Tcp) { + tel.setAttribute(QStringLiteral("mode"), "udp"); } - if (!c.host().isEmpty()) { + tel.setAttribute(QString::fromLatin1("block-size"), qulonglong(d->blockSize)); + d->offerSent = true; + } + + if (d->pendingActions & Private::NewCandidate) { + d->pendingActions &= ~Private::NewCandidate; + bool useProxy = false; + QList candidatesToSend; + for (auto &[_, c] : d->localCandidates) { + if (c.state() != Candidate::New) { + continue; + } + if (c.type() == Candidate::Proxy) { + useProxy = true; + } + qDebug("sending local candidate: cid=%s", qPrintable(c.cid())); tel.appendChild(c.toXml(doc)); + candidatesToSend.append(c); + c.setState(Candidate::Unacked); + } + if (useProxy) { + QString dstaddr = makeKey(d->sid, _pad.staticCast()->session()->manager()->client()->jid(), + _pad.staticCast()->session()->peer()); + tel.setAttribute(QStringLiteral("dstaddr"), dstaddr); } - candidatesToSend.append(c); - d->signalingCandidates.remove(QPair{c.cid(),sessRole}); - c.setState(Candidate::Unacked); + if (!candidatesToSend.isEmpty()) { + upd = makeUpdate(tel, false, [this, candidatesToSend, initial](Task *jt) mutable { + if (jt->success()) { + for (auto &c : candidatesToSend) { + if (c.state() == Candidate::Unacked) { + c.setState(Candidate::Pending); + qDebug("ack: remote side accepted local candidate: cid=%s", qPrintable(c.cid())); + } + } + } else { + for (auto &c : candidatesToSend) { + if (c.state() == Candidate::Unacked) { + c.setState(Candidate::Discarded); + qDebug("ack: remote side discarded local candidate: cid=%s", qPrintable(c.cid())); + } + } + d->updateMinimalPriorityOnConnected(); + } + d->checkAndFinishNegotiation(); + }); + } else { + qWarning("Got NewCandidate pending action but no candidate to send"); + } + } else if (d->pendingActions & Private::CandidateUsed) { + d->pendingActions &= ~Private::CandidateUsed; + // we should have the only remote candidate in Pending state. + // all other has to be discarded by priority check + for (auto &it : d->remoteCandidates) { + auto &c = it.second; + if (c.state() != Candidate::Pending) { + continue; + } + qDebug("sending candidate-used: cid=%s", qPrintable(c.cid())); + auto el = tel.appendChild(doc->createElement(QStringLiteral("candidate-used"))).toElement(); + el.setAttribute(QStringLiteral("cid"), c.cid()); + c.setState(Candidate::Unacked); + + upd = makeUpdate(tel, true, [this, c](Task *) mutable { + if (c.state() == Candidate::Unacked) { + c.setState(Candidate::Accepted); + qDebug("ack: sending candidate-used: cid=%s", qPrintable(c.cid())); + d->remoteUsedCandidate = c; + } + d->checkAndFinishNegotiation(); + }); + break; + } + if (std::get<0>(upd).isNull()) { + qWarning("Got CandidateUsed pending action but no pending candidates"); + } + } else if (d->pendingActions & Private::CandidateError) { + d->pendingActions &= ~Private::CandidateError; + qDebug("sending candidate-error"); + // we are here because all remote are already in Discardd state + tel.appendChild(doc->createElement(QStringLiteral("candidate-error"))); + upd = makeUpdate(tel, true, [this](Task *) mutable { + d->localReportedCandidateError = true; + d->checkAndFinishNegotiation(); + }); + } else if (d->pendingActions & Private::Activated) { + d->pendingActions &= ~Private::Activated; + if (d->localUsedCandidate) { + auto cand = d->localUsedCandidate; + qDebug("sending activated: cid=%s", qPrintable(cand.cid())); + auto el = tel.appendChild(doc->createElement(QStringLiteral("activated"))).toElement(); + el.setAttribute(QStringLiteral("cid"), cand.cid()); + upd = makeUpdate(tel, true); + } + } else if (d->pendingActions & Private::ProxyError) { + // we send proxy error only for local proxy + d->pendingActions &= ~Private::ProxyError; + if (d->localUsedCandidate) { + auto cand = d->localUsedCandidate; + tel.appendChild(doc->createElement(QStringLiteral("proxy-error"))); + qDebug("sending proxy error: cid=%s", qPrintable(cand.cid())); + upd = makeUpdate(tel, true, [this, cand](Task *task) mutable { + qDebug("ack: sending proxy error: cid=%s", qPrintable(cand.cid())); + if ((cand.state() != Candidate::Accepted || d->localUsedCandidate != cand) && task->success()) { + return; // seems like state was changed while we were waiting for an ack + } + cand.setState(Candidate::Discarded); + d->localUsedCandidate = Candidate(); + _state = State::Finished; + emit failed(); + }); + } else { + qWarning("Got ProxyError pending action but no local used candidate is not set"); + } + } else { + qDebug("sending empty transport-info"); + upd = makeUpdate(tel, false, [this, initial](Task *jt) mutable { + if (!jt->success()) { + if (initial) + onFinish(Reason::FailedTransport, QLatin1String("got it error for initial")); + else + qWarning("Ignored failed IQ response"); + } + }); } - if (useProxy) { - QString dstaddr = QCryptographicHash::hash((d->sid + - d->pad->session()->me().full() + - d->pad->session()->peer().full()).toUtf8(), - QCryptographicHash::Sha1); - tel.setAttribute(QStringLiteral("dstaddr"), dstaddr); + + auto &atel = std::get<0>(upd); + if (atel.isNull() && ensureTransportElement) { + atel = tel; } - OutgoingUpdate{tel, [this, candidatesToSend]() mutable { - for (auto &c: candidatesToSend) { - c.setState(Candidate::Pending); - } - }}; // FIXME we should update candidates status here + + return upd; // TODO } - return upd; // TODO -} - -bool Transport::isValid() const -{ - return d != nullptr; -} - -Transport::Features Transport::features() const -{ - return Features(HardToConnect | Reliable | Fast); -} - -QString Transport::sid() const -{ - return d->sid; -} - -bool Transport::incomingConnection(SocksClient *sc, const QString &key) -{ - // incoming direct connection -#if 0 - if(!d->allowIncoming) { - sc->requestDeny(); - sc->deleteLater(); - return; + + bool Transport::isValid() const { return d != nullptr; } + + TransportFeatures Transport::features() const { return _pad->manager()->features(); } + + QString Transport::sid() const { return d->sid; } + + QString Transport::directAddr() const { return d->directAddr; } + + int Transport::maxSupportedChannelsPerComponent(TransportFeatures) const { return 1; } + + XMPP::Jingle::Connection::Ptr Transport::addChannel(TransportFeatures features, const QString &id, int) + { + // TODO consider features and create connections here. + Q_UNUSED(features); // no way create something depending on features + d->connection->setId(id); + return d->connection; } - if(d->mode == Transport::Udp) - sc->grantUDPAssociate("", 0); - else - sc->grantConnect(); - e->relatedServer = static_cast(sender()); - e->i->setIncomingClient(sc); -#endif - return false; -} - -//---------------------------------------------------------------- -// Manager -//---------------------------------------------------------------- - -class Manager::Private -{ -public: - XMPP::Jingle::Manager *jingleManager = nullptr; - S5BServer *serv = nullptr; - - // FIMME it's reuiqred to split transports by direction otherwise we gonna hit conflicts. - // jid,transport-sid -> transport mapping - QSet> sids; - QHash key2transport; - Jid proxy; -}; - -Manager::Manager(QObject *parent) : - TransportManager(parent), - d(new Private) -{ -} - -Manager::~Manager() -{ - d->jingleManager->unregisterTransport(NS); -} - -Transport::Features Manager::features() const -{ - return Transport::Reliable | Transport::Fast; -} - -void Manager::setJingleManager(XMPP::Jingle::Manager *jm) -{ - d->jingleManager = jm; -} - -QSharedPointer Manager::newTransport(const TransportManagerPad::Ptr &pad) -{ - return QSharedPointer(new Transport(pad)); -} - -QSharedPointer Manager::newTransport(const TransportManagerPad::Ptr &pad, const QDomElement &transportEl) -{ - auto t = new Transport(pad, transportEl); - QSharedPointer ret(t); - if (t->isValid()) { - return ret; + + QList Transport::channels() const + { + // return {}; // good to test transport failure + return { { d->connection } }; + } + + //---------------------------------------------------------------- + // Manager + //---------------------------------------------------------------- + + class Manager::Private { + public: + XMPP::Jingle::Manager *jingleManager = nullptr; + + // FIMME it's reuiqred to split transports by direction otherwise we gonna hit conflicts. + // jid,transport-sid -> transport mapping + QSet> sids; + QHash key2transport; + Jid proxy; + }; + + Manager::Manager(QObject *parent) : TransportManager(parent), d(new Private) { } + + Manager::~Manager() + { + if (d->jingleManager) + d->jingleManager->unregisterTransport(NS); + } + + TransportFeatures Manager::features() const + { + return TransportFeature::Reliable | TransportFeature::Fast | TransportFeature::DataOriented + | TransportFeature::Ordered; } - return QSharedPointer(); -} - -TransportManagerPad* Manager::pad(Session *session) -{ - return new Pad(this, session); -} - -void Manager::closeAll() -{ - emit abortAllRequested(); -} - -void Manager::setServer(S5BServer *serv) -{ - if(d->serv) { - d->serv->unlink(this); - d->serv = nullptr; + + void Manager::setJingleManager(XMPP::Jingle::Manager *jm) + { + d->jingleManager = jm; + if (!jm) + return; + // ensure S5BManager is initialized + QTimer::singleShot(0, this, [this]() { + if (!d->jingleManager) // unregistered that early? + return; + auto jt = d->jingleManager->client()->s5bManager()->jtPush(); + connect(jt, &JT_PushS5B::incomingUDPSuccess, this, [this](const Jid &from, const QString &dstaddr) { + Q_UNUSED(from) + auto t = d->key2transport.value(dstaddr); + if (t) { + // TODO return t->incomingUDPSuccess(from); + } + }); + }); } - if(serv) { - d->serv = serv; - d->serv->link(this); + QSharedPointer Manager::newTransport(const TransportManagerPad::Ptr &pad, Origin creator) + { + return QSharedPointer::create(pad, creator).staticCast(); } -} -bool Manager::incomingConnection(SocksClient *client, const QString &key) -{ - auto t = d->key2transport.value(key); - if (t) { - return t->incomingConnection(client, key); + TransportManagerPad *Manager::pad(Session *session) { return new Pad(this, session); } + + QStringList Manager::discoFeatures() const { return { NS }; } + + void Manager::addKeyMapping(const QString &key, Transport *transport) { d->key2transport.insert(key, transport); } + + void Manager::removeKeyMapping(const QString &key) { d->key2transport.remove(key); } + + QString Manager::generateSid(const Jid &remote) + { + auto servers = d->jingleManager->client()->tcpPortReserver()->scope(QString::fromLatin1("s5b"))->allServers(); + QString sid; + QPair key; + QString key1; + QString key2; + auto servChecker = [&](const TcpPortServer::Ptr &s) { + return s.staticCast()->hasKey(key1) || s.staticCast()->hasKey(key2); + }; + + do { +#if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0) + sid = QString("s5b_%1").arg(QRandomGenerator::global()->bounded(0x10000), 4, 16, QChar('0')); +#else + sid = QString("s5b_%1").arg(qrand() & 0xffff, 4, 16, QChar('0')); +#endif + key = qMakePair(remote, sid); + key1 = makeKey(sid, remote, d->jingleManager->client()->jid()); + key2 = makeKey(sid, d->jingleManager->client()->jid(), remote); + } while (d->sids.contains(key) || std::any_of(servers.begin(), servers.end(), servChecker)); + return sid; } - return false; -} - -QString Manager::generateSid(const Jid &remote) -{ - QString sid; - QPair key; - do { - sid = QString("s5b_%1").arg(qrand() & 0xffff, 4, 16, QChar('0')); - key = qMakePair(remote, sid); - } while (d->sids.contains(key)); - return sid; -} - -void Manager::registerSid(const Jid &remote, const QString &sid) -{ - d->sids.insert(qMakePair(remote, sid)); -} - -S5BServer *Manager::socksServ() const -{ - return d->serv; -} - -Jid Manager::userProxy() const -{ - return d->proxy; -} - -//---------------------------------------------------------------- -// Pad -//---------------------------------------------------------------- -Pad::Pad(Manager *manager, Session *session) : - _manager(manager), - _session(session) -{ - -} - -QString Pad::ns() const -{ - return NS; -} - -Session *Pad::session() const -{ - return _session; -} - -TransportManager *Pad::manager() const -{ - return _manager; -} - -QString Pad::generateSid() const -{ - return _manager->generateSid(_session->peer()); -} - -void Pad::registerSid(const QString &sid) -{ - return _manager->registerSid(_session->peer(), sid); -} + void Manager::registerSid(const Jid &remote, const QString &sid) { d->sids.insert(qMakePair(remote, sid)); } + + Jid Manager::userProxy() const { return d->proxy; } + + void Manager::setUserProxy(const Jid &jid) { d->proxy = jid; } + + //---------------------------------------------------------------- + // Pad + //---------------------------------------------------------------- + Pad::Pad(Manager *manager, Session *session) : _manager(manager), _session(session) + { + auto reserver = _session->manager()->client()->tcpPortReserver(); + _discoScope = reserver->scope(QString::fromLatin1("s5b")); + } + + QString Pad::ns() const { return NS; } + + Session *Pad::session() const { return _session; } + + TransportManager *Pad::manager() const { return _manager; } + + QString Pad::generateSid() const { return _manager->generateSid(_session->peer()); } + + void Pad::registerSid(const QString &sid) { return _manager->registerSid(_session->peer(), sid); } } // namespace S5B } // namespace Jingle } // namespace XMPP + +#include "jingle-s5b.moc" diff --git a/src/xmpp/xmpp-im/jingle-s5b.h b/src/xmpp/xmpp-im/jingle-s5b.h index 6275a892..00dddaa3 100644 --- a/src/xmpp/xmpp-im/jingle-s5b.h +++ b/src/xmpp/xmpp-im/jingle-s5b.h @@ -17,188 +17,203 @@ * */ -/* - * In s5b.cpp we have - * S5BManager -> Jingle::S5B::Manager - * S5BManager::Item -> Jingle::S5B::Transport - * S5BManager::Entry -> ??? - * - */ - - #ifndef JINGLE_S5B_H #define JINGLE_S5B_H -#include "jingle.h" +#include "iris/tcpportreserver.h" +#include "jingle-transport.h" +class QHostAddress; class SocksClient; namespace XMPP { - class Client; -class S5BServer; -namespace Jingle { -namespace S5B { +namespace Jingle { namespace S5B { + extern const QString NS; + + class Transport; + + class Candidate { + public: + enum Type { + None, // non standard, just a default + Proxy, + Tunnel, + Assisted, + Direct + }; + + enum { ProxyPreference = 10, TunnelPreference = 110, AssistedPreference = 120, DirectPreference = 126 }; + + /** + * Local candidates states: + * Probing - potential candidate but no ip:port yet. upnp for example + * New - candidate is ready to be sent to remote + * Unacked - candidate is sent to remote but no iq ack yet + * Pending - canidate sent to remote. we have iq ack but no "used" or "error" + * Accepted - we got "candidate-used" for this candidate + * Activating - only for proxy: we activate the proxy + * Active - use this candidate for actual data transfer + * Discarded - we got "candidate-error" so all pending were marked Discarded + * + * Remote candidates states: + * New - the candidate waits its turn to start connection probing + * Probing - connection probing + * Pending - connection was successful, but we didn't send candidate-used to remote + * Unacked - connection was successful and we sent candidate-used to remote but no iq ack yet + * Accepted - we sent candidate-used and got iq ack + * Activating - [not used] + * Active - use this candidate for actual data transfer + * Discarded - failed to connect to all remote candidates + */ + enum State { + New, + Probing, + Pending, + Unacked, + Accepted, + Activating, + Active, + Discarded, + }; + + Candidate(); + Candidate(Transport *transport, const QDomElement &el); + Candidate(const Candidate &other); + Candidate(Transport *transport, const Jid &proxy, const QString &cid, quint16 localPreference = 0); + Candidate(Transport *transport, const TcpPortServer::Ptr &server, const QString &cid, + quint16 localPreference = 0); + ~Candidate(); + Candidate &operator=(const Candidate &other) = default; + inline bool isValid() const { return d != nullptr; } + inline operator bool() const { return isValid(); } + Type type() const; + static const char *typeText(Type t); + QString cid() const; + Jid jid() const; + QString host() const; + void setHost(const QString &host); + quint16 port() const; + void setPort(quint16 port); + quint16 localPort() const; + QHostAddress localAddress() const; + State state() const; + void setState(State s); + static const char *stateText(State s); + quint32 priority() const; + + QDomElement toXml(QDomDocument *doc) const; + QString toString() const; + inline operator QString() const { return toString(); } + + void connectToHost(const QString &key, State successState, QObject *callbackContext, + std::function callback, bool isUdp = false); + bool incomingConnection(SocksClient *sc); + SocksClient *takeSocksClient(); + void deleteSocksClient(); + TcpPortServer::Ptr server() const; + bool isConnected() const; + + bool operator==(const Candidate &other) const; + inline bool operator!=(const Candidate &other) const { return !(*this == other); } + + private: + class Private; + friend class Transport; + QExplicitlySharedDataPointer d; + }; -extern const QString NS; + class Manager; + class Transport : public XMPP::Jingle::Transport { + Q_OBJECT + public: + enum Mode { Tcp, Udp }; -class Candidate { -public: - enum Type { - None, // non standard, just a default - Proxy, - Tunnel, - Assisted, - Direct - }; + Transport(const TransportManagerPad::Ptr &pad, Origin creator); + ~Transport() override; - enum { - ProxyPreference = 10, - TunnelPreference = 110, - AssistedPreference = 120, - DirectPreference = 126 - }; + void prepare() override; + void start() override; + bool update(const QDomElement &transportEl) override; + bool hasUpdates() const override; + OutgoingTransportInfoUpdate takeOutgoingUpdate(bool ensureTransportElement) override; + bool isValid() const override; + TransportFeatures features() const override; - /** - * Local candidates states: - * Probing - potential candidate but no ip:port yet. upnp for example - * New - candidate is ready to be sent to remote - * Unacked - candidate is sent to remote but no iq ack yet - * Pending - canidate sent to remote. we have iq ack but no "used" or "error" - * Accepted - we got "candidate-used" for this candidate - * Active - use this candidate for actual data transfer - * Discarded - we got "candidate-error" so all pending were marked Discarded - * - * Remote candidates states: - * New - the candidate waits its turn to start connection probing - * Probing - connection probing - * Pending - connection was successful, but we didn't send candidate-used to remote - * Unacked - connection was successful and we sent candidate-used to remote but no iq ack yet - * Accepted - we sent candidate-used and got iq ack - * Active - use this candidate for actual data transfer - * Discarded - failed to connect to all remote candidates - */ - enum State { - New, - Probing, - Pending, - Unacked, - Accepted, - Active, - Discarded, - }; + QString sid() const; + QString directAddr() const; + + int maxSupportedChannelsPerComponent(TransportFeatures features) const override; + Connection::Ptr addChannel(TransportFeatures features, const QString &id, int component = 0) override; + QList channels() const override; + + private: + friend class Manager; - Candidate(); - Candidate(const QDomElement &el); - Candidate(const Candidate &other); - Candidate(const Jid &proxy, const QString &cid, quint16 localPreference = 0); - Candidate(const QString &host, quint16 port, const QString &cid, Type type, quint16 localPreference = 0); - ~Candidate(); - inline bool isValid() const { return d != nullptr; } - inline operator bool() const { return isValid(); } - Type type() const; - QString cid() const; - Jid jid() const; - QString host() const; - void setHost(const QString &host); - quint16 port() const; - void setPort(quint16 port); - State state() const; - void setState(State s); - quint32 priority() const; - - QDomElement toXml(QDomDocument *doc) const; - -private: - class Private; - QExplicitlySharedDataPointer d; -}; - -class Manager; -class Transport : public XMPP::Jingle::Transport -{ - Q_OBJECT -public: - enum Mode { - Tcp, - Udp + class Private; + std::unique_ptr d; }; - Transport(const TransportManagerPad::Ptr &pad); - Transport(const TransportManagerPad::Ptr &pad, const QDomElement &transportEl); - ~Transport(); - - TransportManagerPad::Ptr pad() const override; - void setApplication(Application *app) override; - - void prepare() override; - void start() override; - bool update(const QDomElement &transportEl) override; - Action outgoingUpdateType() const override; - OutgoingUpdate takeOutgoingUpdate() override; - bool isValid() const override; - Features features() const override; - - QString sid() const; - - bool incomingConnection(SocksClient *sc, const QString &key); - -private: - friend class Manager; - static QSharedPointer createOutgoing(const TransportManagerPad::Ptr &pad); - static QSharedPointer createIncoming(const TransportManagerPad::Ptr &pad, const QDomElement &transportEl); - - class Private; - QScopedPointer d; -}; - -class Pad : public TransportManagerPad -{ - Q_OBJECT - // TODO -public: - typedef QSharedPointer Ptr; - - Pad(Manager *manager, Session *session); - QString ns() const override; - Session *session() const override; - TransportManager *manager() const override; - - QString generateSid() const; - void registerSid(const QString &sid); -private: - Manager *_manager; - Session *_session; -}; - -class Manager : public TransportManager { - Q_OBJECT -public: - Manager(QObject *parent = nullptr); - ~Manager(); - - XMPP::Jingle::Transport::Features features() const override; - void setJingleManager(XMPP::Jingle::Manager *jm) override; - QSharedPointer newTransport(const TransportManagerPad::Ptr &pad) override; // outgoing. one have to call Transport::start to collect candidates - QSharedPointer newTransport(const TransportManagerPad::Ptr &pad, const QDomElement &transportEl) override; // incoming - TransportManagerPad* pad(Session *session) override; - - void closeAll() override; - - void setServer(S5BServer *serv); - bool incomingConnection(SocksClient *client, const QString &key); // returns false if key is unknown - - QString generateSid(const Jid &remote); - void registerSid(const Jid &remote, const QString &sid); - - S5BServer* socksServ() const; - Jid userProxy() const; -private: - class Private; - QScopedPointer d; -}; + class Pad : public TransportManagerPad { + Q_OBJECT + // TODO + public: + typedef QSharedPointer Ptr; + + Pad(Manager *manager, Session *session); + QString ns() const override; + Session *session() const override; + TransportManager *manager() const override; + + QString generateSid() const; + void registerSid(const QString &sid); + inline TcpPortScope *discoScope() const { return _discoScope; } + + private: + Manager *_manager; + Session *_session; + TcpPortScope *_discoScope; + }; + + class Manager : public TransportManager { + Q_OBJECT + public: + Manager(QObject *parent = nullptr); + ~Manager() override; + + XMPP::Jingle::TransportFeatures features() const override; + void setJingleManager(XMPP::Jingle::Manager *jm) override; + QSharedPointer newTransport(const TransportManagerPad::Ptr &pad, + Origin creator) override; + TransportManagerPad *pad(Session *session) override; + + QStringList discoFeatures() const override; + + QString generateSid(const Jid &remote); + void registerSid(const Jid &remote, const QString &sid); + + /** + * @brief userProxy returns custom (set by user) SOCKS proxy JID + * @return + */ + Jid userProxy() const; + void setUserProxy(const Jid &jid); + + /** + * @brief addKeyMapping sets mapping between key/socks hostname used for direct connection and transport. + * The key is sha1(sid, initiator full jid, responder full jid) + * @param key + * @param transport + */ + void addKeyMapping(const QString &key, Transport *transport); + void removeKeyMapping(const QString &key); + + private: + class Private; + std::unique_ptr d; + }; } // namespace S5B } // namespace Jingle } // namespace XMPP diff --git a/src/xmpp/xmpp-im/jingle-sctp-association_p.cpp b/src/xmpp/xmpp-im/jingle-sctp-association_p.cpp new file mode 100644 index 00000000..cba6c7f1 --- /dev/null +++ b/src/xmpp/xmpp-im/jingle-sctp-association_p.cpp @@ -0,0 +1,338 @@ +/* + * jignle-sctp-association_p.h - Private parto of Jingle SCTP Association + * Copyright (C) 2021 Sergey Ilinykh + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library. If not, see . + * + */ + +#include "jingle-sctp-association_p.h" +#include "jingle-sctp.h" +#include "jingle-webrtc-datachannel_p.h" + +#define SCTP_DEBUG(msg, ...) qDebug("jingle-sctp: " msg, ##__VA_ARGS__) + +namespace XMPP { namespace Jingle { namespace SCTP { + + static constexpr int MAX_STREAMS = 65535; // let's change when we need something but webrtc dc. + static constexpr int MAX_MESSAGE_SIZE = 262144; + static constexpr int MAX_SEND_BUFFER_SIZE = 262144; + + std::weak_ptr Keeper::instance; + + Keeper::Keeper() + { + qDebug("init usrsctp"); + DepUsrSCTP::ClassInit(); + } + + Keeper::~Keeper() + { + qDebug("deinit usrsctp"); + DepUsrSCTP::ClassDestroy(); + } + + Keeper::Ptr Keeper::use() + { + auto i = instance.lock(); + if (!i) { + i = std::make_shared(); + instance = i; + } + return i; + } + + AssociationPrivate::AssociationPrivate(Association *q) : + q(q), keeper(Keeper::use()), assoc(this, MAX_STREAMS, MAX_STREAMS, MAX_MESSAGE_SIZE, MAX_SEND_BUFFER_SIZE, true) + { + } + + void AssociationPrivate::OnSctpAssociationConnecting(RTC::SctpAssociation *) + { + qDebug("jignle-sctp: on connecting"); + } + + void AssociationPrivate::OnSctpAssociationConnected(RTC::SctpAssociation *) + { + qDebug("jignle-sctp: on connected"); + for (auto &channel : channels) { + channel.staticCast()->connect(); + } + } + + void AssociationPrivate::OnSctpAssociationFailed(RTC::SctpAssociation *) { qDebug("jignle-sctp: on failed"); } + + void AssociationPrivate::OnSctpAssociationClosed(RTC::SctpAssociation *) { qDebug("jignle-sctp: on closed"); } + + void AssociationPrivate::OnSctpAssociationSendData(RTC::SctpAssociation *, const uint8_t *data, size_t len) + { + // qDebug("jignle-sctp: on outgoing data"); + QByteArray bytes((char *)data, int(len)); + QMetaObject::invokeMethod(this, "onOutgoingData", Q_ARG(QByteArray, bytes)); + } + + void AssociationPrivate::OnSctpAssociationMessageReceived(RTC::SctpAssociation *, uint16_t streamId, uint32_t ppid, + const uint8_t *msg, size_t len) + { + // qDebug("jignle-sctp: on incoming data"); + QByteArray bytes((char *)msg, int(len)); + QMetaObject::invokeMethod(this, "onIncomingData", Q_ARG(QByteArray, bytes), Q_ARG(quint16, streamId), + Q_ARG(quint32, ppid)); + } + + void AssociationPrivate::OnSctpAssociationBufferedAmount(RTC::SctpAssociation *sctpAssociation, uint32_t len) + { + // qDebug("jignle-sctp: on buffered data: %d", len); + Q_UNUSED(sctpAssociation); + Q_UNUSED(len); + if (!dumpingOutogingBuffer) + procesOutgoingMessageQueue(); + // TODO control buffering to reduce memory consumption + } + + void AssociationPrivate::OnSctpStreamClosed(RTC::SctpAssociation *sctpAssociation, uint16_t streamId) + { + qDebug("jignle-sctp: on stream closed"); + Q_UNUSED(sctpAssociation); + QMetaObject::invokeMethod(this, "onStreamClosed", Q_ARG(quint16, streamId)); + } + + void AssociationPrivate::handleIncomingDataChannelOpen(const QByteArray &data, quint16 streamId) + { + auto channel = WebRTCDataChannel::fromChannelOpen(this, data); + + channel->setStreamId(streamId); + pendingChannels.append(channel); + auto it = channels.constFind(streamId); + if (it != channels.constEnd()) { + qWarning("datachannel %u was replaced", streamId); + (*it)->disconnect(this); + (*it).staticCast()->onDisconnected(WebRTCDataChannel::ChannelReplaced); + } + channels.insert(streamId, channel); + connectChannelSignals(channel); + + // acknowledge channel open instantly + QByteArray reply(4, 0); + reply[0] = DCEP_DATA_CHANNEL_ACK; + write(reply, streamId, PPID_DCEP); + + emit q->newIncomingChannel(); + } + + void AssociationPrivate::setIdSelector(IdSelector selector) + { + switch (selector) { + case IdSelector::Even: + useOddStreamId = false; + if (nextStreamId & 1) + nextStreamId++; + break; + case IdSelector::Odd: + useOddStreamId = true; + if (!(nextStreamId & 1)) + nextStreamId++; + break; + } + } + + bool AssociationPrivate::write(const QByteArray &data, quint16 streamId, quint32 ppid, Reliability reliable, + bool ordered, quint32 reliability) + { + // qDebug("jignle-sctp: write %d bytes on stream %u with ppid %u", data.size(), streamId, ppid); + RTC::DataConsumer consumer; + consumer.sctpParameters.streamId = streamId; + consumer.sctpParameters.ordered = ordered; // ordered=true also enables reliability + consumer.sctpParameters.maxPacketLifeTime = reliable == PartialTimers ? reliability : 0; + consumer.sctpParameters.maxRetransmits = reliable == PartialRexmit ? reliability : 0; + bool success; + assoc.SendSctpMessage( + &consumer, ppid, reinterpret_cast(data.data()), data.size(), + new std::function([this, &success](bool cb_success) { success = cb_success; })); + return success; + } + + void AssociationPrivate::procesOutgoingMessageQueue() + { + if (dumpingOutogingBuffer) + return; // we don't need recursion here + + dumpingOutogingBuffer = true; + // keep going while we can fit the buffer + while (outgoingMessageQueue.size()) { + + auto const &[connection, message] = outgoingMessageQueue.first(); + if (int(MAX_SEND_BUFFER_SIZE - assoc.GetSctpBufferedAmount()) < message.data.size()) + break; + + bool ordered = !(message.channelType & 0x80); + Reliability reliable = ordered ? Reliable + : (message.channelType & 0x3) == 1 ? PartialRexmit + : (message.channelType & 0x3) == 2 ? PartialTimers + : Reliable; + + if (write(message.data, message.streamId, PPID_BINARY, reliable, ordered, message.reliability)) { + int sz = message.data.size(); + connection.staticCast()->onMessageWritten(sz); + } else if (assoc.isSendBufferFull()) + break; + else { + qWarning("unexpected sctp write error"); + connection.staticCast()->onError(QAbstractSocket::SocketResourceError); + } + outgoingMessageQueue.removeFirst(); + } + dumpingOutogingBuffer = false; + } + + void AssociationPrivate::close(quint16 streamId) + { + qDebug("jignle-sctp: close"); + RTC::DataProducer producer; + producer.sctpParameters.streamId = streamId; + assoc.DataProducerClosed(&producer); + } + + quint16 AssociationPrivate::takeNextStreamId() + { + if (!channelsLeft) + return 0xffff; // impossible stream + auto id = nextStreamId; + while (channels.contains(id)) { + id += 2; + if (id == nextStreamId) + return 0xffff; + } + nextStreamId = id + 2; + return id; + } + + Connection::Ptr AssociationPrivate::newChannel(Reliability reliable, bool ordered, quint32 reliability, + quint16 priority, const QString &label, const QString &protocol) + { + SCTP_DEBUG("adding new channel"); + int channelType = int(reliable); + if (ordered) + channelType |= 0x80; + auto channel + = QSharedPointer::create(this, channelType, priority, reliability, label, protocol); + if (transportConnected) { + auto id = takeNextStreamId(); + if (id == 0xffff) + return {}; + channel->setStreamId(id); + channels.insert(id, channel); + channelsLeft--; + qWarning("TODO negotiate datachannel itself"); + } else { + pendingLocalChannels.enqueue(channel); + } + connectChannelSignals(channel); + + return channel; + } + + QList AssociationPrivate::allChannels() const + { + QList ret; + ret.reserve(channels.size() + pendingLocalChannels.size()); + ret += channels.values(); + ret += pendingLocalChannels; + return ret; + } + + Connection::Ptr AssociationPrivate::nextChannel() + { + if (pendingChannels.empty()) + return {}; + return pendingChannels.dequeue(); + } + + void AssociationPrivate::onTransportConnected() + { + SCTP_DEBUG("starting sctp association"); + transportConnected = true; + while (pendingLocalChannels.size()) { + auto channel = pendingLocalChannels.dequeue().staticCast(); + auto id = takeNextStreamId(); + if (id == 0xffff) { // impossible channel + channel->onError(QAbstractSocket::SocketResourceError); + } else { + channel->setStreamId(id); + channels.insert(id, channel); + channelsLeft--; + } + } + assoc.TransportConnected(); + } + + void AssociationPrivate::onTransportError(QAbstractSocket::SocketError error) + { + transportConnected = false; + for (auto &c : channels) { + c.staticCast()->onError(error); + } + } + + void AssociationPrivate::onTransportClosed() + { + transportConnected = false; + for (auto &c : channels) { + c.staticCast()->onDisconnected(WebRTCDataChannel::TransportClosed); + } + } + + void AssociationPrivate::onOutgoingData(const QByteArray &data) + { + outgoingPacketsQueue.enqueue(data); + emit q->readyReadOutgoing(); + } + + void AssociationPrivate::onIncomingData(const QByteArray &data, quint16 streamId, quint32 ppid) + { + auto it = channels.find(streamId); + if (it == channels.end()) { + if (ppid == PPID_DCEP) { + if (data.isEmpty()) { + qWarning("jingle-sctp: dropping invalid dcep"); + } else if (data[0] == DCEP_DATA_CHANNEL_OPEN) { + handleIncomingDataChannelOpen(data, streamId); + } + } else + qWarning("jingle-sctp: data from unknown datachannel. ignoring"); + return; + } + it->staticCast()->onIncomingData(data, ppid); + } + + void AssociationPrivate::onStreamClosed(quint16 streamId) + { + auto it = channels.find(streamId); + if (it == channels.end()) { + qDebug("jingle-sctp: closing not existing stream %d", streamId); + return; + } + it->staticCast()->onDisconnected(WebRTCDataChannel::ChannelClosed); + } + + void AssociationPrivate::connectChannelSignals(Connection::Ptr channel) + { + auto dc = channel.staticCast(); + dc->setOutgoingCallback([this, weakDc = dc.toWeakRef()](const WebRTCDataChannel::OutgoingDatagram &dg) { + outgoingMessageQueue.enqueue({ weakDc.lock(), dg }); + procesOutgoingMessageQueue(); + }); + } + +}}} diff --git a/src/xmpp/xmpp-im/jingle-sctp-association_p.h b/src/xmpp/xmpp-im/jingle-sctp-association_p.h new file mode 100644 index 00000000..312e715c --- /dev/null +++ b/src/xmpp/xmpp-im/jingle-sctp-association_p.h @@ -0,0 +1,104 @@ +/* + * jignle-sctp-association_p.h - Private parto of Jingle SCTP Association + * Copyright (C) 2021 Sergey Ilinykh + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library. If not, see . + * + */ + +#pragma once + +#include "jingle-connection.h" + +#include "irisnet/noncore/sctp/DepUsrSCTP.hpp" +#include "irisnet/noncore/sctp/SctpAssociation.hpp" +#include "jingle-sctp.h" +#include "jingle-webrtc-datachannel_p.h" + +#include +#include + +#include + +namespace XMPP { namespace Jingle { namespace SCTP { + + struct Keeper { + using Ptr = std::shared_ptr; + + static std::weak_ptr instance; + Keeper(); + ~Keeper(); + static Ptr use(); + }; + + class Association; + class AssociationPrivate : public QObject, RTC::SctpAssociation::Listener { + Q_OBJECT + public: + using QualifiedOutgoingMessage = std::pair; + + Association *q; + Keeper::Ptr keeper; + QQueue outgoingPacketsQueue; // ready to be sent over dtls + QQueue outgoingMessageQueue; // ready to be processed by sctp stack + std::mutex mutex; + QHash channels; // streamId -> WebRTCDataChannel + QQueue pendingChannels; + QQueue pendingLocalChannels; + RTC::SctpAssociation assoc; + + bool dumpingOutogingBuffer = false; + bool transportConnected = false; + bool useOddStreamId = false; + quint16 nextStreamId = 0; + quint16 channelsLeft = 32768; + + AssociationPrivate(Association *q); + + void OnSctpAssociationConnecting(RTC::SctpAssociation *) override; + void OnSctpAssociationConnected(RTC::SctpAssociation *) override; + void OnSctpAssociationFailed(RTC::SctpAssociation *) override; + void OnSctpAssociationClosed(RTC::SctpAssociation *) override; + void OnSctpAssociationSendData(RTC::SctpAssociation *, const uint8_t *data, size_t len) override; + void OnSctpAssociationMessageReceived(RTC::SctpAssociation *, uint16_t streamId, uint32_t ppid, + const uint8_t *msg, size_t len) override; + void OnSctpAssociationBufferedAmount(RTC::SctpAssociation *sctpAssociation, uint32_t len) override; + void OnSctpStreamClosed(RTC::SctpAssociation *sctpAssociation, uint16_t streamId) override; + + void handleIncomingDataChannelOpen(const QByteArray &data, quint16 streamId); + void setIdSelector(IdSelector selector); + bool write(const QByteArray &data, quint16 streamId, quint32 ppid, Reliability reliable = Reliable, + bool ordered = true, quint32 reliability = 0); + void close(quint16 streamId); + quint16 takeNextStreamId(); + Connection::Ptr newChannel(Reliability reliable, bool ordered, quint32 reliability, quint16 priority, + const QString &label, const QString &protocol); + QList allChannels() const; + Connection::Ptr nextChannel(); + + void onTransportConnected(); + void onTransportError(QAbstractSocket::SocketError error); + void onTransportClosed(); + + private Q_SLOTS: + void onOutgoingData(const QByteArray &data); + void onIncomingData(const QByteArray &data, quint16 streamId, quint32 ppid); + void onStreamClosed(quint16 streamId); + + private: + void connectChannelSignals(Connection::Ptr channel); + void procesOutgoingMessageQueue(); + }; + +}}} diff --git a/src/xmpp/xmpp-im/jingle-sctp.cpp b/src/xmpp/xmpp-im/jingle-sctp.cpp new file mode 100644 index 00000000..ddf1a730 --- /dev/null +++ b/src/xmpp/xmpp-im/jingle-sctp.cpp @@ -0,0 +1,143 @@ +/* + * jignle-sctp.cpp - Jingle SCTP + * Copyright (C) 2021 Sergey Ilinykh + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library. If not, see . + * + */ + +#include "jingle-sctp.h" + +#include "jingle-sctp-association_p.h" +#include "jingle-transport.h" +#include "jingle-webrtc-datachannel_p.h" +#include "xmpp_xmlcommon.h" + +#include +#include + +#include + +#define SCTP_DEBUG(msg, ...) qDebug("jingle-sctp: " msg, ##__VA_ARGS__) + +namespace XMPP { namespace Jingle { namespace SCTP { + + QDomElement MapElement::toXml(QDomDocument *doc) const + { + QDomElement ret; + if (protocol == Protocol::None) + return ret; + ret = doc->createElementNS(ns(), QLatin1String("sctpmap")); + ret.setAttribute(QLatin1String("protocol"), QLatin1String("webrtc-datachannel")); + ret.setAttribute(QLatin1String("number"), port); + return ret; + } + + bool MapElement::parse(const QDomElement &el) + { + if (el.namespaceURI() != ns()) { + return false; + } + auto p = el.attribute(QLatin1String("protocol")); + protocol = (p == QLatin1String("webrtc-datachannel")) ? Protocol::WebRTCDataChannel : Protocol::None; + port = el.attribute(QLatin1String("number")).toInt(); + return protocol != Protocol::None && port > 0; + } + + QDomElement ChannelElement::toXml(QDomDocument *doc) const + { + auto el = doc->createElementNS(ns(), QLatin1String("channel")); + el.setAttribute(QLatin1String("id"), id); + el.setAttribute(QLatin1String("maxPacketLifeTime"), maxPacketLifeTime); + el.setAttribute(QLatin1String("maxRetransmits"), maxRetransmits); + el.setAttribute(QLatin1String("negotiated"), negotiated); + el.setAttribute(QLatin1String("ordered"), ordered); + el.setAttribute(QLatin1String("protocol"), protocol); + return el; + } + + bool ChannelElement::parse(const QDomElement &el) + { + if (el.namespaceURI() != webrtcDcNs()) { + return false; + } + bool ok; + id = el.attribute(QLatin1String("id")).toUShort(&ok); // REVIEW XEP says id is optional. but is it? + if (!ok) + return false; + QString mplt = el.attribute(QLatin1String("maxPacketLifeTime")); + QString mrtx = el.attribute(QLatin1String("maxRetransmits")); + if (!mplt.isEmpty()) { + maxPacketLifeTime = mplt.toUShort(&ok); + if (!ok) + return false; + } + if (!mrtx.isEmpty()) { + maxRetransmits = mrtx.toUShort(&ok); + if (!ok) + return false; + } + if (maxPacketLifeTime > 0 && maxRetransmits > 0) { + qWarning("found both maxPacketLifeTime and maxRetransmits. expected just one of them"); + return false; + } + XMLHelper::readBoolAttribute(el, QLatin1String("negotiated"), &negotiated); + XMLHelper::readBoolAttribute(el, QLatin1String("ordered"), &ordered); + protocol = el.attribute(QLatin1String("protocol")); + return true; + } + + QString ns() { return QStringLiteral("urn:xmpp:jingle:transports:dtls-sctp:1"); } + QString webrtcDcNs() { return QStringLiteral("urn:xmpp:jingle:transports:webrtc-datachannel:0"); } + + Association::Association(QObject *parent) : QObject(parent), d(new AssociationPrivate(this)) { } + + Association::~Association() { } + + void Association::setIdSelector(IdSelector selector) { d->setIdSelector(selector); } + + QByteArray Association::readOutgoing() + { + // SCTP_DEBUG("read outgoing"); + std::lock_guard lock(d->mutex); + return d->outgoingPacketsQueue.isEmpty() ? QByteArray() : d->outgoingPacketsQueue.dequeue(); + } + + void Association::writeIncoming(const QByteArray &data) + { + // SCTP_DEBUG("write incoming"); + d->assoc.ProcessSctpData(reinterpret_cast(data.data()), data.size()); + } + + int Association::pendingOutgoingDatagrams() const { return d->outgoingPacketsQueue.size(); } + + int Association::pendingChannels() const { return d->pendingChannels.size(); } + + Connection::Ptr Association::nextChannel() { return d->nextChannel(); } + + Connection::Ptr Association::newChannel(Reliability reliable, bool ordered, quint32 reliability, quint16 priority, + const QString &label, const QString &protocol) + { + return d->newChannel(reliable, ordered, reliability, priority, label, protocol); + } + + QList Association::channels() const { return d->allChannels(); } + + void Association::onTransportConnected() { d->onTransportConnected(); } + + void Association::onTransportError(QAbstractSocket::SocketError error) { d->onTransportError(error); } + + void Association::onTransportClosed() { d->onTransportClosed(); } + +}}} diff --git a/src/xmpp/xmpp-im/jingle-sctp.h b/src/xmpp/xmpp-im/jingle-sctp.h new file mode 100644 index 00000000..0ac2d1ee --- /dev/null +++ b/src/xmpp/xmpp-im/jingle-sctp.h @@ -0,0 +1,102 @@ +/* + * jignle-sctp.cpp - Jingle SCTP + * Copyright (C) 2021 Sergey Ilinykh + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library. If not, see . + * + */ + +#ifndef JINGLE_SCTP_H +#define JINGLE_SCTP_H + +#include "jingle-connection.h" + +#include +#include +#include + +#include + +class QDomDocument; + +namespace XMPP { namespace Jingle { namespace SCTP { + enum class Protocol { None, WebRTCDataChannel }; + enum Reliability { Reliable, PartialRexmit, PartialTimers }; + enum class IdSelector { Odd, Even }; + + struct MapElement { + Protocol protocol = Protocol::None; + uint16_t port = 0; + uint16_t streams = 65535; + + MapElement() = default; + inline MapElement(Protocol protocol, uint16_t port, uint16_t streams) : + protocol(protocol), port(port), streams(streams) + { + } + inline MapElement(const QDomElement &el) { parse(el); } + bool isValid() const { return protocol != Protocol::None; } + QDomElement toXml(QDomDocument *doc) const; + bool parse(const QDomElement &el); + }; + + struct ChannelElement { + uint16_t id = 0; + uint16_t maxPacketLifeTime = 0; + uint16_t maxRetransmits = 0; + bool negotiated = false; + bool ordered = true; + QString protocol; + + QDomElement toXml(QDomDocument *doc) const; + bool parse(const QDomElement &el); + }; + + QString ns(); + QString webrtcDcNs(); + + class AssociationPrivate; + class Association : public QObject { + Q_OBJECT; + + public: + Association(QObject *parent); + ~Association(); + + void setIdSelector(IdSelector selector); + QByteArray readOutgoing(); + void writeIncoming(const QByteArray &data); + int pendingOutgoingDatagrams() const; + int pendingChannels() const; + Connection::Ptr nextChannel(); + Connection::Ptr newChannel(Reliability reliable = Reliable, bool ordered = true, quint32 reliability = 0, + quint16 priority = 256, const QString &label = QString(), + const QString &protocol = QString()); + QList channels() const; + // call this when dtls connected + void onTransportConnected(); + void onTransportError(QAbstractSocket::SocketError error); + void onTransportClosed(); + + signals: + void readyReadOutgoing(); + void newIncomingChannel(); + + private: + std::unique_ptr d; + }; + +}}} + +#endif diff --git a/src/xmpp/xmpp-im/jingle-session.cpp b/src/xmpp/xmpp-im/jingle-session.cpp new file mode 100644 index 00000000..0e76a5a3 --- /dev/null +++ b/src/xmpp/xmpp-im/jingle-session.cpp @@ -0,0 +1,1276 @@ +/* + * jignle-session.cpp - Jingle Session + * Copyright (C) 2019 Sergey Ilinykh + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library. If not, see . + * + */ + +#include "jingle-session.h" + +#include "jingle-application.h" +#include "xmpp/jid/jid.h" +#include "xmpp_caps.h" +#include "xmpp_client.h" +#include "xmpp_task.h" +#include "xmpp_xmlcommon.h" + +#include +#include + +template constexpr std::add_const_t &as_const(T &t) noexcept { return t; } + +#if QT_VERSION < QT_VERSION_CHECK(5, 7, 0) +// this adds const to non-const objects (like std::as_const) +template Q_DECL_CONSTEXPR typename std::add_const::type &std::as_const(T &t) noexcept { return t; } +// prevent rvalue arguments: +template void std::as_const(const T &&) = delete; +#endif + +namespace XMPP { namespace Jingle { + //---------------------------------------------------------------------------- + // JT - Jingle Task + //---------------------------------------------------------------------------- + class JT : public Task { + Q_OBJECT + + QDomElement iq_; + Jid to_; + + public: + JT(Task *parent) : Task(parent) { } + + ~JT() { } + + void request(const Jid &to, const QDomElement &jingleEl) + { + to_ = to; + iq_ = createIQ(doc(), "set", to.full(), id()); + iq_.appendChild(jingleEl); + } + + void onGo() { send(iq_); } + + bool take(const QDomElement &x) + { + if (!iqVerify(x, to_, id())) + return false; + + if (x.attribute("type") == "error") { + setError(x); + } else { + setSuccess(); + } + return true; + } + }; + + //---------------------------------------------------------------------------- + // Session + //---------------------------------------------------------------------------- + class Session::Private { + public: + Session *q; + Manager *manager; + QTimer stepTimer; + State state = State::Created; // state of session on our side. if it's incoming we start from Created anyaway + // but Pending state is skipped + Origin role = Origin::Initiator; // my role in the session + XMPP::Stanza::Error lastError; + Reason terminateReason; + QMap> applicationPads; + QMap> transportPads; + QMap contentList; + QSet signalingContent; + QHash groups; + + // not yet acccepted applications from initial incoming request + QList initialIncomingUnacceptedContent; + + // session level updates. session-info for example or some rejected apps + QHash outgoingUpdates; + + QString sid; + Jid origFrom; // "from" attr of IQ. + Jid otherParty; // either "from" or initiator/responder. it's where to send all requests. + Jid localParty; // that one will be set as initiator/responder if provided + bool waitingAck = false; + bool needNotifyGroup = false; // whenever grouping info changes + bool groupingAllowed = false; + + void setSessionFinished() + { + state = State::Finished; + emit q->terminated(); + signalingContent.clear(); + for (auto &c : contentList) { + if (c->state() != State::Finished) { + c->setState(State::Finished); + } + } + auto vals = contentList.values(); + contentList.clear(); + while (vals.size()) { + vals.takeLast()->deleteLater(); + } + q->deleteLater(); + } + + QList genGroupingXML() + { + QList ret; + if (!groupingAllowed) + return ret; + + QDomDocument &doc = *manager->client()->doc(); + + QHashIterator it(groups); + while (it.hasNext()) { + it.next(); + auto g = doc.createElementNS(QLatin1String("urn:xmpp:jingle:apps:grouping:0"), QLatin1String("group")); + g.setAttribute(QLatin1String("semantics"), it.key()); + for (auto const &name : it.value()) { + auto c = doc.createElement(QLatin1String("content")); + c.setAttribute(QLatin1String("name"), name); + g.appendChild(c); + } + ret.append(g); + } + return ret; + } + + template void notifyPads() + { + for (auto &weakPad : transportPads) { + auto pad = weakPad.lock(); + if (pad) { + (pad.data()->*func)(); // just calls pad's method + } + } + for (auto &weakPad : applicationPads) { + auto pad = weakPad.lock(); + if (pad) { + (pad.data()->*func)(); + } + } + } + + void sendJingle(Action action, QList update, + std::function callback = std::function()) + { + QDomDocument &doc = *manager->client()->doc(); + Jingle jingle(action, sid); + if (action == Action::SessionInitiate) { + jingle.setInitiator(manager->client()->jid()); + } + if (action == Action::SessionAccept) { + jingle.setResponder(manager->client()->jid()); + } + auto xml = jingle.toXml(&doc); + + for (const QDomElement &e : update) { + xml.appendChild(e); + } + if (needNotifyGroup + && (action == Action::SessionInitiate || action == Action::SessionAccept || action == Action::ContentAdd + || action == Action::ContentAccept)) { + const auto xmls = genGroupingXML(); + for (auto const &g : xmls) + xml.appendChild(g); + needNotifyGroup = false; + } + + auto jt = new JT(manager->client()->rootTask()); + jt->request(otherParty, xml); + QObject::connect(jt, &JT::finished, q, [jt, jingle, callback, this]() { + waitingAck = false; + if (callback) { + callback(jt); + } + if (!jt->success()) { + lastError = jt->error(); + } + planStep(); + }); + waitingAck = true; + jt->go(true); + } + + void planStep() + { + if (waitingAck) { + return; + } + lastError = Stanza::Error(0, 0); + if (!stepTimer.isActive()) { + stepTimer.start(); + } + } + + void doStep() + { + if (waitingAck || state == State::Finished) { + // in waitingAck we will return here later + qDebug("jingle-doStep: skip step: %s", waitingAck ? "waitingAck" : "session already finished"); + return; + } + + if (terminateReason.condition() && state != State::Finished) { + if (state != State::Created || role == Origin::Responder) { + sendJingle(Action::SessionTerminate, + QList() << terminateReason.toXml(manager->client()->doc())); + } + setSessionFinished(); + qDebug("jingle-doStep: the step finished the session due to terminationReason previously set"); + return; + } + + if (state == State::Created && role == Origin::Responder) { + // we could fail very early if something went wrong with transports init for example + Reason reason; + bool all = true; + for (auto const &c : std::as_const(contentList)) { + if (c->state() < State::Finishing) { + all = false; + break; + } + + if (c->state() == State::Finishing) { + auto upd = c->evaluateOutgoingUpdate(); + if (upd.action == Action::ContentRemove && upd.reason.condition()) { + reason = upd.reason; + } + } + } + if (all) { + terminateReason = reason; + sendJingle(Action::SessionTerminate, + QList() << terminateReason.toXml(manager->client()->doc())); + setSessionFinished(); + qDebug("jingle-doStep: all apps finished -> session finished"); + return; + } + } + + if (state == State::Created) { + qDebug("jingle-doStep: still in Created state. exit"); + return; // should wait for user approval of send/accept + } + + if (outgoingUpdates.size()) { + auto it = outgoingUpdates.begin(); + auto action = it.key(); + auto updates = it.value(); + auto elements = std::get<0>(updates); + auto cb = std::get<1>(updates); + outgoingUpdates.erase(it); + sendJingle(action, elements, cb); + qDebug("jingle-doStep: sent outgoingUpdates"); + return; + } + + QList updateXml; + for (auto &mp : applicationPads) { + auto p = mp.toStrongRef(); + QDomElement el = p->takeOutgoingSessionInfoUpdate(); + if (!el.isNull()) { + updateXml.append(el); + // we can send session-info for just one application. so stop processing + sendJingle(Action::SessionInfo, updateXml, [](JT *jt) { + if (!jt->success()) + qWarning("failure for session-info is ignored"); + }); + qDebug("jingle-doStep: sent session info"); + return; + } + } + + typedef std::tuple, OutgoingUpdateCB> AckHndl; // will be used from callback on iq ack + if (state == State::ApprovedToSend) { // we are going to send session-initiate/accept (already accepted + // by the user but not sent yet) + if (trySendSessionAcceptOrInitiate()) { + qDebug("jingle-doStep: session is not yet ready to be accepted/initiated"); + return; // accepted / initiated or finished with a failure + } + } + + QMultiMap updates; + qDebug("jingle-doStep: %lld applications have updates", qsizetype(signalingContent.size())); + for (auto app : std::as_const(signalingContent)) { + auto updateType = app->evaluateOutgoingUpdate(); + if (updateType.action != Action::NoAction) { + if (state == State::ApprovedToSend && app->flags() & Application::InitialApplication) { + // We need pass here everthing not checked in trySendSessionAcceptOrInitiate + if ((role == Origin::Initiator && updateType.action == Action::ContentAdd) + || (role == Origin::Responder && updateType.action == Action::ContentAccept)) { + continue; // skip in favor of trySendSessionAcceptOrInitiate + } + } + updates.insert(updateType, app); + } + } + + QList acceptApps; + if (updates.size()) { + auto upd = updates.begin().key(); // NOTE maybe some actions have more priority than others + auto const apps = updates.values(upd); + for (auto app : apps) { + QList xml; + OutgoingUpdateCB callback; + std::tie(xml, callback) = app->takeOutgoingUpdate(); + updateXml += xml; + if (callback) { + acceptApps.append(AckHndl { app, callback }); + } + } + sendJingle(upd.action, updateXml, [this, acceptApps](JT *jt) { + for (const auto &h : acceptApps) { + auto app = std::get<0>(h); + auto callback = std::get<1>(h); + if (app) { + callback(jt); + } + } + planStep(); + }); + } + } + + bool trySendSessionAcceptOrInitiate() + { + /* + * For session-initiate everything is pretty much straightforward, just any content with + * Action::ContentAdd update type has to be added. But with session-accept things are more complicated + * 1. Local client could add its content. So we have to check content origin too. + * 2. Remote client could add more content before local session-accept. Then we have two options + * a) send content-accept and skip this content in session-accept later + * b) don't send content-accept and accept everything with session-accept + * We prefer option (b) in our implementation. + */ + typedef std::tuple, OutgoingUpdateCB> AckHndl; + if (role == Origin::Responder) { + for (const auto &c : std::as_const(initialIncomingUnacceptedContent)) { + auto out = c->evaluateOutgoingUpdate(); + if (out.action == Action::ContentReject) { + lastError = XMPP::Stanza::Error(XMPP::Stanza::Error::Cancel, XMPP::Stanza::Error::BadRequest); + setSessionFinished(); + return true; + } + if (out.action != Action::ContentAccept) { + return false; // keep waiting. + } + } + } else { + for (const auto &c : std::as_const(contentList)) { + auto out = c->evaluateOutgoingUpdate(); + if (out.action == Action::ContentRemove) { + lastError = XMPP::Stanza::Error(XMPP::Stanza::Error::Cancel, XMPP::Stanza::Error::BadRequest); + setSessionFinished(); + return true; + } + if (out.action != Action::ContentAdd) { + return false; // keep waiting. + } + } + } + Action actionToSend = Action::SessionAccept; + State finalState = State::Active; + // so all contents is ready for session-initiate. let's do it + if (role == Origin::Initiator) { + sid = manager->registerSession(q); + actionToSend = Action::SessionInitiate; + finalState = State::Pending; + } + + notifyPads<&SessionManagerPad::onSend>(); + + QList contents; + QList acceptApps; + for (const auto &app : std::as_const(contentList)) { + QList xml; + OutgoingUpdateCB callback; + std::tie(xml, callback) = app->takeOutgoingUpdate(); + contents += xml; + // p->setState(State::Unacked); + if (callback) { + acceptApps.append(AckHndl { app, callback }); + } + } + + state = State::Unacked; + initialIncomingUnacceptedContent.clear(); + sendJingle(actionToSend, contents, [this, acceptApps, finalState](JT *jt) { + if (!jt->success()) { + qDebug("Session accept/initiate returned iq error"); + emit q->terminated(); + return; + } + state = finalState; + for (const auto &h : acceptApps) { + auto app = std::get<0>(h); + auto callback = std::get<1>(h); + if (app) { + callback(jt); + if (role == Origin::Responder) { + app->start(); + } + } + } + if (finalState == State::Active) { + emit q->activated(); + } + planStep(); + }); + + return true; + } + + Reason reason(const QDomElement &jingleEl) + { + QDomElement re = jingleEl.firstChildElement(QLatin1String("reason")); + Reason reason; + if (!re.isNull()) { + reason = Reason(re); + if (!reason.isValid()) { + qDebug("invalid reason"); + } + } + return reason; + } + + using TransportResult = std::tuple>; + TransportResult parseIncomingTransport(const QDomElement &contentEl) + { + auto tel = contentEl.firstChildElement(QLatin1String("transport")); + QString transportNS; + if (tel.isNull() || (transportNS = tel.namespaceURI()).isEmpty()) { + TransportResult { false, Reason::NoReason, QSharedPointer() }; + } + auto trPad = q->transportPadFactory(transportNS); + if (!trPad) { + return TransportResult { true, Reason::UnsupportedTransports, QSharedPointer() }; + } + auto transport = trPad->manager()->newTransport(trPad, negateOrigin(role)); + if (transport && transport->update(tel)) { + return TransportResult { true, Reason::NoReason, transport }; + } + return TransportResult { false, Reason::NoReason, QSharedPointer() }; + } + + void addAndInitContent(Origin creator, Application *content) + { + contentList.insert(ContentKey { content->contentName(), creator }, content); + if (state != State::Created && content->evaluateOutgoingUpdate().action != Action::NoAction) { + signalingContent.insert(content); + } + QObject::connect(content, &Application::updated, q, [this, content]() { + signalingContent.insert(content); + planStep(); + }); + QObject::connect(content, &Application::destroyed, q, [this, content]() { + signalingContent.remove(content); + initialIncomingUnacceptedContent.removeOne(content); + for (auto it = contentList.begin(); it != contentList.end(); ++it) { // optimize for large lists? + if (it.value() == content) { + contentList.erase(it); + break; + } + } + }); + } + + enum AddContentError { Ok, Unparsed, Unexpected, Unsupported }; + + std::tuple parseContentAdd(const QDomElement &ce) + { + QDomElement descriptionEl = ce.firstChildElement(QLatin1String("description")); + QString descriptionNS = descriptionEl.namespaceURI(); + typedef std::tuple result; + + ContentBase c(ce); + auto trpr = parseIncomingTransport(ce); + if (!c.isValid() || descriptionEl.isNull() || descriptionNS.isEmpty() || !std::get<0>(trpr)) { + return result { Unparsed, Reason::Success, nullptr }; + } + + auto appPad = q->applicationPadFactory(descriptionNS); + auto trReason = std::get<1>(trpr); + if (!appPad || trReason != Reason::NoReason) { + return result { Unsupported, trReason == Reason::NoReason ? Reason::UnsupportedApplications : trReason, + nullptr }; + } + std::unique_ptr app(appPad->manager()->startApplication(appPad, c.name, c.creator, c.senders)); + if (!app) + return result { Unparsed, Reason::Success, nullptr }; + + auto descErr = app->setRemoteOffer(descriptionEl); + if (descErr == Application::IncompatibleParameters) { + return result { Unsupported, Reason::IncompatibleParameters, nullptr }; + } else if (descErr == Application::Unparsed) { + return result { Unparsed, Reason::Success, nullptr }; + } + + if (app->setTransport(std::get<2>(trpr))) { + return result { Ok, Reason::Success, app.release() }; + } + // TODO We can do transport-replace in all cases where std::get<1>(trpr) != NoReason + return result { Unsupported, Reason::IncompatibleParameters, app.release() }; + } + + typedef std::tuple, QList> + ParseContentListResult; + + ParseContentListResult parseContentAddList(const QDomElement &jingleEl) + { + QMap addSet; + QMap> rejectSet; + + QString contentTag(QStringLiteral("content")); + for (QDomElement ce = jingleEl.firstChildElement(contentTag); !ce.isNull(); + ce = ce.nextSiblingElement(contentTag)) { + + Private::AddContentError err; + Reason::Condition cond; + Application *app; + + std::tie(err, cond, app) = parseContentAdd(ce); + if (err == Private::AddContentError::Unparsed) { + lastError = XMPP::Stanza::Error(XMPP::Stanza::Error::Cancel, XMPP::Stanza::Error::BadRequest); + qDeleteAll(addSet); + return ParseContentListResult(Unparsed, cond, QList(), QList()); + } + + auto contentName = ce.attribute(QLatin1String("name")); + auto it = addSet.find(contentName); + if (err != Private::AddContentError::Ok) { + // can't continue as well + if (app) { // we are going to reject it completely so delete + delete app; + } + + if (it == addSet.end()) { + rejectSet.insert(contentName, std::make_pair(ce, cond)); + } + continue; + } + + rejectSet.remove(contentName); + // REVIEW probably not wantBetterTransport but wantBetterApplication + if (it == addSet.end() || (*it)->wantBetterTransport(app->transport())) { + if (it == addSet.end()) { + addSet.insert(contentName, app); + } else { + delete *it; // unpreferred app + *it = app; + } + } + } + + if (rejectSet.size()) { + QList rejectList; + for (auto const &i : rejectSet) { + rejectList.append(i.first); + } + return ParseContentListResult(Unsupported, rejectSet.first().second, addSet.values(), rejectList); + } + + return ParseContentListResult(Ok, Reason::Success, addSet.values(), QList()); + } + + std::tuple parseContentAccept(const QDomElement &ce) + { + QDomElement descriptionEl = ce.firstChildElement(QLatin1String("description")); + QDomElement transportEl = ce.firstChildElement(QLatin1String("transport")); + QString descriptionNS = descriptionEl.namespaceURI(); + QString transportNS = transportEl.namespaceURI(); + typedef std::tuple result; + + ContentBase c(ce); + if (!c.isValid() || role != c.creator || descriptionEl.isNull() || transportEl.isNull() + || descriptionNS.isEmpty() || transportNS.isEmpty()) { + return result { Unparsed, Reason::NoReason, nullptr }; + } + + auto app = q->content(c.name, role); + if (!(app && app->state() == State::Pending)) { // reaccept is possible + return result { AddContentError::Unexpected, Reason::NoReason, app }; + } + + if (app->pad()->ns() != descriptionNS || app->transport()->pad()->ns() != transportNS) { + // well it's more than unexpected. let's send unparsed + return result { AddContentError::Unparsed, Reason::NoReason, app }; + } + + if (!app->transport()->update(transportEl)) { + // clearly unparsed. otherwise the app will generate failure event with a Reason. + return result { AddContentError::Unparsed, Reason::NoReason, app }; + } + + auto ansret = app->setRemoteAnswer(descriptionEl); + if (ansret == Application::Unparsed) + return result { AddContentError::Unparsed, Reason::NoReason, app }; + + if (ansret == Application::IncompatibleParameters || app->state() != State::Accepted) { + // parsed but was not accepted. so it's somehow incompatible + return result { AddContentError::Unsupported, Reason::IncompatibleParameters, app }; + } + + return result { AddContentError::Ok, Reason::Success, app }; + } + + std::tuple> parseContentAcceptList(const QDomElement &jingleEl) + { + QMap acceptSet; + QMap> rejectSet; + + QString contentTag(QStringLiteral("content")); + for (QDomElement ce = jingleEl.firstChildElement(contentTag); !ce.isNull(); + ce = ce.nextSiblingElement(contentTag)) { + + Private::AddContentError err; + Reason::Condition cond; + Application *app; + + std::tie(err, cond, app) = parseContentAccept(ce); + if (err == Private::AddContentError::Unparsed || err == Private::AddContentError::Unexpected) { + for (auto &a : acceptSet) { + a->setState(State::Pending); // reset state to pending for already passed validation before + // passing error back + } + lastError = XMPP::Stanza::Error(XMPP::Stanza::Error::Cancel, + err == Private::AddContentError::Unexpected + ? XMPP::Stanza::Error::UnexpectedRequest + : XMPP::Stanza::Error::BadRequest); + if (err == Private::AddContentError::Unexpected) { + ErrorUtil::fill(jingleEl.ownerDocument(), lastError, ErrorUtil::OutOfOrder); + } + return std::tuple>(false, QList()); + } + + auto contentName = app->contentName(); + auto it = acceptSet.find(contentName); + auto rit = rejectSet.find(contentName); + if (it != acceptSet.end() || rit != rejectSet.end()) { + // duplicates are not allowed in accept request + for (auto &a : acceptSet) { + a->setState(State::Pending); // reset state to pending for already passed validation before + // passing error back + } + lastError = XMPP::Stanza::Error(XMPP::Stanza::Error::Cancel, XMPP::Stanza::Error::BadRequest); + return std::tuple>(false, QList()); + } + + if (err != Private::AddContentError::Ok) { + app->setState(State::Finished); // we can't keep working with this content for whatever reason. if + // "accept" failed there is no fallback + rejectSet.insert( + contentName, + std::make_pair(ce, + cond)); // NOTE, probably instead of ce we have to generate original description + continue; + } + acceptSet.insert(contentName, app); + } + + if (rejectSet.size()) { + QTimer::singleShot(0, q, [this, rejectSet]() mutable { + auto cond = rejectSet.first().second; + QList rejects; + for (auto const &i : std::as_const(rejectSet)) { + rejects.append(i.first); + } + rejects += Reason(cond).toXml(manager->client()->doc()); + outgoingUpdates.insert(Action::ContentRemove, + OutgoingUpdate { rejects, [this, rejects](bool) { + for (auto &r : rejects) { + ContentBase c(r); + delete contentList.take(ContentKey { c.name, role }); + } + if (contentList.isEmpty()) { + // the other party has to generate session-terminate + // but we do not care already + setSessionFinished(); + } + } }); + }); + } + return std::tuple>(true, acceptSet.values()); + } + + bool handleIncomingContentAdd(const QDomElement &jingleEl) + { + Private::AddContentError err; + Reason::Condition cond; + QList apps; + QList rejects; + + std::tie(err, cond, apps, rejects) = parseContentAddList(jingleEl); + switch (err) { + case Private::AddContentError::Unparsed: + case Private::AddContentError::Unexpected: + lastError = XMPP::Stanza::Error(XMPP::Stanza::Error::Cancel, XMPP::Stanza::Error::BadRequest); + if (err == Private::AddContentError::Unexpected) { + ErrorUtil::fill(jingleEl.ownerDocument(), lastError, ErrorUtil::OutOfOrder); + } + return false; + case Private::AddContentError::Unsupported: + rejects += Reason(cond).toXml(manager->client()->doc()); + outgoingUpdates.insert(Action::ContentReject, OutgoingUpdate { rejects, OutgoingUpdateCB() }); + break; + case Private::AddContentError::Ok: + break; + } + + if (apps.size()) { + Origin remoteRole = negateOrigin(role); + for (auto app : std::as_const(apps)) { + addAndInitContent(remoteRole, app); // TODO check conflicts + } + QTimer::singleShot(0, q, [this]() { emit q->newContentReceived(); }); + } + planStep(); + + return true; + } + + bool handleIncomingContentRemove(const QDomElement &jingleEl) + { + QSet toRemove; + QString contentTag(QStringLiteral("content")); + for (QDomElement ce = jingleEl.firstChildElement(contentTag); !ce.isNull(); + ce = ce.nextSiblingElement(contentTag)) { + ContentBase cb(ce); + if (!cb.isValid()) { + lastError = XMPP::Stanza::Error(XMPP::Stanza::Error::Cancel, XMPP::Stanza::Error::BadRequest); + return false; + } + Application *app = contentList.value(ContentKey { cb.name, cb.creator }); + if (app) { + toRemove.insert(app); + } + } + + auto reasonEl = jingleEl.firstChildElement(QString::fromLatin1("reason")); + Reason reason = reasonEl.isNull() ? Reason(Reason::Success) : Reason(reasonEl); + + for (auto app : toRemove) { + app->incomingRemove(reason); + contentList.remove(ContentKey { app->contentName(), app->creator() }); + delete app; + } + + if (contentList.isEmpty()) { + terminateReason = reason; + } + + planStep(); + return true; + } + + bool handleIncomingSessionTerminate(const QDomElement &jingleEl) + { + terminateReason = Reason(jingleEl.firstChildElement(QString::fromLatin1("reason"))); + setSessionFinished(); + return true; + } + + bool handleIncomingSessionAccept(const QDomElement &jingleEl) + { + bool parsed; + QList apps; + + std::tie(parsed, apps) = parseContentAcceptList(jingleEl); + if (!parsed) { + lastError = XMPP::Stanza::Error(XMPP::Stanza::Error::Cancel, XMPP::Stanza::Error::BadRequest); + return false; + } + + state = State::Connecting; + if (apps.size()) { + for (auto app : std::as_const(apps)) { + app->start(); + } + } + QTimer::singleShot(0, q, [this]() { emit q->activated(); }); + planStep(); + + return true; + } + + bool handleIncomingContentAccept(const QDomElement &jingleEl) + { + bool parsed; + QList apps; + + std::tie(parsed, apps) = parseContentAcceptList(jingleEl); // marks valid apps as accepted + if (!parsed) { + lastError = XMPP::Stanza::Error(XMPP::Stanza::Error::Cancel, XMPP::Stanza::Error::BadRequest); + return false; + } + + if (apps.size() && state >= State::Active) { + for (auto app : std::as_const(apps)) { + app->start(); // start accepted app. connection establishing and data transfer are inside + } + } + planStep(); + + return true; + } + + bool handleIncomingTransportReplace(const QDomElement &jingleEl) + { + qDebug("handle incoming transport replace"); + QVector, QDomElement>> passed; + QList toReject; + QString contentTag(QStringLiteral("content")); + bool doTieBreak = false; + for (QDomElement ce = jingleEl.firstChildElement(contentTag); !ce.isNull(); + ce = ce.nextSiblingElement(contentTag)) { + + ContentBase cb(ce); + bool transportParsed; + Reason::Condition errReason; + QSharedPointer transport; + std::tie(transportParsed, errReason, transport) = parseIncomingTransport(ce); + + if (!cb.isValid() || !transportParsed) { + lastError = XMPP::Stanza::Error(XMPP::Stanza::Error::Cancel, XMPP::Stanza::Error::BadRequest); + return false; + } + Application *app = contentList.value(ContentKey { cb.name, cb.creator }); + if (!app || (app->creator() == role && app->state() <= State::Unacked)) { + qDebug("not existing app or inaporpriate app state"); + lastError = XMPP::Stanza::Error(XMPP::Stanza::Error::Cancel, XMPP::Stanza::Error::ItemNotFound); + return false; + } + + if (errReason) { + qDebug("failed to construct transport"); + toReject.append(ce); + continue; + } + + auto old = app->transport(); + Q_ASSERT(old != nullptr); + // if it's my transport and it's sent but unacknowledged but has to be accepted + if (old->isLocal() && old->state() == State::Unacked && role == Origin::Initiator) { + doTieBreak = true; + continue; + } + + if (!app->transportSelector()->canReplace(app->transport(), transport)) { + qDebug("incoming unsupported or already used transport"); + toReject.append(ce); + continue; + } + + if (!app->isTransportReplaceEnabled()) { + qDebug("transport replace is disabled for %s", qPrintable(app->contentName())); + toReject.append(ce); + continue; + } + + passed.append(std::make_tuple(app, transport, ce)); + } + + for (auto &v : passed) { + Application *app; + QSharedPointer transport; + QDomElement ce; + std::tie(app, transport, ce) = v; + if (doTieBreak) { + if (app->transport()->creator() == role && app->transport()->state() < State::Unacked) + continue; // it will send transport soon + app->selectNextTransport(transport); + } else if (!app->setTransport(transport)) { + // app should generate transport accept eventually. content-accept will + // work too if the content wasn't accepted yet + toReject.append(ce); + } + } + + if (doTieBreak) { + lastError = ErrorUtil::makeTieBreak(*manager->client()->doc()); + return false; + } else if (toReject.size()) { + QList rejectImported; + std::transform(toReject.begin(), toReject.end(), std::back_inserter(rejectImported), + [this](const QDomElement &e) { + return manager->client()->doc()->importNode(e.cloneNode(true), true).toElement(); + }); + outgoingUpdates.insert(Action::TransportReject, OutgoingUpdate { rejectImported, OutgoingUpdateCB() }); + } + + planStep(); + return true; + } + + bool handleIncomingTransportAccept(const QDomElement &jingleEl) + { + QString contentTag(QStringLiteral("content")); + QVector> updates; + for (QDomElement ce = jingleEl.firstChildElement(contentTag); !ce.isNull(); + ce = ce.nextSiblingElement(contentTag)) { + ContentBase cb(ce); + auto transportEl = ce.firstChildElement(QString::fromLatin1("transport")); + QString transportNS = transportEl.namespaceURI(); + if (!cb.isValid() || transportEl.isNull() || transportNS.isEmpty()) { + lastError = XMPP::Stanza::Error(XMPP::Stanza::Error::Cancel, XMPP::Stanza::Error::BadRequest); + return false; + } + + Application *app = contentList.value(ContentKey { cb.name, cb.creator }); + if (!app || !app->transport() || app->transport()->creator() != role + || app->transport()->state() != State::Pending || transportNS != app->transport()->pad()->ns()) { + // ignore out of order + qInfo("ignore out of order transport-accept"); + continue; + } + updates.append(qMakePair(app, transportEl)); + } + + for (auto &u : updates) { + u.first->incomingTransportAccept(u.second); + // if update fails transport should trigger replace procedure + } + + planStep(); + return true; + } + + bool handleIncomingSessionInfo(const QDomElement &jingleEl) + { + bool hasElements = false; + for (QDomElement child = jingleEl.firstChildElement(); !child.isNull(); + child = child.nextSiblingElement()) { + hasElements = true; + auto pad = q->applicationPad(child.namespaceURI()); + if (pad) { + return pad->incomingSessionInfo(jingleEl); // should return true if supported + } + } + if (!hasElements && state >= State::ApprovedToSend) { + return true; + } + return false; + } + + bool handleIncomingTransportInfo(const QDomElement &jingleEl) + { + QString contentTag(QStringLiteral("content")); + QVector, QDomElement>> updates; + for (QDomElement ce = jingleEl.firstChildElement(contentTag); !ce.isNull(); + ce = ce.nextSiblingElement(contentTag)) { + Application *app = nullptr; + ContentBase cb(ce); + if (!cb.isValid() || !(app = q->content(cb.name, cb.creator)) || app->state() >= State::Finishing + || !app->transport()) { + lastError = XMPP::Stanza::Error(XMPP::Stanza::Error::Cancel, XMPP::Stanza::Error::BadRequest); + return false; + } + auto tel = ce.firstChildElement(QStringLiteral("transport")); + if (tel.isNull() || tel.namespaceURI() != app->transport()->pad()->ns()) { + lastError = XMPP::Stanza::Error(XMPP::Stanza::Error::Cancel, XMPP::Stanza::Error::BadRequest); + return false; + } + updates.append(qMakePair(app->transport(), tel)); + } + + for (auto &u : updates) { + if (!u.first->update(u.second)) { + lastError = u.first->lastError(); + return false; // failure should trigger transport replace + } + } + + return true; + } + }; + + Session::Session(Manager *manager, const Jid &peer, Origin role) : d(new Private) + { + d->q = this; + d->role = role; + d->manager = manager; + d->otherParty = peer; + d->groupingAllowed = checkPeerCaps(QLatin1String("urn:ietf:rfc:5888")); + d->stepTimer.setSingleShot(true); + d->stepTimer.setInterval(0); + connect(&d->stepTimer, &QTimer::timeout, this, [this]() { d->doStep(); }); + connect(manager->client(), &Client::disconnected, this, [this]() { + d->waitingAck = false; + d->terminateReason = Reason(Reason::ConnectivityError, QLatin1String("local side disconnected")); + d->setSessionFinished(); + }); + } + + Session::~Session() + { + qDeleteAll(d->contentList); + qDebug("session %s destroyed", qPrintable(d->sid)); + } + + Manager *Session::manager() const { return d->manager; } + State Session::state() const { return d->state; } + Jid Session::me() const { return d->localParty; } + Jid Session::peer() const { return d->otherParty; } + + Jid Session::initiator() const + { + return d->role == Origin::Initiator ? d->manager->client()->jid() : d->otherParty; + } + + Jid Session::responder() const + { + return d->role == Origin::Responder ? d->manager->client()->jid() : d->otherParty; + } + + QString Session::sid() const { return d->sid; } + + Origin Session::role() const { return d->role; } + + Origin Session::peerRole() const { return negateOrigin(d->role); } + + Features Session::peerFeatures() const { return d->manager->client()->capsManager()->disco(peer()).features(); } + + bool Session::checkPeerCaps(const QString &ns) const + { + return d->manager->client()->capsManager()->disco(peer()).features().test(QStringList() << ns); + } + + bool Session::isGroupingAllowed() const { return d->groupingAllowed; } + + XMPP::Stanza::Error Session::lastError() const { return d->lastError; } + + Application *Session::newContent(const QString &ns, Origin senders) + { + auto pad = applicationPadFactory(ns); + if (pad) { + return pad->manager()->startApplication(pad, pad->generateContentName(senders), d->role, senders); + } + return nullptr; + } + + Application *Session::content(const QString &contentName, Origin creator) + { + return d->contentList.value(ContentKey { contentName, creator }); + } + + void Session::addContent(Application *content) + { + Q_ASSERT(d->state < State::Finishing); + d->addAndInitContent(d->role, content); + if (d->state >= State::ApprovedToSend) { + // If we add content to already initiated session then we are gonna + // send it immediatelly. So start prepare + content->prepare(); + } + } + + const QMap &Session::contentList() const { return d->contentList; } + + void Session::setGrouping(const QString &groupType, const QStringList &group) + { + d->groups.insert(groupType, group); + d->needNotifyGroup = true; + } + + ApplicationManagerPad::Ptr Session::applicationPad(const QString &ns) + { + return d->applicationPads.value(ns).toStrongRef(); + } + + TransportManagerPad::Ptr Session::transportPad(const QString &ns) + { + return d->transportPads.value(ns).toStrongRef(); + } + + QSharedPointer Session::newOutgoingTransport(const QString &ns) + { + auto pad = transportPadFactory(ns); + if (pad) { + return pad->manager()->newTransport(pad, d->role); // pad on both side becaue we need shared pointer + } + return QSharedPointer(); + } + + QString Session::preferredApplication() const + { + // TODO some heuristics to detect preferred application + if (d->applicationPads.size()) { + return d->applicationPads.constBegin().key(); + } + return QString(); + } + + QStringList Session::allApplicationTypes() const { return d->applicationPads.keys(); } + + void Session::setLocalJid(const Jid &jid) { d->localParty = jid; } + + void Session::accept() + { + Q_ASSERT(d->role == Origin::Responder && d->state == State::Created); + // So we presented a user incoming session in UI, the user modified it somehow and finally accepted. + d->state = State::ApprovedToSend; + for (auto &c : d->contentList) { + c->prepare(); + } + d->notifyPads<&SessionManagerPad::onLocalAccepted>(); + d->planStep(); + } + + void Session::initiate() + { + emit initiated(); + if (d->role == Origin::Initiator && d->state == State::Created) { + d->state = State::ApprovedToSend; + for (auto &c : d->contentList) { + c->markInitialApplication(true); + c->prepare(); + } + d->notifyPads<&SessionManagerPad::onLocalAccepted>(); + d->planStep(); + } + } + + void Session::terminate(Reason::Condition cond, const QString &comment) + { + if (d->role == Origin::Initiator && d->state == State::ApprovedToSend) { + d->setSessionFinished(); + return; + } + d->state = State::Finishing; + d->terminateReason = Reason(cond, comment); + d->planStep(); + } + + TransportManagerPad::Ptr Session::transportPadFactory(const QString &ns) + { + auto pad = d->transportPads.value(ns).toStrongRef(); + if (!pad) { + auto deleter = [ns, this](TransportManagerPad *pad) { + d->transportPads.remove(ns); + delete pad; + }; + pad = TransportManagerPad::Ptr(d->manager->transportPad(this, ns), deleter); + if (pad) { + d->transportPads.insert(ns, pad); + } + } + return pad; + } + + ApplicationManagerPad::Ptr Session::applicationPadFactory(const QString &ns) + { + auto pad = d->applicationPads.value(ns).toStrongRef(); + if (!pad) { + auto deleter = [ns, this](ApplicationManagerPad *pad) { + d->applicationPads.remove(ns); + delete pad; + }; + pad = ApplicationManagerPad::Ptr(d->manager->applicationPad(this, ns), deleter); + if (pad) { + d->applicationPads.insert(ns, pad); + } + } + return pad; + } + + bool Session::incomingInitiate(const Jingle &jingle, const QDomElement &jingleEl) + { + d->sid = jingle.sid(); + d->origFrom = d->otherParty; + if (jingle.initiator().isValid() && !jingle.initiator().compare(d->origFrom)) { + d->otherParty = jingle.initiator(); + } + + Private::AddContentError err; + Reason::Condition cond; + QList apps; + QList rejects; + + std::tie(err, cond, apps, rejects) = d->parseContentAddList(jingleEl); + switch (err) { + case Private::AddContentError::Unparsed: + case Private::AddContentError::Unexpected: + return false; + case Private::AddContentError::Unsupported: + d->terminateReason = Reason(cond); + d->planStep(); + return true; + case Private::AddContentError::Ok: + if (!apps.size()) + return false; + d->initialIncomingUnacceptedContent = apps; + for (auto app : std::as_const(apps)) { + app->markInitialApplication(true); + d->addAndInitContent(Origin::Initiator, app); + } + d->planStep(); + return true; + } + + return false; + } + + bool Session::updateFromXml(Action action, const QDomElement &jingleEl) + { + if (d->state == State::Finished) { + d->lastError = XMPP::Stanza::Error(XMPP::Stanza::Error::Cancel, XMPP::Stanza::Error::UnexpectedRequest); + ErrorUtil::fill(jingleEl.ownerDocument(), d->lastError, ErrorUtil::OutOfOrder); + return false; + } + + switch (action) { + case Action::ContentAccept: + return d->handleIncomingContentAccept(jingleEl); + case Action::ContentAdd: + return d->handleIncomingContentAdd(jingleEl); + case Action::ContentModify: + break; + case Action::ContentReject: + break; + case Action::ContentRemove: + return d->handleIncomingContentRemove(jingleEl); + case Action::DescriptionInfo: + break; + case Action::SecurityInfo: + break; + case Action::SessionAccept: + return d->handleIncomingSessionAccept(jingleEl); + case Action::SessionInfo: + return d->handleIncomingSessionInfo(jingleEl); + case Action::SessionInitiate: // impossible case. but let compiler be happy + break; + case Action::SessionTerminate: + return d->handleIncomingSessionTerminate(jingleEl); + case Action::TransportAccept: + return d->handleIncomingTransportAccept(jingleEl); + case Action::TransportInfo: + return d->handleIncomingTransportInfo(jingleEl); + case Action::TransportReject: + break; + case Action::TransportReplace: + return d->handleIncomingTransportReplace(jingleEl); + case Action::NoAction: + break; + } + + d->lastError = XMPP::Stanza::Error(XMPP::Stanza::Error::Cancel, XMPP::Stanza::Error::FeatureNotImplemented); + return false; + } +}} + +#include "jingle-session.moc" diff --git a/src/xmpp/xmpp-im/jingle-session.h b/src/xmpp/xmpp-im/jingle-session.h new file mode 100644 index 00000000..8f60683e --- /dev/null +++ b/src/xmpp/xmpp-im/jingle-session.h @@ -0,0 +1,102 @@ +/* + * jignle-session.h - Jingle Session + * Copyright (C) 2019 Sergey Ilinykh + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library. If not, see . + * + */ + +#ifndef JINGLE_SESSION_H +#define JINGLE_SESSION_H + +#include "jingle-application.h" +#include "jingle-transport.h" +#include "xmpp_features.h" + +namespace XMPP { namespace Jingle { + + // class Manager; + class Application; + + class Session : public QObject { + Q_OBJECT + public: + // Note incoming session are not registered in Jingle Manager until validated. + // and then either rejected or registered in Pending state. + + Session(Manager *manager, const Jid &peer, Origin role = Origin::Initiator); + ~Session(); + + Manager *manager() const; + State state() const; + + Jid me() const; + Jid peer() const; + Jid initiator() const; + Jid responder() const; + QString sid() const; + + Origin role() const; // my role in session: initiator or responder + Origin peerRole() const; + bool checkPeerCaps(const QString &ns) const; + Features peerFeatures() const; + + bool isGroupingAllowed() const; + + XMPP::Stanza::Error lastError() const; + + // make new local content but do not add it to session yet + Application *newContent(const QString &ns, Origin senders = Origin::Both); + // get registered content if any + Application *content(const QString &contentName, Origin creator); + void addContent(Application *content); + const QMap &contentList() const; + void setGrouping(const QString &groupType, const QStringList &group); + + ApplicationManagerPad::Ptr applicationPad(const QString &ns); + TransportManagerPad::Ptr transportPad(const QString &ns); + + QSharedPointer newOutgoingTransport(const QString &ns); + + QString preferredApplication() const; + QStringList allApplicationTypes() const; + + void setLocalJid(const Jid &jid); // w/o real use case the implementation is rather stub + + void accept(); + void initiate(); + void terminate(Reason::Condition cond, const QString &comment = QString()); + + // allocates or returns existing pads + ApplicationManagerPad::Ptr applicationPadFactory(const QString &ns); + TransportManagerPad::Ptr transportPadFactory(const QString &ns); + signals: + void managerPadAdded(const QString &ns); + void initiated(); + void activated(); + void terminated(); + void newContentReceived(); + + private: + friend class Manager; + friend class JTPush; + bool incomingInitiate(const Jingle &jingle, const QDomElement &jingleEl); + bool updateFromXml(Action action, const QDomElement &jingleEl); + + class Private; + std::unique_ptr d; + }; +}} + +#endif // JINGLE_SESSION_H diff --git a/src/xmpp/xmpp-im/jingle-transport.cpp b/src/xmpp/xmpp-im/jingle-transport.cpp new file mode 100644 index 00000000..434ebea6 --- /dev/null +++ b/src/xmpp/xmpp-im/jingle-transport.cpp @@ -0,0 +1,107 @@ +/* + * jignle-transport.cpp - Base Jingle transport classes + * Copyright (C) 2019 Sergey Ilinykh + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library. If not, see . + * + */ + +#include "jingle-transport.h" +#include "jingle-session.h" + +#include + +namespace XMPP { namespace Jingle { + //---------------------------------------------------------------------------- + // TransportManager + //---------------------------------------------------------------------------- + TransportManager::TransportManager(QObject *parent) : QObject(parent) { } + + bool TransportManager::canMakeConnection(TransportFeatures desiredFeatures, const QString &) + { + return (features() & desiredFeatures) == desiredFeatures; + } + QStringList TransportManager::ns() const { return discoFeatures(); } + void TransportManager::closeAll(const QString &) { emit abortAllRequested(); } + + //---------------------------------------------------------------------------- + // Transport + //---------------------------------------------------------------------------- + Transport::Transport(TransportManagerPad::Ptr pad, Origin creator) : _creator(creator), _pad(pad) { } + + bool Transport::isRemote() const { return _pad->session()->role() != _creator; } + + void Transport::stop() { _state = State::Finished; } + + int Transport::maxSupportedComponents() const { return 1; } + + void Transport::setComponentsCount(int) { } + + int Transport::maxSupportedChannelsPerComponent(TransportFeatures) const { return 0; } + + void Transport::setState(State newState) + { + _prevState = _state; + _state = newState; + emit stateChanged(); + } + + bool Transport::wasAccepted() const + { + if (isRemote()) + return _state >= State::ApprovedToSend && _state != State::Pending; + return _state >= State::ApprovedToSend; + } + + void Transport::onFinish(Reason::Condition condition, const QString &message) + { + qDebug("Transport::onFinish: %s", qPrintable(_pad->ns())); + _lastReason = Reason(condition, message); + _prevState = _state; + _state = State::Finished; + QPointer p(this); + if (condition != Reason::Condition::Success && condition != Reason::Condition::NoReason) + emit failed(); + if (p) + emit stateChanged(); + } + + void Transport::addAcceptor(TransportFeatures features, ConnectionAcceptorCallback &&acceptor, int componentIndex) + { + _connectionAcceptors.append({ features, std::move(acceptor), componentIndex }); + } + + const QList &Transport::acceptors() const { return _connectionAcceptors; } + + bool Transport::notifyIncomingConnection(Connection::Ptr connection) const + { + for (auto const &acceptor : _connectionAcceptors) { + if ((connection->features() & acceptor.features) == acceptor.features + && (acceptor.componentIndex < 0 || acceptor.componentIndex == connection->component()) + && acceptor.callback(connection)) + return true; + } + return false; + } + + //---------------------------------------------------------------------------- + // TransportSelector + //---------------------------------------------------------------------------- + TransportSelector::~TransportSelector() { } + + bool TransportSelector::canReplace(QSharedPointer old, QSharedPointer newer) + { + return newer && (hasTransport(newer) || compare(old, newer) == 0); + } +}} diff --git a/src/xmpp/xmpp-im/jingle-transport.h b/src/xmpp/xmpp-im/jingle-transport.h new file mode 100644 index 00000000..84c8b19f --- /dev/null +++ b/src/xmpp/xmpp-im/jingle-transport.h @@ -0,0 +1,260 @@ +/* + * jignle-transport.h - Base Jingle transport classes + * Copyright (C) 2019 Sergey Ilinykh + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library. If not, see . + * + */ + +#ifndef JINGLE_TRANSPORT_H +#define JINGLE_TRANSPORT_H + +#include "jingle-connection.h" +#include "jingle.h" + +namespace XMPP { namespace Jingle { + + class TransportManager; + class TransportManagerPad : public SessionManagerPad { + Q_OBJECT + public: + typedef QSharedPointer Ptr; + + virtual TransportManager *manager() const = 0; + }; + + class Transport : public QObject { + Q_OBJECT + public: + Transport(TransportManagerPad::Ptr pad, Origin creator); + + /*enum Direction { // incoming or outgoing file/data transfer. + Outgoing, + Incoming + };*/ + + inline Origin creator() const { return _creator; } + inline State state() const { return _state; } + inline State prevState() const { return _prevState; } + inline Reason lastReason() const { return _lastReason; } + inline XMPP::Stanza::Error lastError() const { return _lastError; } + inline TransportManagerPad::Ptr pad() const { return _pad; } + bool isRemote() const; + inline bool isLocal() const { return !isRemote(); } + + /** + * @brief prepare to send content-add/session-initiate + * When ready, the application first set update type to ContentAdd and then emit updated() + */ + virtual void prepare() = 0; + + /** + * @brief start really transfer data. starting with connection to remote candidates for example + */ + virtual void start() = 0; // for local transport start searching for candidates (including probing proxy,stun + // etc) for remote transport try to connect to all proposed hosts in order their + // priority. in-band transport may just emit updated() here + virtual void stop(); + virtual bool update(const QDomElement &el) = 0; // accepts transport element on incoming transport-info + virtual bool hasUpdates() const = 0; + + /** + * @brief Get an session update from the transport which looks most appropriate + * @param ensureTransportElement - return minimal transport element even if no updates. + * The parameters is mostly required to satisfy XEP-0166 requirement + * for element in the element + * @return dom element and optional task completion callback + */ + virtual OutgoingTransportInfoUpdate takeOutgoingUpdate(bool ensureTransportElement = false) = 0; + virtual bool isValid() const = 0; + + // returns all the available transport features while addChannel() can use just a subset of them + virtual TransportFeatures features() const = 0; + + // a component is basically is a subconnection usually with a dedicated IP port. A component may have one or + // more channels. Components are pretty much generic but channels aren't. Channels give connection objects, + // while components are rather indexes. + + /** + * @brief maxSupportedComponents + * @return max number of components >=1. + * + * If the function returns -1 then it assumes number of components is unlimited. Even so it may depend on some + * factors like number of still available udp ports for example for udp based transport. + */ + virtual int maxSupportedComponents() const; + + /** + * @brief setComponentsCount set desired amount of components + * + * Note, by default there there is 1 component. So if 1 is enough for an application this function can be + * ignored. The function is not allowed to be called after negotiation has been started. In other words call + * it before `prepare()` call. + */ + virtual void setComponentsCount(int count); + + /** + * @brief maxSupportedChannelsPerComponent returns max number supported channels for specific features set + * @param features - required features + * @return number of channel. 0 if not supported for given features set or non-zero if suported. + * + * Transports MUST reimplement this method, otherwise by default it's 0 (the transport won't work) + * + * Channels transfer some specific data. For example if those are DataOriented channels then for ICE transport + * we can assume max SCTP channels. Again for ICE and time-oriented channels it's 1 since the transport doesn't + * handle any channel protocols in this case and it's up to the application to do any multiplexing. + */ + virtual int maxSupportedChannelsPerComponent(TransportFeatures features) const; + + /** + * @brief addChannel adds a channel to the component. + * @param features - required channel features. like DataOriented for example + * @param id - channel id. can be used for demultiplexed incoming negotiations + * @param component - index of component to add the channel to. If -1 then into the most appropriate. + * @return connection object which eventually will fire `connected()` signal + * + * It's necessary to add components in advance since we always have at least one component. + * For example a file transfer transport may just call `addChannel(TransportFeature::DataOriented)` to have + * reliable connection object on the component 0. Transports not supporting components notation are considered + * to support just one component with index 0 + */ + virtual Connection::Ptr addChannel(TransportFeatures features, const QString &id, int componentIndex = -1) = 0; + + /** + * @brief channels + * @return the list of all added channels both local and remote + */ + virtual QList channels() const = 0; + + /** + * @brief addAcceptor adds a connection acceptor for incoming connections + * @param features minimal required set of features of the connection + * @param acceptor a callback function which returns true is connection was accepted + * @param componentIndex index of component to catch connections on or -1 to catch on all components + * + * It's up to the application what to do with connecteion passed to the callback. If the callback returns true + * it mean application accepted the conenction and likely attached some signals to it and prepared for any use. + */ + void addAcceptor(TransportFeatures features, ConnectionAcceptorCallback &&acceptor, int componentIndex = -1); + + /** + * @brief acceptors returns all registered connection acceptors + * @return list of acceptors + */ + const QList &acceptors() const; + + signals: + /** + * found some candidates and they have to be sent. takeUpdate has to be called from this signal + * handler. if it's just always ready then signal has to be sent at least once otherwise + * session-initiate won't be sent. + */ + void updated(); + void failed(); // transport ailed for whatever reason. aborted for example. _state will be State::Finished + void stateChanged(); + + protected: + /// just updates state and signals about the change. No any loggic attached to the new state + void setState(State newState); + + /// Where the user already gave his consent to transfer data. (one exception: State::Finished) + bool wasAccepted() const; + + /// Called in the end of life to trigger final events + void onFinish(Reason::Condition condition, const QString &message = QString()); + + /** + * @brief notifyIncomingConnection checks all acceptors and return true if any accepted the connection + * @return acceptance status + */ + bool notifyIncomingConnection(Connection::Ptr) const; + + State _state = State::Created; + State _prevState = State::Created; + Origin _creator = Origin::None; + QSharedPointer _pad; + Reason _lastReason; + XMPP::Stanza::Error _lastError; + int _componentsCount = 1; + QList _connectionAcceptors; + }; + + // It's an available transports collection per application + struct TransportSelector { + virtual ~TransportSelector(); + // Allocate the most preferred transport from the set + // Returned transport is removed from the list of available. + virtual QSharedPointer getNextTransport() = 0; + + // Allocate alike transport (e.g. we have remote transport but instead want to use our + // own of the same type and similar parameters) + // Returned transport is removed from the list of available. + virtual QSharedPointer getAlikeTransport(QSharedPointer alike) = 0; + + // Checks if replacement old with newer is possible (e.g. calls canReplace) and removes + // the newer transport from the list of available. + // Returns false if impossible. + virtual bool replace(QSharedPointer old, QSharedPointer newer) = 0; + + // Put transport back to the set for future use + virtual void backupTransport(QSharedPointer) = 0; + + // Where we can allocate another transport for a replacement + virtual bool hasMoreTransports() const = 0; + + // Check where we can (still) use this transport for the application + virtual bool hasTransport(QSharedPointer) const = 0; + + /* + >0: transport `a` is more preferred than `b` + <0: transport `a` is less preferred + =0: it's essentially the same transport, so hardly a replacement. + */ + virtual int compare(QSharedPointer a, QSharedPointer b) const = 0; + + // Returns false if it's impossible to replace old with newer for example if the newer is + // not supported or already proven to be useless. + // Default implementation checks is thew newer transport is among remaining or same as old + virtual bool canReplace(QSharedPointer old, QSharedPointer newer); + }; + + class TransportManager : public QObject { + Q_OBJECT + public: + TransportManager(QObject *parent = nullptr); + + // may show more features than Transport instance. For example some transports may work in both reliable and not + // reliable modes + virtual TransportFeatures features() const = 0; + + /// checks where the transport can make a connection with desired features while serving namespace `ns` + virtual bool canMakeConnection(TransportFeatures desiredFeatures, const QString &ns = QString()); + virtual void setJingleManager(Manager *jm) = 0; + + // FIXME rename methods + virtual QSharedPointer newTransport(const TransportManagerPad::Ptr &pad, Origin creator) = 0; + virtual TransportManagerPad *pad(Session *session) = 0; + + // this method is supposed to gracefully close all related sessions as a preparation for plugin unload for + // example + virtual void closeAll(const QString &ns = QString()); + + virtual QStringList ns() const; + virtual QStringList discoFeatures() const = 0; + signals: + void abortAllRequested(); // mostly used by transport instances to abort immediately + }; +}} + +#endif diff --git a/src/xmpp/xmpp-im/jingle-webrtc-datachannel_p.cpp b/src/xmpp/xmpp-im/jingle-webrtc-datachannel_p.cpp new file mode 100644 index 00000000..501d641e --- /dev/null +++ b/src/xmpp/xmpp-im/jingle-webrtc-datachannel_p.cpp @@ -0,0 +1,184 @@ +/* + * jignle-webrtc-datachannel_p.cpp - WebRTC DataChannel implementation + * Copyright (C) 2021 Sergey Ilinykh + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library. If not, see . + * + */ + +#include "jingle-webrtc-datachannel_p.h" +#include "jingle-sctp-association_p.h" + +#include + +namespace XMPP { namespace Jingle { namespace SCTP { + + WebRTCDataChannel::WebRTCDataChannel(AssociationPrivate *association, quint8 channelType, quint32 reliability, + quint16 priority, const QString &label, const QString &protocol, + DcepState state) : + association(association), channelType(channelType), reliability(reliability), priority(priority), label(label), + protocol(protocol), dcepState(state) + { + } + + QSharedPointer WebRTCDataChannel::fromChannelOpen(AssociationPrivate *assoc, + const QByteArray &data) + { + /* + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Message Type | Channel Type | Priority | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Reliability Parameter | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Label Length | Protocol Length | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + \ / + | Label | + / \ + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + \ / + | Protocol | + / \ + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + */ + if (data.size() < 12) { + qWarning("jingle-sctp: truncated header for WebRTC DataChannel DATA_CHANNEL_OPEN. Dropping.."); + return {}; + } + + quint8 channelType = data[1]; + quint16 priority = qFromBigEndian(data.data() + 2); + quint32 reliability = qFromBigEndian(data.data() + 4); + quint16 labelLength = qFromBigEndian(data.data() + 8); + auto protoOff = (12 + labelLength) + labelLength % 4; + quint16 protocolLength = qFromBigEndian(data.data() + 10); + if (protoOff + protocolLength > data.size()) { + qWarning("jingle-sctp: truncated label or protocol in header for WebRTC DataChannel DATA_CHANNEL_OPEN. " + "Dropping.."); + return {}; + } + QString label = QString::fromUtf8(data.data() + 12, labelLength); + QString protocol = QString::fromUtf8(data.data() + protoOff, protocolLength); + // start with DcepNegotiated since caller will ack asap + auto channel = QSharedPointer::create(assoc, channelType, priority, reliability, label, + protocol, DcepNegotiated); + channel->_isRemote = true; + channel->setOpenMode(QIODevice::ReadWrite); + return channel; + } + + void WebRTCDataChannel::connect() + { + // Q_ASSERT(streamId == -1); + auto utf8Label = label.toUtf8(); + auto utf8Protocol = protocol.toUtf8(); + + auto sz = 12 + utf8Label.size(); + auto protoOff = (sz + 3) & ~3; + sz += (utf8Protocol.size() + 3) & ~3; + QByteArray data(sz, 0); + + data[0] = DCEP_DATA_CHANNEL_OPEN; + data[1] = channelType; + qToBigEndian(priority, data.data() + 2); + qToBigEndian(reliability, data.data() + 4); + qToBigEndian(utf8Label.size(), data.data() + 8); + qToBigEndian(utf8Protocol.size(), data.data() + 10); + data.replace(12, utf8Label.size(), utf8Label); + data.replace(protoOff, utf8Protocol.size(), utf8Protocol); + + dcepState = DcepOpening; + association->write(data, streamId, PPID_DCEP); + } + + void WebRTCDataChannel::setOutgoingCallback(OutgoingCallback &&callback) { outgoingCallback = std::move(callback); } + + bool WebRTCDataChannel::hasPendingDatagrams() const { return datagrams.size() > 0; } + + NetworkDatagram WebRTCDataChannel::readDatagram(qint64 maxSize) + { + Q_UNUSED(maxSize) // TODO or not? + return datagrams.size() ? datagrams.takeFirst() : NetworkDatagram(); + } + + bool WebRTCDataChannel::writeDatagram(const NetworkDatagram &data) + { + Q_ASSERT(bool(outgoingCallback)); + outgoingBufSize += data.data().size(); + outgoingCallback({ quint16(streamId), channelType, PPID_BINARY, reliability, data.data() }); + return true; + } + + qint64 WebRTCDataChannel::bytesAvailable() const { return 0; } + + qint64 WebRTCDataChannel::bytesToWrite() const { return 0; /*client->bytesToWrite();*/ } + + void WebRTCDataChannel::close() { XMPP::Jingle::Connection::close(); } + + TransportFeatures WebRTCDataChannel::features() const + { + // FIXME return proper featuers + return TransportFeature::DataOriented | TransportFeature::Reliable | TransportFeature::Ordered + | TransportFeature::Fast | TransportFeature::MessageOriented; + } + + void WebRTCDataChannel::onConnected() + { + qDebug("jingle-sctp: channel connected!"); + emit connected(); + } + + void WebRTCDataChannel::onError(QAbstractSocket::SocketError error) + { + qDebug("jingle-ice: channel failed: %d", error); + } + + void WebRTCDataChannel::onDisconnected(DisconnectReason reason) + { + if (!(openMode() & QIODevice::WriteOnly)) + return; + streamId = -1; + disconnectReason = reason; + setOpenMode(openMode() & ~QIODevice::WriteOnly); + emit disconnected(); + } + + void WebRTCDataChannel::onIncomingData(const QByteArray &data, quint32 ppid) + { + if (ppid == PPID_DCEP) { + if (dcepState == NoDcep) { + qWarning("jingle-sctp: got dcep on prenegotiated datachannel"); + return; + } + if (data.isEmpty() || data[0] != DCEP_DATA_CHANNEL_ACK || dcepState != DcepOpening) { + qWarning("jingle-sctp: unexpected DCEP. ignoring"); + return; + } + setOpenMode(QIODevice::ReadWrite); + emit connected(); + return; + } + // check other PPIDs. + datagrams.append(NetworkDatagram { data }); + emit readyRead(); + } + + void WebRTCDataChannel::onMessageWritten(size_t size) + { + outgoingBufSize -= size; + emit bytesWritten(size); + } +}}} diff --git a/src/xmpp/xmpp-im/jingle-webrtc-datachannel_p.h b/src/xmpp/xmpp-im/jingle-webrtc-datachannel_p.h new file mode 100644 index 00000000..031d30c7 --- /dev/null +++ b/src/xmpp/xmpp-im/jingle-webrtc-datachannel_p.h @@ -0,0 +1,91 @@ +/* + * jignle-webrtc-datachannel_p.h - WebRTC DataChannel implementation + * Copyright (C) 2021 Sergey Ilinykh + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library. If not, see . + * + */ + +#pragma once + +#include "jingle-connection.h" + +namespace XMPP { namespace Jingle { namespace SCTP { + + enum : quint32 { + PPID_DCEP = 50, + PPID_STRING = 51, + PPID_BINARY = 53, + PPID_STRING_EMPTY = 56, + PPID_BINARY_EMPTY = 57 + }; + enum : quint8 { DCEP_DATA_CHANNEL_ACK = 0x02, DCEP_DATA_CHANNEL_OPEN = 0x03 }; + + class AssociationPrivate; + class WebRTCDataChannel : public XMPP::Jingle::Connection { + Q_OBJECT + public: + enum DisconnectReason { TransportClosed, SctpClosed, ChannelClosed, ChannelReplaced }; + enum DcepState { NoDcep, DcepOpening, DcepNegotiated }; + + struct OutgoingDatagram { + quint16 streamId; + quint8 channelType; + quint8 ppid; + quint32 reliability; + QByteArray data; + }; + + using OutgoingCallback = std::function; + + AssociationPrivate *association; + QList datagrams; + DisconnectReason disconnectReason = ChannelClosed; + std::size_t outgoingBufSize = 0; + OutgoingCallback outgoingCallback; + + quint8 channelType = 0; + quint32 reliability = 0; + quint16 priority = 256; + QString label; + QString protocol; + int streamId = -1; + DcepState dcepState = NoDcep; + + WebRTCDataChannel(AssociationPrivate *association, quint8 channelType = 0, quint32 reliability = 0, + quint16 priority = 256, const QString &label = QString(), const QString &protocol = QString(), + DcepState state = NoDcep); + static QSharedPointer fromChannelOpen(AssociationPrivate *assoc, const QByteArray &data); + + void setStreamId(quint16 id) { streamId = id; } + void connect(); + void setOutgoingCallback(OutgoingCallback &&callback); + + bool hasPendingDatagrams() const override; + NetworkDatagram readDatagram(qint64 maxSize = -1) override; + bool writeDatagram(const NetworkDatagram &data) override; + qint64 bytesAvailable() const override; + qint64 bytesToWrite() const override; + void close() override; + TransportFeatures features() const override; + + public: + void onConnected(); + void onError(QAbstractSocket::SocketError error); + void onDisconnected(DisconnectReason reason); + void onIncomingData(const QByteArray &data, quint32 ppid); + void onMessageWritten(size_t size); + }; + +}}} diff --git a/src/xmpp/xmpp-im/jingle.cpp b/src/xmpp/xmpp-im/jingle.cpp index cefa1059..20ae9c15 100644 --- a/src/xmpp/xmpp-im/jingle.cpp +++ b/src/xmpp/xmpp-im/jingle.cpp @@ -1,4 +1,4 @@ -/* +/* * jignle.cpp - General purpose Jingle * Copyright (C) 2019 Sergey Ilinykh * @@ -18,1351 +18,768 @@ */ #include "jingle.h" -#include "xmpp_xmlcommon.h" -#include "xmpp/jid/jid.h" + +#include "jingle-application.h" +#include "jingle-session.h" #include "xmpp-im/xmpp_hash.h" +#include "xmpp/jid/jid.h" #include "xmpp_client.h" -#include "xmpp_task.h" #include "xmpp_stream.h" +#include "xmpp_task.h" +#include "xmpp_xmlcommon.h" #include +#include #include #include -#include #include #include #include -#include +#if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0) +#include +#endif +#include + +namespace XMPP { namespace Jingle { + const QString NS(QStringLiteral("urn:xmpp:jingle:1")); + const QString ERROR_NS(QStringLiteral("urn:xmpp:jingle:errors:1")); + + //---------------------------------------------------------------------------- + // Jingle + //---------------------------------------------------------------------------- + static const struct { + const char *text; + Action action; + } jingleActions[] + = { { "content-accept", Action::ContentAccept }, { "content-add", Action::ContentAdd }, + { "content-modify", Action::ContentModify }, { "content-reject", Action::ContentReject }, + { "content-remove", Action::ContentRemove }, { "description-info", Action::DescriptionInfo }, + { "security-info", Action::SecurityInfo }, { "session-accept", Action::SessionAccept }, + { "session-info", Action::SessionInfo }, { "session-initiate", Action::SessionInitiate }, + { "session-terminate", Action::SessionTerminate }, { "transport-accept", Action::TransportAccept }, + { "transport-info", Action::TransportInfo }, { "transport-reject", Action::TransportReject }, + { "transport-replace", Action::TransportReplace } }; + + Origin negateOrigin(Origin o) + { + switch (o) { + case Origin::None: + return Origin::Both; + case Origin::Both: + return Origin::None; + case Origin::Initiator: + return Origin::Responder; + case Origin::Responder: + return Origin::Initiator; + } + return Origin::None; + } + + class Jingle::Private : public QSharedData { + public: + Action action; + QString sid; + Jid initiator; + Jid responder; + }; -namespace XMPP { -namespace Jingle { - -const QString NS(QStringLiteral("urn:xmpp:jingle:1")); - - -//---------------------------------------------------------------------------- -// Jingle -//---------------------------------------------------------------------------- -static const struct { - const char *text; - Action action; -} jingleActions[] = { -{ "content-accept", Action::ContentAccept }, -{ "content-add", Action::ContentAdd }, -{ "content-modify", Action::ContentModify }, -{ "content-reject", Action::ContentReject }, -{ "content-remove", Action::ContentRemove }, -{ "description-info", Action::DescriptionInfo }, -{ "security-info", Action::SecurityInfo }, -{ "session-accept", Action::SessionAccept }, -{ "session-info", Action::SessionInfo }, -{ "session-initiate", Action::SessionInitiate }, -{ "session-terminate", Action::SessionTerminate }, -{ "transport-accept", Action::TransportAccept }, -{ "transport-info", Action::TransportInfo }, -{ "transport-reject", Action::TransportReject }, -{ "transport-replace", Action::TransportReplace } -}; - -class Jingle::Private : public QSharedData -{ -public: - Action action; - QString sid; - Jid initiator; - Jid responder; -}; - -Jingle::Jingle() -{ - -} - -Jingle::Jingle(Action action, const QString &sid) : - d(new Private) -{ - d->action = action; - d->sid = sid; -} - -Jingle::Jingle(const QDomElement &e) -{ - QString actionStr = e.attribute(QLatin1String("action")); - Action action; - QString sid = e.attribute(QLatin1String("sid")); - Jid initiator; - Jid responder; - - - bool found = false; - for (unsigned int i = 0; i < sizeof(jingleActions) / sizeof(jingleActions[0]); i++) { - if (actionStr == jingleActions[i].text) { - found = true; - action = jingleActions[i].action; - break; - } - } - if (!found || sid.isEmpty()) { - return; + Jingle::Jingle() { } + + Jingle::Jingle(Action action, const QString &sid) : d(new Private) + { + d->action = action; + d->sid = sid; } - if (!e.attribute(QLatin1String("initiator")).isEmpty()) { - initiator = Jid(e.attribute(QLatin1String("initiator"))); - if (initiator.isNull()) { - qDebug("malformed initiator jid"); - return; + Jingle::Jingle(const QDomElement &e) + { + QString actionStr = e.attribute(QLatin1String("action")); + Action action = Action::NoAction; + QString sid = e.attribute(QLatin1String("sid")); + Jid initiator; + Jid responder; + + for (unsigned int i = 0; i < sizeof(jingleActions) / sizeof(jingleActions[0]); i++) { + if (actionStr == jingleActions[i].text) { + action = jingleActions[i].action; + break; + } } - } - if (!e.attribute(QLatin1String("responder")).isEmpty()) { - responder = Jid(e.attribute(QLatin1String("responder"))); - if (responder.isNull()) { - qDebug("malformed responder jid"); + if (action == Action::NoAction || sid.isEmpty()) { return; } - } - - d = new Private; - d->action = action; - d->sid = sid; - d->responder = responder; -} - -Jingle::Jingle(const Jingle &other) : - d(other.d) -{ -} - -Jingle::~Jingle() -{ - -} + if (!e.attribute(QLatin1String("initiator")).isEmpty()) { + initiator = Jid(e.attribute(QLatin1String("initiator"))); + if (initiator.isNull()) { + qDebug("malformed initiator jid"); + return; + } + } + if (!e.attribute(QLatin1String("responder")).isEmpty()) { + responder = Jid(e.attribute(QLatin1String("responder"))); + if (responder.isNull()) { + qDebug("malformed responder jid"); + return; + } + } -Jingle::Private* Jingle::ensureD() -{ - if (!d) { - d = new Private; + d = new Private; + d->action = action; + d->sid = sid; + d->responder = responder; } - return d.data(); -} -QDomElement Jingle::toXml(QDomDocument *doc) const -{ - if (!d || d->sid.isEmpty() || d->action == Action::NoAction) { - return QDomElement(); - } + Jingle::Jingle(const Jingle &other) : d(other.d) { } - QDomElement query = doc->createElementNS(NS, QLatin1String("jingle")); - //query.setAttribute("xmlns", JINGLE_NS); - for (unsigned int i = 0; i < sizeof(jingleActions) / sizeof(jingleActions[0]); i++) { - if (jingleActions[i].action == d->action) { - query.setAttribute(QLatin1String("action"), QLatin1String(jingleActions[i].text)); - break; + Jingle::~Jingle() { } + + Jingle::Private *Jingle::ensureD() + { + if (!d) { + d = new Private; } + return d.data(); } - if(!d->initiator.isNull()) - query.setAttribute(QLatin1String("initiator"), d->initiator.full()); - if(!d->responder.isNull()) - query.setAttribute(QLatin1String("responder"), d->responder.full()); - query.setAttribute(QLatin1String("sid"), d->sid); - - return query; -} - -Action Jingle::action() const -{ - return d->action; -} - -const QString &Jingle::sid() const -{ - return d->sid; -} - -const Jid &Jingle::initiator() const -{ - return d->initiator; -} - -void Jingle::setInitiator(const Jid &jid) -{ - d->initiator = jid; -} - -const Jid &Jingle::responder() const -{ - return d->responder; -} - -void Jingle::setResponder(const Jid &jid) -{ - d->responder = jid; -} - - -//---------------------------------------------------------------------------- -// Reason -//---------------------------------------------------------------------------- -static const QMap reasonConditions = { - { QStringLiteral("alternative-session"), Reason::AlternativeSession }, - { QStringLiteral("busy"), Reason::Busy }, - { QStringLiteral("cancel"), Reason::Cancel }, - { QStringLiteral("connectivity-error"), Reason::ConnectivityError }, - { QStringLiteral("decline"), Reason::Decline }, - { QStringLiteral("expired"), Reason::Expired }, - { QStringLiteral("failed-application"), Reason::FailedApplication }, - { QStringLiteral("failed-transport"), Reason::FailedTransport }, - { QStringLiteral("general-error"), Reason::GeneralError }, - { QStringLiteral("gone"), Reason::Gone }, - { QStringLiteral("incompatible-parameters"), Reason::IncompatibleParameters }, - { QStringLiteral("media-error"), Reason::MediaError }, - { QStringLiteral("security-error"), Reason::SecurityError }, - { QStringLiteral("success"), Reason::Success }, - { QStringLiteral("timeout"), Reason::Timeout }, - { QStringLiteral("unsupported-applications"), Reason::UnsupportedApplications }, - { QStringLiteral("unsupported-transports"), Reason::UnsupportedTransports }, -}; - -class Reason::Private :public QSharedData { -public: - Reason::Condition cond; - QString text; -}; - -Reason::Reason() -{ - -} - -Reason::~Reason() -{ - -} - -Reason::Reason(Reason::Condition cond, const QString &text) : - d(new Private) -{ - d->cond = cond; - d->text = text; -} - -Reason::Reason(const QDomElement &e) -{ - if(e.tagName() != QLatin1String("reason")) - return; - - Condition condition = NoReason; - QString text; - QString rns = e.attribute(QStringLiteral("xmlns")); - - for (QDomElement c = e.firstChildElement(); !c.isNull(); c = c.nextSiblingElement()) { - if (c.tagName() == QLatin1String("text")) { - text = c.text(); - } - else if (c.attribute(QStringLiteral("xmlns")) != rns) { - // TODO add here all the extensions to reason. - } - else { - condition = reasonConditions.value(c.tagName()); + QDomElement Jingle::toXml(QDomDocument *doc) const + { + if (!d || d->sid.isEmpty() || d->action == Action::NoAction) { + return QDomElement(); } - } - if (condition != NoReason) { - d = new Private; - d->cond = condition; - d->text = text; - } -} - -Reason::Reason(const Reason &other) : - d(other.d) -{ - -} - -Reason::Condition Reason::condition() const -{ - if (d) return d->cond; - return NoReason; -} - -void Reason::setCondition(Condition cond) -{ - ensureD()->cond = cond; -} - -QString Reason::text() const -{ - if (d) return d->text; - return QString(); -} - -void Reason::setText(const QString &text) -{ - ensureD()->text = text; -} - -QDomElement Reason::toXml(QDomDocument *doc) const -{ - if (d && d->cond != NoReason) { - for (auto r = reasonConditions.cbegin(); r != reasonConditions.cend(); ++r) { - if (r.value() == d->cond) { - QDomElement e = doc->createElement(QLatin1String("reason")); - e.appendChild(doc->createElement(r.key())); - if (!d->text.isEmpty()) { - e.appendChild(textTag(doc, QLatin1String("text"), d->text)); - } - return e; + QDomElement query = doc->createElementNS(NS, QLatin1String("jingle")); + for (unsigned int i = 0; i < sizeof(jingleActions) / sizeof(jingleActions[0]); i++) { + if (jingleActions[i].action == d->action) { + query.setAttribute(QLatin1String("action"), QLatin1String(jingleActions[i].text)); + break; } } - } - return QDomElement(); -} -Reason::Private* Reason::ensureD() -{ - if (!d) { - d = new Private; - } - return d.data(); -} - -//---------------------------------------------------------------------------- -// ContentBase -//---------------------------------------------------------------------------- -ContentBase::ContentBase(Origin creator, const QString &name) : - creator(creator), name(name) -{ -} - -ContentBase::ContentBase(const QDomElement &el) -{ - static QMap sendersMap({ - {QStringLiteral("initiator"), Origin::Initiator}, - {QStringLiteral("none"), Origin::Initiator}, - {QStringLiteral("responder"), Origin::Initiator} - }); - creator = creatorAttr(el); - name = el.attribute(QLatin1String("name")); - senders = sendersMap.value(el.attribute(QLatin1String("senders"))); - disposition = el.attribute(QLatin1String("disposition")); // if empty, it's "session" -} - -QDomElement ContentBase::toXml(QDomDocument *doc, const char *tagName) const -{ - if (!isValid()) { - return QDomElement(); - } - auto el = doc->createElement(QLatin1String(tagName)); - setCreatorAttr(el, creator); - el.setAttribute(QLatin1String("name"), name); - - QString sendersStr; - switch (senders) { - case Origin::None: - sendersStr = QLatin1String("none"); - break; - - case Origin::Initiator: - sendersStr = QLatin1String("initiator"); - break; - - case Origin::Responder: - sendersStr = QLatin1String("responder"); - break; - - case Origin::Both: - default: - break; - } + if (!d->initiator.isNull()) + query.setAttribute(QLatin1String("initiator"), d->initiator.full()); + if (!d->responder.isNull()) + query.setAttribute(QLatin1String("responder"), d->responder.full()); + query.setAttribute(QLatin1String("sid"), d->sid); - if (!disposition.isEmpty() && disposition != QLatin1String("session")) { - el.setAttribute(QLatin1String("disposition"), disposition); // NOTE review how we can parse it some generic way - } - if (!sendersStr.isEmpty()) { - el.setAttribute(QLatin1String("senders"), sendersStr); + return query; } - return el; -} + Action Jingle::action() const { return d->action; } + const QString &Jingle::sid() const { return d->sid; } -Origin ContentBase::creatorAttr(const QDomElement &el) -{ - auto creatorStr = el.attribute(QLatin1String("creator")); - if (creatorStr == QLatin1String("initiator")) { - return Origin::Initiator; - } - if (creatorStr == QLatin1String("responder")) { - return Origin::Responder; - } - return Origin::None; -} - -bool ContentBase::setCreatorAttr(QDomElement &el, Origin creator) -{ - if (creator == Origin::Initiator) { - el.setAttribute(QLatin1String("creator"), QLatin1String("initiator")); - } else if (creator == Origin::Responder) { - el.setAttribute(QLatin1String("creator"), QLatin1String("responder")); - } else { - return false; - } - return true; -} - -//---------------------------------------------------------------------------- -// Application -//---------------------------------------------------------------------------- -ApplicationManager::ApplicationManager(QObject *parent) : - QObject(parent) -{ -} - -//---------------------------------------------------------------------------- -// TransportManager -//---------------------------------------------------------------------------- -TransportManager::TransportManager(QObject *parent) : - QObject(parent) -{ - -} - -//---------------------------------------------------------------------------- -// JT - Jingle Task -//---------------------------------------------------------------------------- -class JTPush : public Task -{ - Q_OBJECT -public: - JTPush(Task *parent) : - Task(parent) - { + const Jid &Jingle::initiator() const { return d->initiator; } - } + void Jingle::setInitiator(const Jid &jid) { d->initiator = jid; } + + const Jid &Jingle::responder() const { return d->responder; } + + void Jingle::setResponder(const Jid &jid) { d->responder = jid; } + + //---------------------------------------------------------------------------- + // Reason + //---------------------------------------------------------------------------- + using ReasonMap = QMap; + + Q_GLOBAL_STATIC_WITH_ARGS(ReasonMap, reasonConditions, + ({ + { QLatin1String("alternative-session"), Reason::AlternativeSession }, + { QLatin1String("busy"), Reason::Busy }, + { QLatin1String("cancel"), Reason::Cancel }, + { QLatin1String("connectivity-error"), Reason::ConnectivityError }, + { QLatin1String("decline"), Reason::Decline }, + { QLatin1String("expired"), Reason::Expired }, + { QLatin1String("failed-application"), Reason::FailedApplication }, + { QLatin1String("failed-transport"), Reason::FailedTransport }, + { QLatin1String("general-error"), Reason::GeneralError }, + { QLatin1String("gone"), Reason::Gone }, + { QLatin1String("incompatible-parameters"), Reason::IncompatibleParameters }, + { QLatin1String("media-error"), Reason::MediaError }, + { QLatin1String("security-error"), Reason::SecurityError }, + { QLatin1String("success"), Reason::Success }, + { QLatin1String("timeout"), Reason::Timeout }, + { QLatin1String("unsupported-applications"), Reason::UnsupportedApplications }, + { QLatin1String("unsupported-transports"), Reason::UnsupportedTransports }, + })) + + class Reason::Private : public QSharedData { + public: + Reason::Condition cond; + QString text; + }; + + Reason::Reason() { } - ~JTPush(){} + Reason::~Reason() { } - bool take(const QDomElement &iq) + Reason::Reason(Reason::Condition cond, const QString &text) : d(new Private) { - if (iq.tagName() != QLatin1String("iq") || iq.attribute(QLatin1String("type")) != QLatin1String("set")) { - return false; - } - auto jingleEl = iq.firstChildElement(QStringLiteral("jingle")); - if (jingleEl.isNull() || jingleEl.attribute(QStringLiteral("xmlns")) != ::XMPP::Jingle::NS) { - return false; - } - Jingle jingle(jingleEl); - if (!jingle.isValid()) { - respondError(iq, Stanza::Error::Cancel, Stanza::Error::BadRequest); - return true; - } + d->cond = cond; + d->text = text; + } - QString fromStr(iq.attribute(QStringLiteral("from"))); - Jid from(fromStr); - if (jingle.action() == Action::SessionInitiate) { - if (!client()->jingleManager()->isAllowedParty(from) || - (!jingle.initiator().isEmpty() && !client()->jingleManager()->isAllowedParty(jingle.initiator()))) { - respondError(iq, Stanza::Error::Cancel, Stanza::Error::ServiceUnavailable); - return true; - } + Reason::Reason(const QDomElement &e) + { + if (e.tagName() != QLatin1String("reason")) + return; - Jid redirection(client()->jingleManager()->redirectionJid()); - if (redirection.isValid()) { - respondError(iq, Stanza::Error::Modify, Stanza::Error::Redirect, QStringLiteral("xmpp:")+redirection.full()); - return true; - } + Condition condition = NoReason; + QString text; + QString rns = e.namespaceURI(); - auto session = client()->jingleManager()->session(from, jingle.sid()); - if (session) { - // FIXME what if not yet acknowledged. xep-0166 has a solution for that - respondError(iq, Stanza::Error::Cancel, Stanza::Error::Conflict); - return true; - } - session = client()->jingleManager()->incomingSessionInitiate(from, jingle, jingleEl); - if (!session) { - respondError(iq, client()->jingleManager()->lastError()); - return true; - } - } else { - auto session = client()->jingleManager()->session(from, jingle.sid()); - if (session) { - respondError(iq, Stanza::Error::Cancel, Stanza::Error::Conflict); - return true; - } - if (!session->updateFromXml(jingle.action(), jingleEl)) { - respondError(iq, session->lastError()); - return true; + for (QDomElement c = e.firstChildElement(); !c.isNull(); c = c.nextSiblingElement()) { + if (c.tagName() == QLatin1String("text")) { + text = c.text(); + } else if (c.namespaceURI() != rns) { + // TODO add here all the extensions to reason. + } else { + condition = reasonConditions->value(c.tagName()); } } - auto resp = createIQ(client()->doc(), "result", fromStr, iq.attribute(QStringLiteral("id"))); - client()->send(resp); - return true; + if (condition != NoReason) { + d = new Private; + d->cond = condition; + d->text = text; + } } - void respondError(const QDomElement &iq, Stanza::Error::ErrorType errType, Stanza::Error::ErrorCond errCond, const QString &text = QString()) + Reason::Reason(const Reason &other) : d(other.d) { } + + Reason &Reason::operator=(const Reason &other) { - auto resp = createIQ(client()->doc(), "error", iq.attribute(QStringLiteral("from")), iq.attribute(QStringLiteral("id"))); - Stanza::Error error(errType, errCond, text); - resp.appendChild(error.toXml(*client()->doc(), client()->stream().baseNS())); - client()->send(resp); + d = other.d; + return *this; } - void respondError(const QDomElement &iq, const Stanza::Error &error) + Reason::Condition Reason::condition() const { - auto resp = createIQ(client()->doc(), "error", iq.attribute(QStringLiteral("from")), iq.attribute(QStringLiteral("id"))); - resp.appendChild(error.toXml(*client()->doc(), client()->stream().baseNS())); - client()->send(resp); + if (d) + return d->cond; + return NoReason; } -}; -//---------------------------------------------------------------------------- -// JT - Jingle Task -//---------------------------------------------------------------------------- -class JT : public Task -{ - Q_OBJECT + void Reason::setCondition(Condition cond) { ensureD()->cond = cond; } - QDomElement iq_; - Jid to_; - -public: - JT(Task *parent) : - Task(parent) + QString Reason::text() const { - + if (d) + return d->text; + return QString(); } - ~JT(){} + void Reason::setText(const QString &text) { ensureD()->text = text; } - void request(const Jid &to, const QDomElement &jingleEl) + QDomElement Reason::toXml(QDomDocument *doc) const { - to_ = to; - iq_ = createIQ(doc(), "set", to.full(), id()); - iq_.appendChild(jingleEl); + if (d && d->cond != NoReason) { + for (auto r = reasonConditions->cbegin(); r != reasonConditions->cend(); ++r) { + if (r.value() == d->cond) { + QDomElement e = doc->createElement(QLatin1String("reason")); + e.appendChild(doc->createElement(r.key())); + if (!d->text.isEmpty()) { + e.appendChild(textTag(doc, QLatin1String("text"), d->text)); + } + return e; + } + } + } + return QDomElement(); } - void onGo() + Reason::Private *Reason::ensureD() { - send(iq_); + if (!d) { + d = new Private; + } + return d.data(); } - bool take(const QDomElement &x) - { - if(!iqVerify(x, to_, id())) - return false; + //---------------------------------------------------------------------------- + // ContentBase + //---------------------------------------------------------------------------- + ContentBase::ContentBase(Origin creator, const QString &name) : creator(creator), name(name) { } - if(x.attribute("type") == "error") { - setError(x); - } else { - setSuccess(); - } - return true; + ContentBase::ContentBase(const QDomElement &el) + { + static QMap sendersMap({ { QStringLiteral("initiator"), Origin::Initiator }, + { QStringLiteral("none"), Origin::Both }, + { QStringLiteral("responder"), Origin::Responder } }); + creator = creatorAttr(el); + name = el.attribute(QLatin1String("name")); + senders = sendersMap.value(el.attribute(QLatin1String("senders"))); + disposition = el.attribute(QLatin1String("disposition")); // if empty, it's "session" } -}; - - -//---------------------------------------------------------------------------- -// Session -//---------------------------------------------------------------------------- -class Session::Private -{ -public: - Session *q; - Manager *manager; - QTimer stepTimer; - State state = State::Created; // state of session on our side. if it's incoming we start from Created anyaway but Pending state is skipped - Origin role = Origin::Initiator; // my role in the session - XMPP::Stanza::Error lastError; - Reason terminateReason; - Action outgoingUpdateType = Action::NoAction; - QDomElement outgoingUpdate; - QMap> applicationPads; - QMap> transportPads; - //QMap myContent; // content::creator=(role == Session::Role::Initiator?initiator:responder) - //QMap remoteContent; // content::creator=(role == Session::Role::Responder?initiator:responder) - QMap contentList; - QSet signalingContent; - QString sid; - Jid origFrom; // "from" attr of IQ. - Jid otherParty; // either "from" or initiator/responder. it's where to send all requests. - Jid localParty; // that one will be set as initiator/responder if provided - bool waitingAck = false; - - void sendJingle(Action action, QList update, std::function successCB = std::function()) + + QDomElement ContentBase::toXml(QDomDocument *doc, const QString &tagName, const QString &ns) const { - QDomDocument &doc = *manager->client()->doc(); - Jingle jingle(action, sid); - if (action == Action::SessionInitiate) { - jingle.setInitiator(manager->client()->jid()); + if (!isValid()) { + return QDomElement(); } - if (action == Action::SessionAccept) { - jingle.setResponder(manager->client()->jid()); + auto el = ns.isEmpty() ? doc->createElement(tagName) : doc->createElementNS(ns, tagName); + setCreatorAttr(el, creator); + el.setAttribute(QLatin1String("name"), name); + + QString sendersStr; + switch (senders) { + case Origin::None: + sendersStr = QLatin1String("none"); + break; + + case Origin::Initiator: + sendersStr = QLatin1String("initiator"); + break; + + case Origin::Responder: + sendersStr = QLatin1String("responder"); + break; + + case Origin::Both: + break; } - auto xml = jingle.toXml(&doc); - for (const QDomElement &e: update) { - xml.appendChild(e); + if (!disposition.isEmpty() && disposition != QLatin1String("session")) { + el.setAttribute(QLatin1String("disposition"), + disposition); // NOTE review how we can parse it some generic way + } + if (!sendersStr.isEmpty()) { + el.setAttribute(QLatin1String("senders"), sendersStr); } - auto jt = new JT(manager->client()->rootTask()); - jt->request(otherParty, xml); - QObject::connect(jt, &JT::finished, manager, [jt, jingle, successCB, this](){ - waitingAck = false; - if (jt->success()) { - if (successCB) { - successCB(); - } else { - planStep(); - } - } else { - state = State::Finished; - lastError = jt->error(); - emit q->terminated(); - q->deleteLater(); - } - }); - waitingAck = true; - jt->go(true); + return el; } - void planStep() { - if (waitingAck) { - return; + Origin ContentBase::creatorAttr(const QDomElement &el) + { + auto creatorStr = el.attribute(QLatin1String("creator")); + if (creatorStr == QLatin1String("initiator")) { + return Origin::Initiator; } - lastError = Stanza::Error(0, 0); - if (!stepTimer.isActive()) { - stepTimer.start(); + if (creatorStr == QLatin1String("responder")) { + return Origin::Responder; } + return Origin::None; } - void doStep() { - if (waitingAck) { // we will return here when ack is received. Session::Unacked is possible also only with waitingAck - return; + bool ContentBase::setCreatorAttr(QDomElement &el, Origin creator) + { + if (creator == Origin::Initiator) { + el.setAttribute(QLatin1String("creator"), QLatin1String("initiator")); + } else if (creator == Origin::Responder) { + el.setAttribute(QLatin1String("creator"), QLatin1String("responder")); + } else { + return false; } - if (terminateReason.condition() && state != State::Finished) { - if (state != State::Created || role == Origin::Responder) { - sendJingle(Action::SessionTerminate, QList() << terminateReason.toXml(manager->client()->doc())); + return true; + } + + //---------------------------------------------------------------------------- + // JTPush - Jingle Task + //---------------------------------------------------------------------------- + class JTPush : public Task { + Q_OBJECT + + QList externalManagers; + QList externalSessions; + + public: + JTPush(Task *parent) : Task(parent) { } + + ~JTPush() { } + + inline void addExternalManager(const QString &ns) { externalManagers.append(ns); } + inline void forgetExternalSession(const QString &sid) { externalSessions.removeOne(sid); } + inline void registerExternalSession(const QString &sid) { externalSessions.append(sid); } + + bool take(const QDomElement &iq) + { + if (iq.tagName() != QLatin1String("iq") || iq.attribute(QLatin1String("type")) != QLatin1String("set")) { + return false; } - state = State::Finished; - q->deleteLater(); - emit q->terminated(); - return; - } - if (state == State::Created || state == State::Finished) { - return; // we will start doing something when initiate() is called - } - typedef std::tuple,OutgoingUpdateCB> AckHndl; // will be used from callback on iq ack - if (state == State::PrepareLocalOffer) { // we are going to send session-initiate/accept (already accepted by the user but not sent yet) - /* - * For session-initiate everything is prety much straightforward, just any content with Action::ContentAdd - * update type has to be added. But with session-accept things are more complicated - * 1. Local client could add its content. So we have to check content origin too. - * 2. Remote client could add more content before local session-accept. Then we have two options - * a) send content-accept and skip this content in session-accept later - * b) don't send content-accept and accept everything with session-accept - * We prefer option (b) in our implementation. - */ - Action expectedContentAction = role == Origin::Initiator? Action::ContentAdd : Action::ContentAccept; - for (const auto &c: contentList) { - if (c->creator() != role) { - continue; // we care only about local content for now. + auto jingleEl = iq.firstChildElement(QStringLiteral("jingle")); + if (jingleEl.isNull() || jingleEl.namespaceURI() != ::XMPP::Jingle::NS) { + return false; + } + + Jingle jingle(jingleEl); + if (!jingle.isValid()) { + respondError(iq, Stanza::Error::Cancel, Stanza::Error::BadRequest); + return true; + } + + if (externalManagers.size()) { + if (jingle.action() == Action::SessionInitiate) { + auto cname = QString::fromLatin1("content"); + auto dname = QString::fromLatin1("description"); + for (auto n = jingleEl.firstChildElement(cname); !n.isNull(); n = n.nextSiblingElement(cname)) { + auto del = n.firstChildElement(dname); + if (!del.isNull() && externalManagers.contains(del.namespaceURI())) { + externalSessions.append(jingle.sid()); + return false; + } + } + } else if (externalSessions.contains(jingle.sid())) { + if (jingle.action() == Action::SessionTerminate) { + externalSessions.removeOne(jingle.sid()); + } + return false; } - auto out = c->outgoingUpdateType(); - if (out == Action::ContentReject) { // yeah we are rejecting local content. invalid? - lastError = XMPP::Stanza::Error(XMPP::Stanza::Error::Cancel, XMPP::Stanza::Error::BadRequest); - state = State::Finished; - q->deleteLater(); - emit q->terminated(); - return; + } + + QString fromStr(iq.attribute(QStringLiteral("from"))); + Jid from(fromStr); + if (jingle.action() == Action::SessionInitiate) { + if (!client()->jingleManager()->isAllowedParty(from) + || (!jingle.initiator().isEmpty() + && !client()->jingleManager()->isAllowedParty(jingle.initiator()))) { + respondError(iq, Stanza::Error::Cancel, Stanza::Error::ServiceUnavailable); + return true; } - if (out != expectedContentAction) { - return; // keep waiting. + + Jid redirection(client()->jingleManager()->redirectionJid()); + if (redirection.isValid()) { + respondError(iq, Stanza::Error::Modify, Stanza::Error::Redirect, + QStringLiteral("xmpp:") + redirection.full()); + return true; } - } - Action actionToSend = Action::SessionAccept; - State finalState = State::Active; - // so all contents is ready for session-initiate. let's do it - if (role == Origin::Initiator) { - sid = manager->generateSessionId(otherParty); - actionToSend = Action::SessionInitiate; - finalState = State::Pending; - } - QList contents; - QList acceptApps; - for (const auto &p: contentList) { - if (p->creator() != role) { - continue; // we care only about local content for now. + auto session = client()->jingleManager()->session(from, jingle.sid()); + if (session) { + if (session->role() == Origin::Initiator) { // + respondTieBreak(iq); + } else { + // second session from this peer with the same sid. + respondError(iq, Stanza::Error::Cancel, Stanza::Error::BadRequest); + } + return true; } - QDomElement xml; - OutgoingUpdateCB callback; - std::tie(xml, callback) = p->takeOutgoingUpdate(); - contents.append(xml); - //p->setState(State::Unacked); - if (callback) { - acceptApps.append(AckHndl{p, callback}); + session = client()->jingleManager()->incomingSessionInitiate(from, jingle, jingleEl); + if (!session) { + respondError(iq, client()->jingleManager()->lastError()); + return true; } - } - state = State::Unacked; - sendJingle(actionToSend, contents, [this, acceptApps, finalState](){ - state = finalState; - for (const auto &h: acceptApps) { - auto app = std::get<0>(h); - auto callback = std::get<1>(h); - if (app) { - callback(); + } else { + auto session = client()->jingleManager()->session(from, jingle.sid()); + if (!session) { + if (jingle.action() == Action::SessionTerminate) { + auto resp = createIQ(client()->doc(), "result", fromStr, iq.attribute(QStringLiteral("id"))); + client()->send(resp); + } else { + auto el = client()->doc()->createElementNS(ERROR_NS, QStringLiteral("unknown-session")); + respondError(iq, Stanza::Error::Cancel, Stanza::Error::ItemNotFound, QString(), el); } + return true; } - if (finalState == State::Active) { - emit q->activated(); + if (!session->updateFromXml(jingle.action(), jingleEl)) { + respondError(iq, session->lastError()); + return true; } - planStep(); - }); - - return; - } - - // So session is either in State::Pending or State::Active here. - // State::Connecting status is skipped for session. - QList updateXml; - for (auto mp : applicationPads) { - auto p = mp.toStrongRef(); - QDomElement el = p->takeOutgoingSessionInfoUpdate(); - if (!el.isNull()) { - updateXml.append(el); - // we can send session-info for just one application. so stop processing - sendJingle(Action::SessionInfo, updateXml, [this](){planStep();}); - return; } - } - - QMultiMap updates; - for (auto app : contentList) { - Action updateType = app->outgoingUpdateType(); - if (updateType != Action::NoAction) { - updates.insert(updateType, app); - } + auto resp = createIQ(client()->doc(), "result", fromStr, iq.attribute(QStringLiteral("id"))); + client()->send(resp); + return true; } - QList acceptApps; - if (updates.size()) { - Action action = updates.begin().key(); - auto apps = updates.values(action); - for (auto app: apps) { - QDomElement xml; - OutgoingUpdateCB callback; - std::tie(xml, callback) = app->takeOutgoingUpdate(); - updateXml.append(xml); - if (callback) { - acceptApps.append(AckHndl{app, callback}); - } + void respondError(const QDomElement &iq, Stanza::Error::ErrorType errType, Stanza::Error::ErrorCond errCond, + const QString &text = QString(), const QDomElement &jingleErr = QDomElement()) + { + auto resp = createIQ(client()->doc(), "error", iq.attribute(QStringLiteral("from")), + iq.attribute(QStringLiteral("id"))); + Stanza::Error error(errType, errCond, text); + auto errEl = error.toXml(*client()->doc(), client()->stream().baseNS()); + if (!jingleErr.isNull()) { + errEl.appendChild(jingleErr); } - sendJingle(action, updateXml, [this, acceptApps](){ - for (const auto &h: acceptApps) { - auto app = std::get<0>(h); - auto callback = std::get<1>(h); - if (app) { - callback(); - } - } - planStep(); - }); + resp.appendChild(errEl); + client()->send(resp); } - } - Reason reason(const QDomElement &jingleEl) - { - QDomElement re = jingleEl.firstChildElement(QLatin1String("reason")); - Reason reason; - if(!re.isNull()) { - reason = Reason(re); - if (!reason.isValid()) { - qDebug("invalid reason"); - } + void respondTieBreak(const QDomElement &iq) + { + Stanza::Error error(Stanza::Error::Cancel, Stanza::Error::Conflict); + ErrorUtil::fill(*client()->doc(), error, ErrorUtil::TieBreak); + respondError(iq, error); } - return reason; - } - enum AddContentError { - Ok, - Unparsed, - Unsupported + void respondError(const QDomElement &iq, const Stanza::Error &error) + { + auto resp = createIQ(client()->doc(), "error", iq.attribute(QStringLiteral("from")), + iq.attribute(QStringLiteral("id"))); + resp.appendChild(error.toXml(*client()->doc(), client()->stream().baseNS())); + client()->send(resp); + } }; - std::tuple addContent(const QDomElement &ce) - { - QDomElement descriptionEl = ce.firstChildElement(QLatin1String("description")); - QDomElement transportEl = ce.firstChildElement(QLatin1String("transport")); - QString descriptionNS = descriptionEl.attribute(QStringLiteral("xmlns")); - QString transportNS = transportEl.attribute(QStringLiteral("xmlns")); - typedef std::tuple result; - - ContentBase c(ce); - if (!c.isValid() || descriptionEl.isNull() || transportEl.isNull() || descriptionNS.isEmpty() || transportNS.isEmpty()) { - return result{Unparsed, Reason::Success, nullptr}; - } + //---------------------------------------------------------------------------- + // SessionManagerPad - handle event related to a type of app/transport but not specific instance + //---------------------------------------------------------------------------- + QDomElement SessionManagerPad::takeOutgoingSessionInfoUpdate() { return QDomElement(); } - auto appPad = q->applicationPadFactory(descriptionNS); - if (!appPad) { - return result{Unsupported, Reason::UnsupportedApplications, nullptr}; // condition - } - QScopedPointer app(appPad->manager()->startApplication(appPad, c.name, c.creator, c.senders)); - auto descErr = app->setDescription(descriptionEl); - if (descErr == Application::IncompatibleParameters) { - return result{Unsupported, Reason::IncompatibleParameters, nullptr}; - } else - if (descErr == Application::Unparsed) { - return result{Unparsed, Reason::Success, nullptr}; - } else + void SessionManagerPad::populateOutgoing(Action action, QDomElement &el) + { + Q_UNUSED(action); + Q_UNUSED(el); + } + + void SessionManagerPad::onLocalAccepted() { } + void SessionManagerPad::onSend() { } + + QDomDocument *SessionManagerPad::doc() const { return session()->manager()->client()->doc(); } + + //---------------------------------------------------------------------------- + // Manager + //---------------------------------------------------------------------------- + class Manager::Private { + public: + XMPP::Client *client; + Manager *manager; + std::unique_ptr pushTask; + // ns -> application + std::map> applicationManagers; + // ns -> parser function + std::map> transportManagers; + std::function remoteJidCecker; + + // when set/valid any incoming session initiate will be replied with redirection error + Jid redirectionJid; + XMPP::Stanza::Error lastError; + QHash, Session *> sessions; + int maxSessions = -1; // no limit + + void setupSession(Session *s) { - // same for transport - auto trPad = q->transportPadFactory(transportNS); - if (!trPad) { - return result{Unsupported, Reason::UnsupportedTransports, app.take()}; // condition or we can try fallback and fail with - } - auto transport = trPad->manager()->newTransport(trPad, transportEl); - if (transport) { - if (app->setTransport(transport)) { - return result{Ok, Reason::Success, app.take()}; - } - return result{Unsupported, Reason::UnsupportedTransports, app.take()}; - } + QObject::connect(s, &Session::terminated, manager, + [this, s]() { sessions.remove(qMakePair(s->peer(), s->sid())); }); } + }; - return result{Unparsed, Reason::Success, nullptr}; - } -}; - -Session::Session(Manager *manager, const Jid &peer) : - d(new Private) -{ - d->q = this; - d->manager = manager; - d->otherParty = peer; - d->stepTimer.setSingleShot(true); - d->stepTimer.setInterval(0); - connect(&d->stepTimer, &QTimer::timeout, this, [this](){ d->doStep();}); - -} - -Session::~Session() -{ - qDebug("session %s destroyed", qPrintable(d->sid)); -} - -Manager *Session::manager() const -{ - return d->manager; -} - -State Session::state() const -{ - return d->state; -} - -Jid Session::me() const -{ - return d->manager->client()->jid(); -} - -Jid Session::peer() const -{ - return d->otherParty; -} - -Jid Session::initiator() const -{ - return d->role == Origin::Initiator? d->manager->client()->jid() : d->otherParty; -} - -Jid Session::responder() const -{ - return d->role == Origin::Responder? d->manager->client()->jid() : d->otherParty; -} - -Origin Session::role() const -{ - return d->role; -} - -XMPP::Stanza::Error Session::lastError() const -{ - return d->lastError; -} - -Application *Session::newContent(const QString &ns, Origin senders) -{ - auto pad = applicationPadFactory(ns); - if (pad) { - return pad->manager()->startApplication(pad, pad->generateContentName(senders), d->role, senders); - } - return nullptr; -} - -Application *Session::content(const QString &contentName, Origin creator) -{ - return d->contentList.value(ContentKey{contentName, creator}); -} - -void Session::addContent(Application *content) -{ - d->contentList.insert(ContentKey{content->contentName(), d->role}, content); - if (d->state != State::Created && content->outgoingUpdateType() != Action::NoAction) { - d->signalingContent.insert(content); - } - connect(content, &Application::updated, this, [this](){ - d->signalingContent.insert(static_cast(sender())); - if (!d->waitingAck && !d->stepTimer.isActive()) { - d->stepTimer.start(); + Manager::Manager(Client *client) : QObject(client), d(new Private()) + { + d->client = client; + d->manager = this; + d->pushTask.reset(new JTPush(client->rootTask())); + /* + static bool mtReg = false; + if (!mtReg) { + qRegisterMetaType(); } - }); -} - -const QMap &Session::contentList() const -{ - return d->contentList; -} - -ApplicationManagerPad::Ptr Session::applicationPad(const QString &ns) -{ - return d->applicationPads.value(ns).toStrongRef(); -} - -TransportManagerPad::Ptr Session::transportPad(const QString &ns) -{ - return d->transportPads.value(ns).toStrongRef(); -} - -QSharedPointer Session::newOutgoingTransport(const QString &ns) -{ - auto pad = transportPadFactory(ns); - if (pad) { - return pad->manager()->newTransport(pad); // pad on both side becaue we need shared pointer + */ } - return QSharedPointer(); -} - -QString Session::preferredApplication() const -{ - // TODO some heuristics to detect preferred application - return d->applicationPads.keys().value(0); -} - -QStringList Session::allApplicationTypes() const -{ - return d->applicationPads.keys(); -} - -void Session::setLocalJid(const Jid &jid) -{ - d->localParty = jid; -} - -void Session::accept() -{ - // So we presented a user incoming session in UI, the user modified it somehow and finally accepted. - if (d->role == Origin::Responder && d->state == State::Created) { - d->state = State::PrepareLocalOffer; - for (auto &c: d->contentList) { - c->prepare(); + + Manager::~Manager() + { + for (auto &m : d->transportManagers) { + m.second->setJingleManager(nullptr); } - d->planStep(); - } -} - -void Session::initiate() -{ - if (d->role == Origin::Initiator && d->state == State::Created) { - d->state = State::PrepareLocalOffer; - for (auto &c: d->contentList) { - c->prepare(); + for (auto &m : d->applicationManagers) { + m.second->setJingleManager(nullptr); } - d->planStep(); } -} - -void Session::terminate(Reason::Condition cond, const QString &comment) -{ - d->terminateReason = Reason(cond, comment); -} - -TransportManagerPad::Ptr Session::transportPadFactory(const QString &ns) -{ - auto pad = d->transportPads.value(ns).toStrongRef(); - if (!pad) { - auto deleter = [ns, this](TransportManagerPad *pad){ - d->transportPads.remove(ns); - delete pad; - }; - pad = TransportManagerPad::Ptr(d->manager->transportPad(this, ns), deleter); - if (pad) { - d->transportPads.insert(ns, pad); - } + + Client *Manager::client() const { return d->client; } + + void Manager::addExternalManager(const QString &ns) { d->pushTask->addExternalManager(ns); } + + void Manager::registerExternalSession(const QString &sid) { d->pushTask->registerExternalSession(sid); } + + void Manager::forgetExternalSession(const QString &sid) { d->pushTask->forgetExternalSession(sid); } + + void Manager::setRedirection(const Jid &to) { d->redirectionJid = to; } + + const Jid &Manager::redirectionJid() const { return d->redirectionJid; } + + void Manager::registerApplication(ApplicationManager *app) + { + auto const &nss = app->ns(); + for (auto const &ns : nss) + d->applicationManagers.emplace(ns, app); + app->setJingleManager(this); } - return pad; -} - -ApplicationManagerPad::Ptr Session::applicationPadFactory(const QString &ns) -{ - auto pad = d->applicationPads.value(ns).toStrongRef(); - if (!pad) { - auto deleter = [ns, this](ApplicationManagerPad *pad){ - d->applicationPads.remove(ns); - delete pad; - }; - pad = ApplicationManagerPad::Ptr(d->manager->applicationPad(this, ns), deleter); - if (pad) { - d->applicationPads.insert(ns, pad); + + void Manager::unregisterApp(const QString &ns) + { + auto node = d->applicationManagers.extract(ns); + if (node) { + node.mapped()->closeAll(ns); } } - return pad; -} - -bool Session::incomingInitiate(const Jingle &jingle, const QDomElement &jingleEl) -{ - d->sid = jingle.sid(); - d->origFrom = d->otherParty; - if (jingle.initiator().isValid() && !jingle.initiator().compare(d->origFrom)) { - d->otherParty = jingle.initiator(); - } - //auto key = qMakePair(from, jingle.sid()); - QMap> addSet; // application to supported + bool Manager::isRegisteredApplication(const QString &ns) { return d->applicationManagers.count(ns); } - QString contentTag(QStringLiteral("content")); - for(QDomElement ce = jingleEl.firstChildElement(contentTag); - !ce.isNull(); ce = ce.nextSiblingElement(contentTag)) { - Private::AddContentError err; - Reason::Condition cond; - Application *app; - auto r = d->addContent(ce); - std::tie(err, cond, app) = r; - if (err == Private::AddContentError::Unparsed) { - for (auto const &i: addSet) { - delete std::get<1>(i); - } - d->lastError = XMPP::Stanza::Error(XMPP::Stanza::Error::Cancel, XMPP::Stanza::Error::BadRequest); - return false; + ApplicationManagerPad *Manager::applicationPad(Session *session, const QString &ns) + { + auto it = d->applicationManagers.find(ns); + if (it == d->applicationManagers.end()) { + return nullptr; } - if (err == Private::AddContentError::Unsupported) { - if (cond == Reason::Condition::UnsupportedTransports) { + return it->second->pad(session); + } - } - // can't continue as well - for (auto const &i: addSet) { - delete std::get<1>(i); - } - d->outgoingUpdateType = Action::SessionTerminate; - Reason r(cond); - d->outgoingUpdate = r.toXml(d->manager->client()->doc()); - return true; - } - QString contentName = ce.attribute(QStringLiteral("name")); - if (err == Private::AddContentError::Ok) { - auto et = addSet.value(contentName); - auto eapp = std::get<1>(et); - if (!eapp || eapp->wantBetterTransport(app->transport())) { - addSet.insert(contentName, std::make_tuple(cond,app)); - } - } else if (!std::get<1>(addSet.value(contentName))) { - // something unsupported. but lets parse all items. maybe it will get replaced - addSet.insert(contentName, std::make_tuple(cond,app)); + void Manager::registerTransport(TransportManager *transport) + { + auto const &nss = transport->ns(); + for (auto const &ns : nss) + d->transportManagers.emplace(ns, transport); + transport->setJingleManager(this); + } + + void Manager::unregisterTransport(const QString &ns) + { + auto trManager = d->transportManagers.extract(ns); + if (trManager) { + trManager.mapped()->closeAll(ns); } + } - // TODO at this point if all addSet items have application it's success, - // otherwise we have to think what to do with this. for example replace transport if it's unsupported. + bool Manager::isRegisteredTransport(const QString &ns) { return d->transportManagers.count(ns); } - for (auto const &i: addSet) { - Reason::Condition cond; - Application *app; - std::tie(cond, app) = i; - if (!app) { - // TODO - return false; // FIXME. memory release - } - d->contentList.insert(ContentKey{app->contentName(), Origin::Initiator}, app); + bool Manager::isAllowedParty(const Jid &jid) const + { + if (d->remoteJidCecker) { + return d->remoteJidCecker(jid); } + // REVIEW probably we can check Client's internal roster when checker is not set. + return true; } - d->planStep(); - return true; -} - -bool Session::updateFromXml(Action action, const QDomElement &jingleEl) -{ - if (action == Action::SessionInfo) { + Session *Manager::session(const Jid &remoteJid, const QString &sid) + { + return d->sessions.value(qMakePair(remoteJid, sid)); } - if (action != Action::ContentAdd) { - return false; + void Manager::detachSession(Session *s) + { + s->disconnect(this); + d->sessions.remove(qMakePair(s->peer(), s->sid())); } - QMap addSet; // application to supported - bool parsed = true; - int unsupported = 0; - Reason::Condition rejectCond = Reason::Condition::Success; + void Manager::setRemoteJidChecker(std::function checker) { d->remoteJidCecker = checker; } - QString contentTag(QStringLiteral("content")); - for(QDomElement ce = jingleEl.firstChildElement(contentTag); - !ce.isNull(); ce = ce.nextSiblingElement(contentTag)) { - Private::AddContentError err; - Reason::Condition cond; - Application *app; - QString contentName = ce.attribute(QStringLiteral("name")); - if (!contentName.size()) { - parsed = false; - break; + TransportManagerPad *Manager::transportPad(Session *session, const QString &ns) + { + auto transportManager = d->transportManagers.find(ns); + if (transportManager == d->transportManagers.end()) { + return nullptr; } + return transportManager->second->pad(session); + } - std::tie(err, cond, app) = d->addContent(ce); - bool alreadyAdded = addSet.contains(contentName); - if (err != Private::AddContentError::Ok) { - // can't continue as well - if (app) { // we are going to reject it completely so delete - delete app; - } - if (err == Private::AddContentError::Unsupported) { - rejectCond = cond; - } - if (!alreadyAdded) { - unsupported++; - addSet.insert(contentName, nullptr); - } // else just ignore this unsupported content. we aready have one - continue; + QStringList Manager::availableTransports(const TransportFeatures &features) const + { + std::vector> prio; + prio.reserve(d->transportManagers.size()); + for (auto const &[ns, m] : d->transportManagers) { + if (m->canMakeConnection(features, ns)) + prio.emplace_back(m->features(), ns); } + std::sort(prio.begin(), prio.end(), [](auto const &a, auto const &b) { return a.first < b.first; }); + QStringList nss; + std::transform(prio.begin(), prio.end(), std::back_inserter(nss), [](auto const &p) { return p.second; }); + return nss; + // sorting by features is totally unreliable, so we have TransportSelector to do better job + } - auto eapp = addSet.value(contentName); - if (alreadyAdded && !eapp) { - unsupported--; // we are going to overwrite previous with successful + QStringList Manager::discoFeatures() const + { + QStringList ret; + for (auto const &mgr : d->applicationManagers) { + ret += mgr.second->discoFeatures(); } - if (!eapp || eapp->wantBetterTransport(app->transport())) { - addSet.insert(contentName, app); + for (auto const &mgr : d->transportManagers) { + ret += mgr.second->discoFeatures(); } + return ret; } - if (unsupported && rejectCond == Reason::Condition::Success) { - parsed = false; // the only way it's possible - } - if (!parsed) { - d->lastError = XMPP::Stanza::Error(XMPP::Stanza::Error::Cancel, XMPP::Stanza::Error::BadRequest); - qDeleteAll(addSet); - return false; - } else if (unsupported) { - d->outgoingUpdateType = Action::ContentReject; - Reason r(rejectCond); - d->outgoingUpdate = r.toXml(d->manager->client()->doc()); - qDeleteAll(addSet); - return true; + Session *Manager::incomingSessionInitiate(const Jid &from, const Jingle &jingle, const QDomElement &jingleEl) + { + if (d->maxSessions > 0 && d->sessions.size() == d->maxSessions) { + d->lastError = XMPP::Stanza::Error(XMPP::Stanza::Error::Wait, XMPP::Stanza::Error::ResourceConstraint); + return nullptr; + } + auto key = qMakePair(from, jingle.sid()); + auto s = new Session(this, from, Origin::Responder); + if (s->incomingInitiate(jingle, jingleEl)) { // if parsed well + d->sessions.insert(key, s); + d->setupSession(s); + // emit incomingSession makes sense when there are no unsolved conflicts in content descriptions / + // transports + // QTimer::singleShot(0,[s, this](){ emit incomingSession(s); }); + // QMetaObject::invokeMethod(this, "incomingSession", Qt::QueuedConnection, Q_ARG(Session *, s)); + QTimer::singleShot(0, this, [s, this]() { emit incomingSession(s); }); + return s; + } + d->lastError = s->lastError(); + delete s; + return nullptr; + } + + XMPP::Stanza::Error Manager::lastError() const { return d->lastError; } + + Session *Manager::newSession(const Jid &j) + { + auto s = new Session(this, j); + d->setupSession(s); + return s; } - Origin remoteRole = negateOrigin(d->role); - for (auto const &app: addSet) { - d->contentList.insert(ContentKey{app->contentName(), remoteRole}, app); // TODO check conflicts + QString Manager::registerSession(Session *session) + { + QString id; + auto peer = session->peer(); + do { +#if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0) + id = QString("%1").arg(QRandomGenerator::global()->generate(), 6, 32, QChar('0')); +#else + id = QString("%1").arg(quint32(qrand()), 6, 32, QChar('0')); +#endif + } while (d->sessions.contains(qMakePair(peer, id))); + d->sessions.insert(qMakePair(peer, id), session); + return id; + } + + //---------------------------------------------------------------------------- + // ErrorUtil + //---------------------------------------------------------------------------- + const char *ErrorUtil::names[ErrorUtil::Last] + = { "out-of-order", "tie-break", "unknown-session", "unsupported-info" }; + + Stanza::Error ErrorUtil::make(QDomDocument &doc, int jingleCond, int type, int condition, const QString &text) + { + auto el = doc.createElementNS(ERROR_NS, QString::fromLatin1(names[jingleCond - 1])); + return Stanza::Error(type, condition, text, el); } - return true; -} - - -//---------------------------------------------------------------------------- -// SessionManagerPad - handle event related to a type of app/transport but not specific instance -//---------------------------------------------------------------------------- -QDomElement SessionManagerPad::takeOutgoingSessionInfoUpdate() -{ - return QDomElement(); -} - -//---------------------------------------------------------------------------- -// Manager -//---------------------------------------------------------------------------- -class Manager::Private -{ -public: - XMPP::Client *client; - Manager *manager; - QScopedPointer pushTask; - // ns -> application - QMap> applicationManagers; - // ns -> parser function - QMap> transportManagers; - std::function remoteJidCecker; - - // when set/valid any incoming session initiate will be replied with redirection error - Jid redirectionJid; - XMPP::Stanza::Error lastError; - QHash,Session*> sessions; - int maxSessions = -1; // no limit -}; - -Manager::Manager(Client *client) : - d(new Private()) -{ - d->client = client; - d->manager = this; - d->pushTask.reset(new JTPush(client->rootTask())); -} - -Manager::~Manager() -{ -} - -Client *Manager::client() const -{ - return d->client; -} - -void Manager::setRedirection(const Jid &to) -{ - d->redirectionJid = to; -} - -const Jid &Manager::redirectionJid() const -{ - return d->redirectionJid; -} - -void Manager::registerApp(const QString &ns, ApplicationManager *app) -{ - d->applicationManagers.insert(ns, app); - app->setJingleManager(this); -} - -void Manager::unregisterApp(const QString &ns) -{ - auto appManager = d->applicationManagers.value(ns); - if (appManager) { - appManager->closeAll(); - d->applicationManagers.remove(ns); - } -} - -bool Manager::isRegisteredApplication(const QString &ns) -{ - return d->applicationManagers.contains(ns); -} - -ApplicationManagerPad *Manager::applicationPad(Session *session, const QString &ns) -{ - auto am = d->applicationManagers.value(ns); - if (!am) { - return NULL; - } - return am->pad(session); -} - -void Manager::registerTransport(const QString &ns, TransportManager *transport) -{ - d->transportManagers.insert(ns, transport); - transport->setJingleManager(this); -} - -void Manager::unregisterTransport(const QString &ns) -{ - auto trManager = d->transportManagers.value(ns); - if (trManager) { - trManager->closeAll(); - d->transportManagers.remove(ns); + void ErrorUtil::fill(QDomDocument doc, Stanza::Error &error, int jingleCond) + { + error.appSpec = doc.createElementNS(ERROR_NS, QString::fromLatin1(names[jingleCond - 1])); } -} -bool Manager::isRegisteredTransport(const QString &ns) -{ - return d->transportManagers.contains(ns); -} - -bool Manager::isAllowedParty(const Jid &jid) const -{ - if (d->remoteJidCecker) { - return d->remoteJidCecker(jid); - } - // REVIEW probably we can check Client's internal roster when checker is not set. - return true; -} - -Session *Manager::session(const Jid &remoteJid, const QString &sid) -{ - return d->sessions.value(qMakePair(remoteJid, sid)); -} - -void Manager::setRemoteJidChecker(std::function checker) -{ - d->remoteJidCecker = checker; -} - -TransportManagerPad* Manager::transportPad(Session *session, const QString &ns) -{ - auto transportManager = d->transportManagers.value(ns); - if (!transportManager) { - return NULL; - } - return transportManager->pad(session); -} - -QStringList Manager::availableTransports(const Transport::Features &features) const -{ - QStringList ret; - for (auto it = d->transportManagers.cbegin(); it != d->transportManagers.cend(); ++it) { - if (((*it)->features() & features) == features) { - ret.append(it.key()); + int ErrorUtil::jingleCondition(const Stanza::Error &error) + { + if (error.appSpec.namespaceURI() != ERROR_NS) { + return UnknownError; } + QString tagName = error.appSpec.tagName(); + for (int i = 0; i < int(sizeof(names) / sizeof(names[0])); ++i) { + if (tagName == names[i]) { + return i + 1; + } + } + return UnknownError; } - return ret; -} - -Session* Manager::incomingSessionInitiate(const Jid &from, const Jingle &jingle, const QDomElement &jingleEl) -{ - if (d->maxSessions > 0 && d->sessions.size() == d->maxSessions) { - d->lastError = XMPP::Stanza::Error(XMPP::Stanza::Error::Wait, XMPP::Stanza::Error::ResourceConstraint); - return NULL; - } - auto key = qMakePair(from, jingle.sid()); - auto s = new Session(this, from); - if (s->incomingInitiate(jingle, jingleEl)) { // if parsed well - d->sessions.insert(key, s); - // emit incomingSession makes sense when there are no unsolved conflicts in content descriptions / transports - //QTimer::singleShot(0,[s, this](){ emit incomingSession(s); }); - QMetaObject::invokeMethod(this, "incomingSession", Qt::QueuedConnection, Q_ARG(Session*, s)); - return s; - } - d->lastError = s->lastError(); - delete s; - return NULL; -} - -XMPP::Stanza::Error Manager::lastError() const -{ - return d->lastError; -} - -Session *Manager::newSession(const Jid &j) -{ - return new Session(this, j); -} - -QString Manager::generateSessionId(const Jid &peer) -{ - QString id; - do { - id = QString("%1").arg(quint32(qrand()), 6, 32, QChar('0')); - } while (d->sessions.contains(QPair(peer,id))); - return id; -} - -Origin negateOrigin(Origin o) -{ - switch (o) { - case Origin::None: return Origin::Both; - case Origin::Both: return Origin::None; - case Origin::Initiator: return Origin::Responder; - case Origin::Responder: return Origin::Initiator; + + Stanza::Error ErrorUtil::makeTieBreak(QDomDocument &doc) + { + return make(doc, TieBreak, XMPP::Stanza::Error::Cancel, XMPP::Stanza::Error::Conflict); } - return Origin::None; -} + Stanza::Error ErrorUtil::makeOutOfOrder(QDomDocument &doc) + { + return make(doc, OutOfOrder, XMPP::Stanza::Error::Cancel, XMPP::Stanza::Error::UnexpectedRequest); + } } // namespace Jingle } // namespace XMPP diff --git a/src/xmpp/xmpp-im/jingle.h b/src/xmpp/xmpp-im/jingle.h index 43a294d3..6ea71c1b 100644 --- a/src/xmpp/xmpp-im/jingle.h +++ b/src/xmpp/xmpp-im/jingle.h @@ -20,220 +20,117 @@ #ifndef JINGLE_H #define JINGLE_H -#include "xmpp_hash.h" +#include "iris/xmpp_stanza.h" +#include #include #include + #include -class QDomElement; class QDomDocument; +class QDomElement; namespace XMPP { class Client; +class Task; namespace Jingle { + extern const QString NS; -extern const QString NS; - -class Manager; -class Session; - -enum class Origin { - None, - Both, - Initiator, - Responder -}; - -inline uint qHash(const XMPP::Jingle::Origin &o, uint seed = 0) -{ - return ::qHash(int(o), seed); -} - -enum class State { - Created, // just after constructor - PrepareLocalOffer, // content accepted by local user but we are not ready yet to send content-accept or session-accept. - // same for content-add/session-initiate, where user already already sent/added in ui and it's network turn. - Unacked, // local conten offer is sent to remote but no IQ ack yet - Pending, // waits for session-accept or content-accept from remote - Connecting, // s5b/ice probes etc (particular application state. can be omited for other entities) - Active, // active transfer. transport is connected - Finished // transfering is finished for whatever reason -}; - -enum class Action { - NoAction, // non-standard, just a default - ContentAccept, - ContentAdd, - ContentModify, - ContentReject, - ContentRemove, - DescriptionInfo, - SecurityInfo, - SessionAccept, - SessionInfo, - SessionInitiate, - SessionTerminate, - TransportAccept, - TransportInfo, - TransportReject, - TransportReplace -}; - -typedef QPair ContentKey; -typedef std::function OutgoingUpdateCB; -typedef std::tuple OutgoingUpdate; - -class Jingle -{ -public: - Jingle(); // make invalid jingle element - Jingle(Action action, const QString &sid); // start making outgoing jingle - Jingle(const QDomElement &e); // likely incoming - Jingle(const Jingle &); - ~Jingle(); - - QDomElement toXml(QDomDocument *doc) const; - inline bool isValid() const { return d != nullptr; } - Action action() const; - const QString &sid() const; - const Jid &initiator() const; - void setInitiator(const Jid &jid); - const Jid &responder() const; - void setResponder(const Jid &jid); -private: - class Private; - QSharedDataPointer d; - Jingle::Private *ensureD(); -}; - -class Reason { - class Private; -public: - enum Condition - { - NoReason = 0, // non-standard, just a default - AlternativeSession, - Busy, - Cancel, - ConnectivityError, - Decline, - Expired, - FailedApplication, - FailedTransport, - GeneralError, - Gone, - IncompatibleParameters, - MediaError, - SecurityError, - Success, - Timeout, - UnsupportedApplications, - UnsupportedTransports - }; - - Reason(); - ~Reason(); - Reason(Condition cond, const QString &text = QString()); - Reason(const QDomElement &el); - Reason(const Reason &other); - inline bool isValid() const { return d != nullptr; } - Condition condition() const; - void setCondition(Condition cond); - QString text() const; - void setText(const QString &text); - - QDomElement toXml(QDomDocument *doc) const; - -private: - Private *ensureD(); - - QSharedDataPointer d; -}; - -class ContentBase { -public: - - inline ContentBase(){} - ContentBase(Origin creator, const QString &name); - ContentBase(const QDomElement &el); - - inline bool isValid() const { return creator != Origin::None && !name.isEmpty(); } - - QDomElement toXml(QDomDocument *doc, const char *tagName) const; - static Origin creatorAttr(const QDomElement &el); - static bool setCreatorAttr(QDomElement &el, Origin creator); + class Manager; + class Session; - Origin creator = Origin::None; - QString name; - Origin senders = Origin::Both; - QString disposition; // default "session" -}; + enum class Origin { None, Both, Initiator, Responder }; -class Security -{ - -}; - -/** - * @brief The SessionManagerPad class - TransportManager/AppManager PAD - * - * The class is intended to be used to monitor global session events - * as well as send them in context of specific application type. - * - * For example a session has 3 content elements (voice, video and whiteboard). - * voice and video are related to RTP application while whiteaboard (Jingle SXE) - * is a different application. Therefore the session will have 2 pads: - * rtp pad and whitebaord pad. - * The pads are connected to both session and transport/application manager - * and their main task to handle Jingle session-info events. - * - * SessionManagerPad is a base class for all kinds of pads. - * UI can connect to its signals. - */ -class SessionManagerPad : public QObject -{ - Q_OBJECT -public: - virtual QDomElement takeOutgoingSessionInfoUpdate(); - virtual QString ns() const = 0; - virtual Session *session() const = 0; -}; - -class TransportManager; -class TransportManagerPad : public SessionManagerPad -{ - Q_OBJECT -public: - typedef QSharedPointer Ptr; - - virtual TransportManager *manager() const = 0; -}; - -class ApplicationManager; -class ApplicationManagerPad : public SessionManagerPad -{ - Q_OBJECT -public: - typedef QSharedPointer Ptr; - - using SessionManagerPad::SessionManagerPad; - - virtual ApplicationManager *manager() const = 0; +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) + inline uint qHash(const XMPP::Jingle::Origin &o, uint seed = 0) { return ::qHash(int(o), seed); } +#else + inline size_t qHash(const XMPP::Jingle::Origin &o, size_t seed = 0) { return ::qHash(int(o), seed); } +#endif /* - * for example we transfer a file - * then first file may generate name "file1", next "file2" etc - * As result it will be sent as - */ - virtual QString generateContentName(Origin senders) = 0; -}; + Session states: + * Created - new session + * ApprovedToSend - user accepted session but it's not yet ready for session-initiate/accept message + * Unacked - session-initiate/accept was sent. wait for IQ ack + * Pending - local only: session-initiate was acknowledged. awaits session-accept. + * Active - session was accepted and now active. + * Finihed - session-terminate was sent/received + + Local app states: + * Created - after constructor till local user initiates the sending + * ApprovedToSend - user already clicked "send" but our offer is not ready yet (collecting candidates) + * Unacked - initial offer is sent but no iq result yet + * Pending - got iq result but no accept (answer) request yet + * Accepted - remote accepted the app. waiting for start() (for example when all session is accepted) + * Connecting - session was accepted (or content-accepted for early-session). negotiating connection + * Active - connection was established. now real data passes. + * Finishing - need to send some final statuses over signalling + * Finished - app was removed from session + + Remote app states: + * Created - after constructor till local user accepts the app + * ApprovedToSend - user already accepted but our answer is not ready yet (collecting candidates) + * Unacked - the answer is sent but no iq result yet + * Accepted - waiting for start() (for example when all session is accepted) + * Connecting - session was accepted (or content-accepted for early-session). negotiating connection + * Active - connection was established. now real data passes. + * Finishing - need to send some final statuses over signalling + * Finished - app was removed from session + + Local transport states (our initial offer or our outgoing transport-replace): + * Created - initial: just after constructor but before "send" button was pushed. + * replace: if previous was > Created then replace will start right from ApprovedToSend + * ApprovedToSend - initial: we are preparing initial offer ("send" was pushed already) + * replace: we are going to replace previously sent trasport offer. preparing new one + * Unacked - no iq "result" yet + * Pending - got iq result but no any kind of transport accept + * Accepted - session/content/transport-replace accepted. app should start negotiation explicitly + * Connecting - connetion negotiation + * Active - traferring data + * Finished - In failure case: Needs to report transport failure / replace / reject + + Remote transport states (remote initial offer or remote transport-replace): + * Created - short-term state before the initial offer has been parsed + * Pending - initial: local user hasn't accepted yet the offer + * replace: remote changes its own offer before local accepted anything + * ApprovedToSend - initial/replace: user accepted the offer. we are preparing our response + * Unacked - no iq "result" yet + * Accepted - session/content/transport-replace accepted. app should start negotiation explicitly + * Connecting - connetion negotiation + * Active - traferring data + * Finished - In failure case: Needs to report transport failure / replace / reject + + Locally initiated session passes all the above and remotely initiated skips Pending. + */ + enum class State { Created, ApprovedToSend, Unacked, Pending, Accepted, Connecting, Active, Finishing, Finished }; + + enum class Action { // ordered by priority. first sent first + NoAction, // non-standard, just a default + TransportAccept, + TransportInfo, + TransportReject, + TransportReplace, + SessionInfo, + ContentAccept, + ContentAdd, + ContentModify, + ContentReject, + ContentRemove, + DescriptionInfo, + SecurityInfo, + SessionAccept, + SessionInitiate, + SessionTerminate + }; + +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) + inline uint qHash(const XMPP::Jingle::Action &o, uint seed = 0) { return ::qHash(int(o), seed); } +#else + inline size_t qHash(const XMPP::Jingle::Action &o, size_t seed = 0) { return ::qHash(int(o), seed); } +#endif -class Application; -class Transport : public QObject { - Q_OBJECT -public: /* Categorization by speed, reliability and connectivity - speed: realtim, fast, slow @@ -249,248 +146,251 @@ class Transport : public QObject { Also most of transports may add extra features but it's matter of configuration. For example all of them can enable p2p crypto mode ( should work here) */ - enum Feature { - // connection establishment - HardToConnect = 0x01, // anything else but ibb - AlwaysConnect = 0x02, // ibb. basically it's always connected - - // reliability - NotReliable = 0x10, // datagram-oriented - Reliable = 0x20, // connection-orinted - - // speed. - Slow = 0x100, // only ibb is here probably - Fast = 0x200, // basically all tcp-based and reliable part of sctp - RealTime = 0x400 // it's rather about synchronization of frames with time which implies fast - }; - Q_DECLARE_FLAGS(Features, Feature) + enum class TransportFeature { + HighProbableConnect = 0x01, // e.g ICE. ICE will have priority over others thanks to Fast|MessageOriented + AlwaysConnect = 0x02, // e.g. IBB. basically it's always connected + // 0x04 is reserved for possible Unordered + Ordered = 0x08, // where MessageOriented transport guarantees ordered delivery - using QObject::QObject; + // exclusive + Unreliable = 0x10, // losses are acceptable + Reliable = 0x20, // losses are unacceptable - enum Direction { // incoming or outgoing file/data transfer. - Outgoing, - Incoming - }; + Fast = 0x40, // assumes p2p or some light proxies like TURN/S5B (likely everything but IBB) + LowOverhead = 0x80, // p2p only - /** - * @brief prepare to send content-add/session-initiate - * When ready, the application first set update type to ContentAdd and then emit updated() - */ - virtual void prepare() = 0; + // exclusive + StreamOriented = 0x200, + MessageOriented = 0x400, - /** - * @brief start really transfer data. starting with connection to remote candidates for example - */ - virtual void start() = 0; // for local transport start searching for candidates (including probing proxy,stun etc) - // for remote transport try to connect to all proposed hosts in order their priority. - // in-band transport may just emit updated() here - virtual bool update(const QDomElement &el) = 0; // accepts transport element on incoming transport-info - virtual Action outgoingUpdateType() const = 0; - virtual OutgoingUpdate takeOutgoingUpdate() = 0; - virtual bool isValid() const = 0; - virtual Features features() const = 0; - virtual TransportManagerPad::Ptr pad() const = 0; - virtual void setApplication(Application *) = 0; -signals: - void updated(); // found some candidates and they have to be sent. takeUpdate has to be called from this signal handler. - // if it's just always ready then signal has to be sent at least once otherwise session-initiate won't be sent. - void connected(); // this signal is for app logic. maybe to finally start drawing some progress bar - void failed(); // transport ailed for whatever reason. aborted for example -}; - -class Application : public QObject -{ - Q_OBJECT -public: - - enum SetDescError { - Ok, - Unparsed, - IncompatibleParameters // this one is for + // exclusive + DataOriented = 0x800, // the goal is to deliver data + LiveOriented = 0x1000 // the goal is keep data synchronized with time (e.g. for rtp) }; + Q_DECLARE_FLAGS(TransportFeatures, TransportFeature) + Q_DECLARE_OPERATORS_FOR_FLAGS(TransportFeatures) - virtual ApplicationManagerPad::Ptr pad() const = 0; - virtual State state() const = 0; - virtual void setState(State state) = 0; // likely just remember the state + using ContentKey = QPair; + using OutgoingUpdateCB = std::function; - virtual Origin creator() const = 0; - virtual Origin senders() const = 0; - virtual QString contentName() const = 0; - virtual SetDescError setDescription(const QDomElement &description) = 0; + // list of elements to b inserted to and success callback + using OutgoingUpdate = std::tuple, OutgoingUpdateCB>; - /** - * @brief setTransport checks if transport is compatible and stores it - * @param transport - * @return false if not compatible - */ - virtual bool setTransport(const QSharedPointer &transport) = 0; - virtual QSharedPointer transport() const = 0; - virtual Action outgoingUpdateType() const = 0; - virtual OutgoingUpdate takeOutgoingUpdate() = 0; // this may return something only when outgoingUpdateType() != NoAction - virtual bool wantBetterTransport(const QSharedPointer &) const = 0; - virtual bool selectNextTransport() = 0; + // transport element and success callback + using OutgoingTransportInfoUpdate = std::tuple; - /** - * @brief prepare to send content-add/session-initiate - * When ready, the application first set update type to ContentAdd and then emit updated() - */ - virtual void prepare() = 0; - virtual void start() = 0; - -signals: - void updated(); -}; - -class Session : public QObject -{ - Q_OBJECT -public: - // Note incoming session are not registered in Jingle Manager until validated. - // and then either rejected or registered in Pending state. - - Session(Manager *manager, const Jid &peer); - ~Session(); - - Manager* manager() const; - State state() const; - - Jid me() const; - Jid peer() const; - Jid initiator() const; - Jid responder() const; - - Origin role() const; // my role in session: initiator or responder - XMPP::Stanza::Error lastError() const; - - // make new local content but do not add it to session yet - Application *newContent(const QString &ns, Origin senders = Origin::Both); - // get registered content if any - Application *content(const QString &contentName, Origin creator); - void addContent(Application *content); - const QMap &contentList() const; - - ApplicationManagerPad::Ptr applicationPad(const QString &ns); - TransportManagerPad::Ptr transportPad(const QString &ns); - - QSharedPointer newOutgoingTransport(const QString &ns); - - QString preferredApplication() const; - QStringList allApplicationTypes() const; - - void setLocalJid(const Jid &jid); // w/o real use case the implementation is rather stub - - void accept(); - void initiate(); - void terminate(Reason::Condition cond, const QString &comment = QString()); - - // allocates or returns existing pads - ApplicationManagerPad::Ptr applicationPadFactory(const QString &ns); - TransportManagerPad::Ptr transportPadFactory(const QString &ns); -signals: - void managerPadAdded(const QString &ns); - void activated(); - void terminated(); - -private: - friend class Manager; - friend class JTPush; - bool incomingInitiate(const Jingle &jingle, const QDomElement &jingleEl); - bool updateFromXml(Action action, const QDomElement &jingleEl); - - class Private; - QScopedPointer d; -}; - -class ApplicationManager : public QObject -{ - Q_OBJECT -public: - ApplicationManager(QObject *parent = nullptr); - - virtual void setJingleManager(Manager *jm) = 0; - virtual Application* startApplication(const ApplicationManagerPad::Ptr &pad, const QString &contentName, Origin creator, Origin senders) = 0; - virtual ApplicationManagerPad *pad(Session *session) = 0; - - // this method is supposed to gracefully close all related sessions as a preparation for plugin unload for example - virtual void closeAll() = 0; -}; - -class TransportManager : public QObject -{ - Q_OBJECT -public: - - TransportManager(QObject *parent = nullptr); - - // may show more features than Transport instance. For example some transports may work in both reliable and not reliable modes - virtual Transport::Features features() const = 0; - virtual void setJingleManager(Manager *jm) = 0; - - // FIXME rename methods - virtual QSharedPointer newTransport(const TransportManagerPad::Ptr &pad) = 0; // outgoing. one have to call Transport::start to collect candidates - virtual QSharedPointer newTransport(const TransportManagerPad::Ptr &pad, const QDomElement &transportEl) = 0; // incoming - virtual TransportManagerPad* pad(Session *session) = 0; - - // this method is supposed to gracefully close all related sessions as a preparation for plugin unload for example - virtual void closeAll() = 0; -signals: - void abortAllRequested(); // mostly used by transport instances to abort immediately -}; - -class Manager : public QObject -{ - Q_OBJECT - -public: - explicit Manager(XMPP::Client *client = 0); - ~Manager(); - - XMPP::Client* client() const; + class ErrorUtil { + public: + enum { + UnknownError, // unparsed/unknown error + OutOfOrder, + TieBreak, + UnknownSession, + UnsupportedInfo, + Last + }; - void setRedirection(const Jid &to); - const Jid &redirectionJid() const; - - void registerApp(const QString &ns, ApplicationManager *app); - void unregisterApp(const QString &ns); - bool isRegisteredApplication(const QString &ns); - ApplicationManagerPad* applicationPad(Session *session, const QString &ns); // allocates new pad on application manager - - void registerTransport(const QString &ns, TransportManager *transport); - void unregisterTransport(const QString &ns); - bool isRegisteredTransport(const QString &ns); - TransportManagerPad* transportPad(Session *session, const QString &ns); // allocates new pad on transport manager - QStringList availableTransports(const Transport::Features &features = Transport::Features()) const; + static const char *names[Last]; - /** - * @brief isAllowedParty checks if the remote jid allowed to initiate a session - * @param jid - remote jid - * @return true if allowed - */ - bool isAllowedParty(const Jid &jid) const; - void setRemoteJidChecker(std::function checker); + static XMPP::Stanza::Error make(QDomDocument &doc, int jingleCond, int type = XMPP::Stanza::Error::Cancel, + int condition = XMPP::Stanza::Error::UndefinedCondition, + const QString &text = QString()); - Session* session(const Jid &remoteJid, const QString &sid); - Session* newSession(const Jid &j); - QString generateSessionId(const Jid &peer); - XMPP::Stanza::Error lastError() const; + static XMPP::Stanza::Error makeTieBreak(QDomDocument &doc); + static XMPP::Stanza::Error makeOutOfOrder(QDomDocument &doc); -signals: - void incomingSession(Session *); + static void fill(QDomDocument doc, XMPP::Stanza::Error &error, int jingleCond); + static int jingleCondition(const XMPP::Stanza::Error &error); + }; -private: - friend class JTPush; - Session *incomingSessionInitiate(const Jid &from, const Jingle &jingle, const QDomElement &jingleEl); + class Jingle { + public: + Jingle(); // make invalid jingle element + Jingle(Action action, const QString &sid); // start making outgoing jingle + Jingle(const QDomElement &e); // likely incoming + Jingle(const Jingle &); + ~Jingle(); + + QDomElement toXml(QDomDocument *doc) const; + inline bool isValid() const { return d != nullptr; } + Action action() const; + const QString &sid() const; + const Jid &initiator() const; + void setInitiator(const Jid &jid); + const Jid &responder() const; + void setResponder(const Jid &jid); + + private: + class Private; + QSharedDataPointer d; + Jingle::Private *ensureD(); + }; - class Private; - QScopedPointer d; -}; + class Reason { + class Private; + + public: + enum Condition { + NoReason = 0, // non-standard, just a default + AlternativeSession, + Busy, + Cancel, + ConnectivityError, + Decline, + Expired, + FailedApplication, + FailedTransport, + GeneralError, + Gone, + IncompatibleParameters, + MediaError, + SecurityError, + Success, + Timeout, + UnsupportedApplications, + UnsupportedTransports + }; + + Reason(); + ~Reason(); + Reason(Condition cond, const QString &text = QString()); + Reason(const QDomElement &el); + Reason(const Reason &other); + Reason &operator=(const Reason &); + inline bool isValid() const { return d != nullptr; } + Condition condition() const; + void setCondition(Condition cond); + QString text() const; + void setText(const QString &text); + + QDomElement toXml(QDomDocument *doc) const; + + private: + Private *ensureD(); + + QSharedDataPointer d; + }; + + class ContentBase { + public: + inline ContentBase() { } + ContentBase(Origin creator, const QString &name); + ContentBase(const QDomElement &el); + + inline bool isValid() const { return creator != Origin::None && !name.isEmpty(); } + + inline QDomElement toXml(QDomDocument *doc, const char *tagName, const QString &ns = QString()) const + { + return toXml(doc, QLatin1String(tagName), ns); + } + QDomElement toXml(QDomDocument *doc, const QString &tagName, const QString &ns = QString()) const; + static Origin creatorAttr(const QDomElement &el); + static bool setCreatorAttr(QDomElement &el, Origin creator); + + Origin creator = Origin::None; + QString name; + Origin senders = Origin::Both; + QString disposition; // default "session" + }; -Origin negateOrigin(Origin o); + class Security { }; + + /** + * @brief The SessionManagerPad class - TransportManager/AppManager PAD + * + * The class is intended to be used to monitor global session events + * as well as send them in context of specific application type. + * + * For example a session has 3 content elements (voice, video and whiteboard). + * voice and video are related to RTP application while whiteaboard (Jingle SXE) + * is a different application. Therefore the session will have 2 pads: + * rtp pad and whitebaord pad. + * The pads are connected to both session and transport/application manager + * and their main task to handle Jingle session-info events. + * + * SessionManagerPad is a base class for all kinds of pads. + * UI can connect to its signals. + */ + class SessionManagerPad : public QObject { + Q_OBJECT + public: + virtual QDomElement takeOutgoingSessionInfoUpdate(); + virtual QString ns() const = 0; + virtual Session *session() const = 0; + + virtual void onLocalAccepted(); // changing to prepare state + virtual void onSend(); // local stuff is prepared we are going to send it to remote + + virtual void populateOutgoing(Action action, QDomElement &el); + QDomDocument *doc() const; + }; + + class ApplicationManager; + class ApplicationManagerPad; + class TransportManager; + class TransportManagerPad; + class Manager : public QObject { + Q_OBJECT + + public: + explicit Manager(XMPP::Client *client = nullptr); + ~Manager(); + + XMPP::Client *client() const; + + // if we have another jingle manager we can add its contents' namespaces here. + void addExternalManager(const QString &ns); + // on outgoing session destroy an external manager should call this function. + void registerExternalSession(const QString &sid); + void forgetExternalSession(const QString &sid); + + void setRedirection(const Jid &to); + const Jid &redirectionJid() const; + + void registerApplication(ApplicationManager *app); + void unregisterApp(const QString &ns); + bool isRegisteredApplication(const QString &ns); + ApplicationManagerPad *applicationPad(Session *session, + const QString &ns); // allocates new pad on application manager + + void registerTransport(TransportManager *transport); + void unregisterTransport(const QString &ns); + bool isRegisteredTransport(const QString &ns); + TransportManagerPad *transportPad(Session *session, + const QString &ns); // allocates new pad on transport manager + QStringList availableTransports(const TransportFeatures &features = TransportFeatures()) const; + + QStringList discoFeatures() const; + + /** + * @brief isAllowedParty checks if the remote jid allowed to initiate a session + * @param jid - remote jid + * @return true if allowed + */ + bool isAllowedParty(const Jid &jid) const; + void setRemoteJidChecker(std::function checker); + + Session *session(const Jid &remoteJid, const QString &sid); + Session *newSession(const Jid &j); + QString registerSession(Session *session); + XMPP::Stanza::Error lastError() const; + + void detachSession(Session *s); // disconnect the session from manager + signals: + void incomingSession(Session *); + + private: + friend class JTPush; + Session *incomingSessionInitiate(const Jid &from, const Jingle &jingle, const QDomElement &jingleEl); + + class Private; + std::unique_ptr d; + }; + + Origin negateOrigin(Origin o); } // namespace Jingle } // namespace XMPP -Q_DECLARE_OPERATORS_FOR_FLAGS(XMPP::Jingle::Transport::Features) - #endif // JINGLE_H diff --git a/src/xmpp/xmpp-im/s5b.cpp b/src/xmpp/xmpp-im/s5b.cpp index 2839ac39..0398ac63 100644 --- a/src/xmpp/xmpp-im/s5b.cpp +++ b/src/xmpp/xmpp-im/s5b.cpp @@ -19,34 +19,37 @@ #include "s5b.h" -#include -#include -#include -#include -#include - -#include "xmpp_xmlcommon.h" #include "im.h" #include "jingle-s5b.h" #include "socks.h" +#include "tcpportreserver.h" +#include "xmpp_xmlcommon.h" +#include +#include +#include +#include +#include #ifdef Q_OS_WIN -# include +#include +#ifdef _MSC_VER +#include +#endif #else -# include +#include #endif #define MAXSTREAMHOSTS 5 +// #define S5B_DEBUG static const char *S5B_NS = "http://jabber.org/protocol/bytestreams"; namespace XMPP { - static QString makeKey(const QString &sid, const Jid &requester, const Jid &target) { #ifdef S5B_DEBUG - qDebug("makeKey: sid=%s requester=%s target=%s %s", qPrintable(sid), - qPrintable(requester.full()), qPrintable(target.full()), + qDebug("makeKey: sid=%s requester=%s target=%s %s", qPrintable(sid), qPrintable(requester.full()), + qPrintable(target.full()), qPrintable(QCA::Hash("sha1").hashToString(QString(sid + requester.full() + target.full()).toUtf8()))); #endif QString str = sid + requester.full() + target.full(); @@ -55,15 +58,14 @@ static QString makeKey(const QString &sid, const Jid &requester, const Jid &targ static bool haveHost(const StreamHostList &list, const Jid &j) { - for(StreamHostList::ConstIterator it = list.begin(); it != list.end(); ++it) { - if((*it).jid().compare(j)) + for (const auto &streamHost : list) { + if (streamHost.jid().compare(j)) return true; } return false; } -class S5BManager::Item : public QObject -{ +class S5BManager::Item : public QObject { Q_OBJECT public: @@ -71,40 +73,41 @@ class S5BManager::Item : public QObject enum { ErrRefused, ErrConnect, ErrWrongHost, ErrProxy }; enum { Unknown, Fast, NotFast }; - S5BManager *m = nullptr; - int state = 0; - QString sid, key, out_key, out_id, in_id; - Jid self, peer; - StreamHostList in_hosts; - JT_S5B *task = nullptr; - JT_S5B *proxy_task = nullptr; - SocksClient *client = nullptr; - SocksClient *client_out = nullptr; - SocksUDP *client_udp = nullptr; - SocksUDP *client_out_udp = nullptr; - S5BConnector *conn = nullptr; - S5BConnector *proxy_conn = nullptr; - bool wantFast = false; + S5BManager *m = nullptr; + int state = 0; + QString sid, key, out_key, out_id, in_id; + Jid self, peer; + StreamHostList in_hosts; + JT_S5B *task = nullptr; + JT_S5B *proxy_task = nullptr; + QList> relatedServers; + SocksClient *client = nullptr; + SocksClient *client_out = nullptr; + SocksUDP *client_udp = nullptr; + SocksUDP *client_out_udp = nullptr; + S5BConnector *conn = nullptr; + S5BConnector *proxy_conn = nullptr; + // S5BServersManager::S5BLocalServers *localServ = nullptr; + bool wantFast = false; StreamHost proxy; - int targetMode = 0; // requester sets this once it figures it out - bool fast; // target sets this - bool activated; - bool lateProxy; - bool connSuccess; - bool localFailed, remoteFailed; - bool allowIncoming; - bool udp; - int statusCode = 0; - Jid activatedStream; + int targetMode = 0; // requester sets this once it figures it out + bool fast; // target sets this + bool activated; + bool lateProxy; + bool connSuccess; + bool localFailed, remoteFailed; + bool allowIncoming; + bool udp; + int statusCode = 0; + Jid activatedStream; Item(S5BManager *manager); ~Item(); void resetConnection(); void startRequester(const QString &_sid, const Jid &_self, const Jid &_peer, bool fast, bool udp); - void startTarget(const QString &_sid, const Jid &_self, const Jid &_peer, - const QString &_dstaddr, const StreamHostList &hosts, - const QString &iq_id, bool fast, bool udp); + void startTarget(const QString &_sid, const Jid &_self, const Jid &_peer, const QString &_dstaddr, + const StreamHostList &hosts, const QString &iq_id, bool fast, bool udp); void handleFast(const StreamHostList &hosts, const QString &iq_id); void doOutgoing(); @@ -140,64 +143,46 @@ private slots: //---------------------------------------------------------------------------- // S5BDatagram //---------------------------------------------------------------------------- -S5BDatagram::S5BDatagram() -{ -} +S5BDatagram::S5BDatagram() { } -S5BDatagram::S5BDatagram(int source, int dest, const QByteArray &data) - : _source(source) - , _dest(dest) - , _buf(data) -{ -} +S5BDatagram::S5BDatagram(int source, int dest, const QByteArray &data) : _source(source), _dest(dest), _buf(data) { } -int S5BDatagram::sourcePort() const -{ - return _source; -} +int S5BDatagram::sourcePort() const { return _source; } -int S5BDatagram::destPort() const -{ - return _dest; -} +int S5BDatagram::destPort() const { return _dest; } -QByteArray S5BDatagram::data() const -{ - return _buf; -} +QByteArray S5BDatagram::data() const { return _buf; } //---------------------------------------------------------------------------- // S5BConnection //---------------------------------------------------------------------------- -class S5BConnection::Private -{ +class S5BConnection::Private { public: - S5BManager *m; - SocksClient *sc; - SocksUDP *su; - int state; - Jid peer; - QString sid; - bool remote; - bool switched; - bool notifyRead, notifyClose; - int id; - S5BRequest req; - Jid proxy; - Mode mode; - QList dglist; + S5BManager *m; + SocksClient *sc; + SocksUDP *su; + int state; + Jid peer; + QString sid; + bool remote; + bool switched; + bool notifyRead, notifyClose; + int id; + S5BRequest req; + Jid proxy; + Mode mode; + QList dglist; }; -static int id_conn = 0; +static int id_conn = 0; static int num_conn = 0; -S5BConnection::S5BConnection(S5BManager *m, QObject *parent) - : BSConnection(parent) +S5BConnection::S5BConnection(S5BManager *m, QObject *parent) : BSConnection(parent) { - d = new Private; - d->m = m; - d->sc = 0; - d->su = 0; + d = new Private; + d->m = m; + d->sc = nullptr; + d->su = nullptr; ++num_conn; d->id = id_conn++; @@ -223,47 +208,41 @@ S5BConnection::~S5BConnection() void S5BConnection::resetConnection(bool clear) { d->m->con_unlink(this); - if(clear && d->sc) { + if (clear && d->sc) { delete d->sc; - d->sc = 0; + d->sc = nullptr; } delete d->su; - d->su = 0; - if(clear) { + d->su = nullptr; + if (clear) { while (!d->dglist.isEmpty()) { delete d->dglist.takeFirst(); } } d->state = Idle; setOpenMode(QIODevice::NotOpen); - d->peer = Jid(); - d->sid = QString(); - d->remote = false; - d->switched = false; - d->notifyRead = false; + d->peer = Jid(); + d->sid = QString(); + d->remote = false; + d->switched = false; + d->notifyRead = false; d->notifyClose = false; } -Jid S5BConnection::proxy() const -{ - return d->proxy; -} +Jid S5BConnection::proxy() const { return d->proxy; } -void S5BConnection::setProxy(const Jid &proxy) -{ - d->proxy = proxy; -} +void S5BConnection::setProxy(const Jid &proxy) { d->proxy = proxy; } void S5BConnection::connectToJid(const Jid &peer, const QString &sid, Mode m) { resetConnection(true); - if(!d->m->isAcceptableSID(peer, sid)) + if (!d->m->isAcceptableSID(peer, sid)) return; - d->peer = peer; - d->sid = sid; + d->peer = peer; + d->sid = sid; d->state = Requesting; - d->mode = m; + d->mode = m; #ifdef S5B_DEBUG qDebug("S5BConnection[%d]: connecting %s [%s]\n", d->id, qPrintable(d->peer.full()), qPrintable(d->sid)); #endif @@ -272,7 +251,7 @@ void S5BConnection::connectToJid(const Jid &peer, const QString &sid, Mode m) void S5BConnection::accept() { - if(d->state != WaitingForAccept) + if (d->state != WaitingForAccept) return; d->state = Connecting; @@ -284,12 +263,12 @@ void S5BConnection::accept() void S5BConnection::close() { - if(d->state == Idle) + if (d->state == Idle) return; - if(d->state == WaitingForAccept) + if (d->state == WaitingForAccept) d->m->con_reject(this); - else if(d->state == Active) + else if (d->state == Active) d->sc->close(); #ifdef S5B_DEBUG qDebug("S5BConnection[%d]: closing %s [%s]\n", d->id, qPrintable(d->peer.full()), qPrintable(d->sid)); @@ -297,46 +276,28 @@ void S5BConnection::close() resetConnection(); } -Jid S5BConnection::peer() const -{ - return d->peer; -} +Jid S5BConnection::peer() const { return d->peer; } -QString S5BConnection::sid() const -{ - return d->sid; -} +QString S5BConnection::sid() const { return d->sid; } -BytestreamManager* S5BConnection::manager() const -{ - return d->m; -} +BytestreamManager *S5BConnection::manager() const { return d->m; } -bool S5BConnection::isRemote() const -{ - return d->remote; -} +bool S5BConnection::isRemote() const { return d->remote; } -S5BConnection::Mode S5BConnection::mode() const -{ - return d->mode; -} +S5BConnection::Mode S5BConnection::mode() const { return d->mode; } -int S5BConnection::state() const -{ - return d->state; -} +int S5BConnection::state() const { return d->state; } qint64 S5BConnection::writeData(const char *data, qint64 maxSize) { - if(d->state == Active && d->mode == Stream) + if (d->state == Active && d->mode == Stream) return d->sc->write(data, maxSize); return 0; } qint64 S5BConnection::readData(char *data, qint64 maxSize) { - if(d->sc) + if (d->sc) return d->sc->read(data, maxSize); else return 0; @@ -344,7 +305,7 @@ qint64 S5BConnection::readData(char *data, qint64 maxSize) qint64 S5BConnection::bytesAvailable() const { - if(d->sc) + if (d->sc) return d->sc->bytesAvailable(); else return 0; @@ -352,7 +313,7 @@ qint64 S5BConnection::bytesAvailable() const qint64 S5BConnection::bytesToWrite() const { - if(d->state == Active) + if (d->state == Active) return d->sc->bytesToWrite(); else return 0; @@ -362,38 +323,35 @@ void S5BConnection::writeDatagram(const S5BDatagram &i) { QByteArray buf; buf.resize(i.data().size() + 4); - ushort ssp = htons(i.sourcePort()); - ushort sdp = htons(i.destPort()); + ushort ssp = htons(uint16_t(i.sourcePort())); + ushort sdp = htons(uint16_t(i.destPort())); QByteArray data = i.data(); memcpy(buf.data(), &ssp, 2); memcpy(buf.data() + 2, &sdp, 2); - memcpy(buf.data() + 4, data.data(), data.size()); + memcpy(buf.data() + 4, data.data(), size_t(data.size())); sendUDP(buf); } S5BDatagram S5BConnection::readDatagram() { - if(d->dglist.isEmpty()) + if (d->dglist.isEmpty()) return S5BDatagram(); - S5BDatagram *i = d->dglist.takeFirst(); - S5BDatagram val = *i; + S5BDatagram *i = d->dglist.takeFirst(); + S5BDatagram val = *i; delete i; return val; } -int S5BConnection::datagramsAvailable() const -{ - return d->dglist.count(); -} +int S5BConnection::datagramsAvailable() const { return d->dglist.count(); } void S5BConnection::man_waitForAccept(const S5BRequest &r) { - d->state = WaitingForAccept; + d->state = WaitingForAccept; d->remote = true; - d->req = r; - d->peer = r.from; - d->sid = r.sid; - d->mode = r.udp ? Datagram : Stream; + d->req = r; + d->peer = r.from; + d->sid = r.sid; + d->mode = r.udp ? Datagram : Stream; } void S5BConnection::man_clientReady(SocksClient *sc, SocksUDP *sc_udp) @@ -405,7 +363,7 @@ void S5BConnection::man_clientReady(SocksClient *sc, SocksUDP *sc_udp) connect(d->sc, SIGNAL(bytesWritten(qint64)), SLOT(sc_bytesWritten(qint64))); connect(d->sc, SIGNAL(error(int)), SLOT(sc_error(int))); - if(sc_udp) { + if (sc_udp) { d->su = sc_udp; connect(d->su, SIGNAL(packetReady(QByteArray)), SLOT(su_packetReady(QByteArray))); } @@ -417,57 +375,53 @@ void S5BConnection::man_clientReady(SocksClient *sc, SocksUDP *sc_udp) #endif // bytes already in the stream? - if(d->sc->bytesAvailable()) { + if (d->sc->bytesAvailable()) { #ifdef S5B_DEBUG qDebug("Stream has %d bytes in it.\n", (int)d->sc->bytesAvailable()); #endif d->notifyRead = true; } // closed before it got here? - if(!d->sc->isOpen()) { + if (!d->sc->isOpen()) { #ifdef S5B_DEBUG qDebug("Stream was closed before S5B request finished?\n"); #endif d->notifyClose = true; } - if(d->notifyRead || d->notifyClose) + if (d->notifyRead || d->notifyClose) QTimer::singleShot(0, this, SLOT(doPending())); emit connected(); } void S5BConnection::doPending() { - if(d->notifyRead) { - if(d->notifyClose) + if (d->notifyRead) { + if (d->notifyClose) QTimer::singleShot(0, this, SLOT(doPending())); sc_readyRead(); - } - else if(d->notifyClose) + } else if (d->notifyClose) sc_connectionClosed(); } -void S5BConnection::man_udpReady(const QByteArray &buf) -{ - handleUDP(buf); -} +void S5BConnection::man_udpReady(const QByteArray &buf) { handleUDP(buf); } void S5BConnection::man_failed(int x) { resetConnection(true); - if(x == S5BManager::Item::ErrRefused) + if (x == S5BManager::Item::ErrRefused) setError(ErrRefused); - if(x == S5BManager::Item::ErrConnect) + if (x == S5BManager::Item::ErrConnect) setError(ErrConnect); - if(x == S5BManager::Item::ErrWrongHost) + if (x == S5BManager::Item::ErrWrongHost) setError(ErrConnect); - if(x == S5BManager::Item::ErrProxy) + if (x == S5BManager::Item::ErrProxy) setError(ErrProxy); } void S5BConnection::sc_connectionClosed() { // if we have a pending read notification, postpone close - if(d->notifyRead) { + if (d->notifyRead) { #ifdef S5B_DEBUG qDebug("closed while pending read\n"); #endif @@ -476,7 +430,7 @@ void S5BConnection::sc_connectionClosed() } d->notifyClose = false; resetConnection(); - connectionClosed(); + emit connectionClosed(); } void S5BConnection::sc_delayedCloseFinished() @@ -487,7 +441,7 @@ void S5BConnection::sc_delayedCloseFinished() void S5BConnection::sc_readyRead() { - if(d->mode == Datagram) { + if (d->mode == Datagram) { // throw the data away d->sc->readAll(); return; @@ -501,7 +455,7 @@ void S5BConnection::sc_readyRead() void S5BConnection::sc_bytesWritten(qint64 x) { // echo - bytesWritten(x); + emit bytesWritten(x); } void S5BConnection::sc_error(int) @@ -510,33 +464,30 @@ void S5BConnection::sc_error(int) setError(ErrSocket); } -void S5BConnection::su_packetReady(const QByteArray &buf) -{ - handleUDP(buf); -} +void S5BConnection::su_packetReady(const QByteArray &buf) { handleUDP(buf); } void S5BConnection::handleUDP(const QByteArray &buf) { // must be at least 4 bytes, to accomodate virtual ports - if(buf.size() < 4) + if (buf.size() < 4) return; // drop ushort ssp, sdp; memcpy(&ssp, buf.data(), 2); memcpy(&sdp, buf.data() + 2, 2); - int source = ntohs(ssp); - int dest = ntohs(sdp); + int source = ntohs(ssp); + int dest = ntohs(sdp); QByteArray data; data.resize(buf.size() - 4); - memcpy(data.data(), buf.data() + 4, data.size()); + memcpy(data.data(), buf.data() + 4, size_t(data.size())); d->dglist.append(new S5BDatagram(source, dest, data)); - datagramReady(); + emit datagramReady(); } void S5BConnection::sendUDP(const QByteArray &buf) { - if(d->su) + if (d->su) d->su->write(buf); else d->m->con_sendUDP(this, buf); @@ -545,58 +496,48 @@ void S5BConnection::sendUDP(const QByteArray &buf) //---------------------------------------------------------------------------- // S5BManager //---------------------------------------------------------------------------- -class S5BManager::Entry -{ +class S5BManager::Entry { public: Entry() = default; - ~Entry() - { - delete query; - } + ~Entry() { delete query; } S5BConnection *c = nullptr; - Item *i = nullptr; - QString sid; - JT_S5B *query = nullptr; - StreamHost proxyInfo; - QPointer relatedServer; + Item *i = nullptr; + QString sid; + JT_S5B *query = nullptr; + StreamHost proxyInfo; - bool udp_init = false; + bool udp_init = false; QHostAddress udp_addr; - int udp_port = 0; + int udp_port = 0; }; -class S5BManager::Private -{ +class S5BManager::Private { public: - Client *client; - S5BServer *serv; - QList activeList; + Client *client; + QList activeList; S5BConnectionList incomingConns; - JT_PushS5B *ps; + JT_PushS5B *ps; }; -S5BManager::S5BManager(Client *parent) - : BytestreamManager(parent) +S5BManager::S5BManager(Client *parent) : BytestreamManager(parent) { // S5B needs SHA1 - //if(!QCA::isSupported(QCA::CAP_SHA1)) + // if(!QCA::isSupported(QCA::CAP_SHA1)) // QCA::insertProvider(createProviderHash()); - d = new Private; + d = new Private; d->client = parent; - d->serv = 0; d->ps = new JT_PushS5B(d->client->rootTask()); connect(d->ps, SIGNAL(incoming(S5BRequest)), SLOT(ps_incoming(S5BRequest))); - connect(d->ps, SIGNAL(incomingUDPSuccess(Jid,QString)), SLOT(ps_incomingUDPSuccess(Jid,QString))); - connect(d->ps, SIGNAL(incomingActivate(Jid,QString,Jid)), SLOT(ps_incomingActivate(Jid,QString,Jid))); + connect(d->ps, SIGNAL(incomingUDPSuccess(Jid, QString)), SLOT(ps_incomingUDPSuccess(Jid, QString))); + connect(d->ps, SIGNAL(incomingActivate(Jid, QString, Jid)), SLOT(ps_incomingActivate(Jid, QString, Jid))); } S5BManager::~S5BManager() { - setServer(0); while (!d->incomingConns.isEmpty()) { delete d->incomingConns.takeFirst(); } @@ -604,50 +545,25 @@ S5BManager::~S5BManager() delete d; } -const char* S5BManager::ns() -{ - return S5B_NS; -} - -Client *S5BManager::client() const -{ - return d->client; -} - -S5BServer *S5BManager::server() const -{ - return d->serv; -} +const char *S5BManager::ns() { return S5B_NS; } -void S5BManager::setServer(S5BServer *serv) -{ - if(d->serv) { - d->serv->unlink(this); - d->serv = 0; - } +Client *S5BManager::client() const { return d->client; } - if(serv) { - d->serv = serv; - d->serv->link(this); - } -} +JT_PushS5B *S5BManager::jtPush() const { return d->ps; } -BSConnection *S5BManager::createConnection() -{ - return new S5BConnection(this); -} +BSConnection *S5BManager::createConnection() { return new S5BConnection(this); } S5BConnection *S5BManager::takeIncoming() { - if(d->incomingConns.isEmpty()) - return 0; + if (d->incomingConns.isEmpty()) + return nullptr; S5BConnection *c = d->incomingConns.takeFirst(); // move to activeList Entry *e = new Entry; - e->c = c; - e->sid = c->d->sid; + e->c = c; + e->sid = c->d->sid; d->activeList.append(e); return c; @@ -662,20 +578,20 @@ void S5BManager::ps_incoming(const S5BRequest &req) bool ok = false; // ensure we don't already have an incoming connection from this peer+sid S5BConnection *c = findIncoming(req.from, req.sid); - if(!c) { + if (!c) { // do we have an active entry with this sid already? Entry *e = findEntryBySID(req.from, req.sid); - if(e) { - if(e->i) { + if (e) { + if (e->i) { // loopback - if(req.from.compare(d->client->jid()) && (req.id == e->i->out_id)) { + if (req.from.compare(d->client->jid()) && (req.id == e->i->out_id)) { #ifdef S5B_DEBUG qDebug("ALLOWED: loopback\n"); #endif ok = true; } // allowed by 'fast mode' - else if(e->i->state == Item::Requester && e->i->targetMode == Item::Unknown) { + else if (e->i->state == Item::Requester && e->i->targetMode == Item::Unknown) { #ifdef S5B_DEBUG qDebug("ALLOWED: fast-mode\n"); #endif @@ -683,15 +599,14 @@ void S5BManager::ps_incoming(const S5BRequest &req) return; } } - } - else { + } else { #ifdef S5B_DEBUG qDebug("ALLOWED: we don't have it\n"); #endif ok = true; } } - if(!ok) { + if (!ok) { d->ps->respondError(req.from, req.id, Stanza::Error::NotAcceptable, "SID in use"); return; } @@ -706,10 +621,10 @@ void S5BManager::ps_incoming(const S5BRequest &req) void S5BManager::ps_incomingUDPSuccess(const Jid &from, const QString &key) { Entry *e = findEntryByHash(key); - if(e && e->i) { - if(e->i->conn) + if (e && e->i) { + if (e->i->conn) e->i->conn->man_udpSuccess(from); - else if(e->i->proxy_conn) + else if (e->i->proxy_conn) e->i->proxy_conn->man_udpSuccess(from); } } @@ -717,7 +632,7 @@ void S5BManager::ps_incomingUDPSuccess(const Jid &from, const QString &key) void S5BManager::ps_incomingActivate(const Jid &from, const QString &sid, const Jid &streamHost) { Entry *e = findEntryBySID(from, sid); - if(e && e->i) + if (e && e->i) e->i->incomingActivate(streamHost); } @@ -726,8 +641,7 @@ void S5BManager::doSuccess(const Jid &peer, const QString &id, const Jid &stream d->ps->respondSuccess(peer, id, streamHost); } -void S5BManager::doError(const Jid &peer, const QString &id, - Stanza::Error::ErrorCond cond, const QString &str) +void S5BManager::doError(const Jid &peer, const QString &id, Stanza::Error::ErrorCond cond, const QString &str) { d->ps->respondError(peer, id, cond, str); } @@ -739,113 +653,98 @@ void S5BManager::doActivate(const Jid &peer, const QString &sid, const Jid &stre bool S5BManager::isAcceptableSID(const Jid &peer, const QString &sid) const { - QString key = makeKey(sid, d->client->jid(), peer); - QString key_out = makeKey(sid, peer, d->client->jid()); //not valid in muc via proxy + QString key = makeKey(sid, d->client->jid(), peer); + QString key_out = makeKey(sid, peer, d->client->jid()); // not valid in muc via proxy - // if we have a server, then check through it - if(d->serv) { - if(findServerEntryByHash(key) || findServerEntryByHash(key_out)) - return false; - } - else { - if(findEntryByHash(key) || findEntryByHash(key_out)) - return false; + for (Entry *e : std::as_const(d->activeList)) { + if (e->i) { + if (e->i->key == key || e->i->key == key_out) + return false; + else { + for (auto &s : e->i->relatedServers) { + if (s->hasKey(key) || s->hasKey(key_out)) { + return false; + } + } + } + } } + return true; } -const char* S5BManager::sidPrefix() const -{ - return "s5b_"; -} +const char *S5BManager::sidPrefix() const { return "s5b_"; } S5BConnection *S5BManager::findIncoming(const Jid &from, const QString &sid) const { - foreach(S5BConnection *c, d->incomingConns) { - if(c->d->peer.compare(from) && c->d->sid == sid) + for (S5BConnection *c : std::as_const(d->incomingConns)) { + if (c->d->peer.compare(from) && c->d->sid == sid) return c; } - return 0; + return nullptr; } S5BManager::Entry *S5BManager::findEntry(S5BConnection *c) const { - foreach(Entry *e, d->activeList) { - if(e->c == c) + for (Entry *e : std::as_const(d->activeList)) { + if (e->c == c) return e; } - return 0; + return nullptr; } S5BManager::Entry *S5BManager::findEntry(Item *i) const { - foreach(Entry *e, d->activeList) { - if(e->i == i) + for (Entry *e : std::as_const(d->activeList)) { + if (e->i == i) return e; } - return 0; + return nullptr; } S5BManager::Entry *S5BManager::findEntryByHash(const QString &key) const { - foreach(Entry *e, d->activeList) { - if(e->i && e->i->key == key) + for (Entry *e : std::as_const(d->activeList)) { + if (e->i && e->i->key == key) return e; } - return 0; + return nullptr; } S5BManager::Entry *S5BManager::findEntryBySID(const Jid &peer, const QString &sid) const { - foreach(Entry *e, d->activeList) { - if(e->i && e->i->peer.compare(peer) && e->sid == sid) - return e; - } - return 0; -} - -S5BManager::Entry *S5BManager::findServerEntryByHash(const QString &key) const -{ - const QList &manList = d->serv->managerList(); - foreach(S5BManager *m, manList) { - Entry *e = m->findEntryByHash(key); - if(e) + for (Entry *e : std::as_const(d->activeList)) { + if (e->i && e->i->peer.compare(peer) && e->sid == sid) return e; } - return 0; + return nullptr; } -bool S5BManager::srv_ownsHash(const QString &key) const -{ - if(findEntryByHash(key)) - return true; - return false; -} +bool S5BManager::srv_ownsHash(const QString &key) const { return findEntryByHash(key) != nullptr; } void S5BManager::srv_incomingReady(SocksClient *sc, const QString &key) { Entry *e = findEntryByHash(key); - if(!e->i->allowIncoming) { + if (!e->i->allowIncoming) { sc->requestDeny(); - sc->deleteLater(); return; } - if(e->c->d->mode == S5BConnection::Datagram) + if (e->c->d->mode == S5BConnection::Datagram) sc->grantUDPAssociate("", 0); else sc->grantConnect(); - e->relatedServer = static_cast(sender()); e->i->setIncomingClient(sc); } -void S5BManager::srv_incomingUDP(bool init, const QHostAddress &addr, int port, const QString &key, const QByteArray &data) +void S5BManager::srv_incomingUDP(bool init, const QHostAddress &addr, int port, const QString &key, + const QByteArray &data) { Entry *e = findEntryByHash(key); - if(e->c->d->mode != S5BConnection::Datagram) + if (e->c->d->mode != S5BConnection::Datagram) return; // this key isn't in udp mode? drop! - if(init) { - if(e->udp_init) + if (init) { + if (e->udp_init) return; // only init once // lock on to this sender @@ -859,31 +758,26 @@ void S5BManager::srv_incomingUDP(bool init, const QHostAddress &addr, int port, } // not initialized yet? something went wrong - if(!e->udp_init) + if (!e->udp_init) return; // must come from same source as when initialized - if(addr.toString() != e->udp_addr.toString() || port != e->udp_port) + if (addr.toString() != e->udp_addr.toString() || port != e->udp_port) return; e->c->man_udpReady(data); } -void S5BManager::srv_unlink() -{ - d->serv = 0; -} - void S5BManager::con_connect(S5BConnection *c) { - if(findEntry(c)) + if (findEntry(c)) return; Entry *e = new Entry; - e->c = c; - e->sid = c->d->sid; + e->c = c; + e->sid = c->d->sid; d->activeList.append(e); - if(c->d->proxy.isValid()) { + if (c->d->proxy.isValid()) { queryProxy(e); return; } @@ -893,11 +787,11 @@ void S5BManager::con_connect(S5BConnection *c) void S5BManager::con_accept(S5BConnection *c) { Entry *e = findEntry(c); - if(!e) + if (!e) return; - if(e->c->d->req.fast) { - if(targetShouldOfferProxy(e)) { + if (e->c->d->req.fast) { + if (targetShouldOfferProxy(e)) { queryProxy(e); return; } @@ -907,20 +801,18 @@ void S5BManager::con_accept(S5BConnection *c) void S5BManager::con_reject(S5BConnection *c) { - d->ps->respondError(c->d->peer, c->d->req.id, Stanza::Error::NotAcceptable, - "Not acceptable"); + d->ps->respondError(c->d->peer, c->d->req.id, Stanza::Error::NotAcceptable, "Not acceptable"); } void S5BManager::con_unlink(S5BConnection *c) { Entry *e = findEntry(c); - if(!e) + if (!e) return; // active incoming request? cancel it - if(e->i && e->i->conn) - d->ps->respondError(e->i->peer, e->i->out_id, - Stanza::Error::NotAcceptable, "Not acceptable"); + if (e->i && e->i->conn) + d->ps->respondError(e->i->peer, e->i->out_id, Stanza::Error::NotAcceptable, "Not acceptable"); delete e->i; d->activeList.removeAll(e); delete e; @@ -929,18 +821,18 @@ void S5BManager::con_unlink(S5BConnection *c) void S5BManager::con_sendUDP(S5BConnection *c, const QByteArray &buf) { Entry *e = findEntry(c); - if(!e) + if (!e) return; - if(!e->udp_init) + if (!e->udp_init) return; - if(e->relatedServer) - e->relatedServer->writeUDP(e->udp_addr, e->udp_port, buf); + if (e->i->relatedServers.size()) // FIXME in fact we shoule keep eact related server in Connection + e->i->relatedServers[0]->writeUDP(e->udp_addr, e->udp_port, buf); } void S5BManager::item_accepted() { - Item *i = static_cast(sender()); + Item *i = static_cast(sender()); Entry *e = findEntry(i); emit e->c->accepted(); // signal @@ -948,38 +840,38 @@ void S5BManager::item_accepted() void S5BManager::item_tryingHosts(const StreamHostList &list) { - Item *i = static_cast(sender()); + Item *i = static_cast(sender()); Entry *e = findEntry(i); - e->c->tryingHosts(list); // signal + emit e->c->tryingHosts(list); // signal } void S5BManager::item_proxyConnect() { - Item *i = static_cast(sender()); + Item *i = static_cast(sender()); Entry *e = findEntry(i); - e->c->proxyConnect(); // signal + emit e->c->proxyConnect(); // signal } void S5BManager::item_waitingForActivation() { - Item *i = static_cast(sender()); + Item *i = static_cast(sender()); Entry *e = findEntry(i); - e->c->waitingForActivation(); // signal + emit e->c->waitingForActivation(); // signal } void S5BManager::item_connected() { - Item *i = static_cast(sender()); + Item *i = static_cast(sender()); Entry *e = findEntry(i); // grab the client - SocksClient *client = i->client; - i->client = 0; + SocksClient *client = i->client; + i->client = nullptr; SocksUDP *client_udp = i->client_udp; - i->client_udp = 0; + i->client_udp = nullptr; // give it to the connection e->c->man_clientReady(client, client_udp); @@ -987,7 +879,7 @@ void S5BManager::item_connected() void S5BManager::item_error(int x) { - Item *i = static_cast(sender()); + Item *i = static_cast(sender()); Entry *e = findEntry(i); e->c->man_failed(x); @@ -995,7 +887,7 @@ void S5BManager::item_error(int x) void S5BManager::entryContinue(Entry *e) { - e->i = new Item(this); + e->i = new Item(this); e->i->proxy = e->proxyInfo; connect(e->i, SIGNAL(accepted()), SLOT(item_accepted())); @@ -1005,21 +897,20 @@ void S5BManager::entryContinue(Entry *e) connect(e->i, SIGNAL(connected()), SLOT(item_connected())); connect(e->i, SIGNAL(error(int)), SLOT(item_error(int))); - if(e->c->isRemote()) { + if (e->c->isRemote()) { const S5BRequest &req = e->c->d->req; e->i->startTarget(e->sid, d->client->jid(), e->c->d->peer, req.dstaddr, req.hosts, req.id, req.fast, req.udp); - } - else { - e->i->startRequester(e->sid, d->client->jid(), e->c->d->peer, true, e->c->d->mode == S5BConnection::Datagram ? true: false); - e->c->requesting(); // signal + } else { + e->i->startRequester(e->sid, d->client->jid(), e->c->d->peer, true, e->c->d->mode == S5BConnection::Datagram); + emit e->c->requesting(); // signal } } void S5BManager::queryProxy(Entry *e) { QPointer self = this; - e->c->proxyQuery(); // signal - if(!self) + emit e->c->proxyQuery(); // signal + if (!self) return; #ifdef S5B_DEBUG @@ -1034,35 +925,34 @@ void S5BManager::queryProxy(Entry *e) void S5BManager::query_finished() { JT_S5B *query = static_cast(sender()); - Entry* e = 0; - foreach(Entry* i, d->activeList) { - if(i->query == query) { + Entry *e = nullptr; + for (Entry *i : std::as_const(d->activeList)) { + if (i->query == query) { e = i; break; } } - if(!e) + if (!e) return; - e->query = 0; + e->query = nullptr; #ifdef S5B_DEBUG qDebug("query finished: "); #endif - if(query->success()) { + if (query->success()) { e->proxyInfo = query->proxyInfo(); #ifdef S5B_DEBUG qDebug("host/ip=[%s] port=[%d]\n", qPrintable(e->proxyInfo.host()), e->proxyInfo.port()); #endif - } - else { + } else { #ifdef S5B_DEBUG qDebug("fail\n"); #endif } QPointer self = this; - e->c->proxyResult(query->success()); // signal - if(!self) + emit e->c->proxyResult(query->success()); // signal + if (!self) return; entryContinue(e); @@ -1070,40 +960,35 @@ void S5BManager::query_finished() bool S5BManager::targetShouldOfferProxy(Entry *e) { - if(!e->c->d->proxy.isValid()) + if (!e->c->d->proxy.isValid()) return false; // if target, don't offer any proxy if the requester already did const StreamHostList &hosts = e->c->d->req.hosts; - for(StreamHostList::ConstIterator it = hosts.begin(); it != hosts.end(); ++it) { - if((*it).isProxy()) + for (const auto &host : hosts) { + if (host.isProxy()) return false; } // ensure we don't offer the same proxy as the requester - if(haveHost(hosts, e->c->d->proxy)) - return false; - - return true; + return !haveHost(hosts, e->c->d->proxy); } //---------------------------------------------------------------------------- // S5BManager::Item //---------------------------------------------------------------------------- -S5BManager::Item::Item(S5BManager *manager) - : QObject(nullptr) - , m(manager) -{ - resetConnection(); -} +S5BManager::Item::Item(S5BManager *manager) : QObject(nullptr), m(manager) { resetConnection(); } -S5BManager::Item::~Item() -{ - resetConnection(); -} +S5BManager::Item::~Item() { resetConnection(); } void S5BManager::Item::resetConnection() { + for (auto &s : relatedServers) { + s->unregisterKey(key); + s.reset(); + } + relatedServers.clear(); + delete task; task = nullptr; @@ -1128,56 +1013,56 @@ void S5BManager::Item::resetConnection() delete client_out; client_out = nullptr; - state = Idle; - wantFast = false; - targetMode = Unknown; - fast = false; - activated = false; - lateProxy = false; - connSuccess = false; - localFailed = false; - remoteFailed = false; + state = Idle; + wantFast = false; + targetMode = Unknown; + fast = false; + activated = false; + lateProxy = false; + connSuccess = false; + localFailed = false; + remoteFailed = false; allowIncoming = false; - udp = false; + udp = false; } void S5BManager::Item::startRequester(const QString &_sid, const Jid &_self, const Jid &_peer, bool fast, bool _udp) { - sid = _sid; - self = _self; - peer = _peer; - key = makeKey(sid, self, peer); - out_key = makeKey(sid, peer, self); + sid = _sid; + self = _self; + peer = _peer; + key = makeKey(sid, self, peer); + out_key = makeKey(sid, peer, self); wantFast = fast; - udp = _udp; + udp = _udp; #ifdef S5B_DEBUG - qDebug("S5BManager::Item initiating request %s [%s] (inhash=%s)\n", qPrintable(peer.full()), qPrintable(sid), qPrintable(key)); + qDebug("S5BManager::Item initiating request %s [%s] (inhash=%s)\n", qPrintable(peer.full()), qPrintable(sid), + qPrintable(key)); #endif state = Requester; doOutgoing(); } -void S5BManager::Item::startTarget(const QString &_sid, const Jid &_self, - const Jid &_peer, const QString &_dstaddr, - const StreamHostList &hosts, const QString &iq_id, - bool _fast, bool _udp) +void S5BManager::Item::startTarget(const QString &_sid, const Jid &_self, const Jid &_peer, const QString &_dstaddr, + const StreamHostList &hosts, const QString &iq_id, bool _fast, bool _udp) { - sid = _sid; - peer = _peer; - self = _self; + sid = _sid; + peer = _peer; + self = _self; in_hosts = hosts; - in_id = iq_id; - fast = _fast; - key = makeKey(sid, self, peer); - out_key = _dstaddr.isEmpty() ? makeKey(sid, peer, self) : _dstaddr; - udp = _udp; + in_id = iq_id; + fast = _fast; + key = makeKey(sid, self, peer); + out_key = _dstaddr.isEmpty() ? makeKey(sid, peer, self) : _dstaddr; + udp = _udp; #ifdef S5B_DEBUG - qDebug("S5BManager::Item incoming request %s [%s] (inhash=%s)\n", qPrintable(peer.full()), qPrintable(sid), qPrintable(key)); + qDebug("S5BManager::Item incoming request %s [%s] (inhash=%s)\n", qPrintable(peer.full()), qPrintable(sid), + qPrintable(key)); #endif state = Target; - if(fast) + if (fast) doOutgoing(); doIncoming(); } @@ -1187,17 +1072,16 @@ void S5BManager::Item::handleFast(const StreamHostList &hosts, const QString &iq targetMode = Fast; QPointer self = this; - emit accepted(); - if(!self) + emit accepted(); + if (!self) return; // if we already have a stream, then bounce this request - if(client) { + if (client) { m->doError(peer, iq_id, Stanza::Error::NotAcceptable, "Not acceptable"); - } - else { + } else { in_hosts = hosts; - in_id = iq_id; + in_id = iq_id; doIncoming(); } } @@ -1205,24 +1089,39 @@ void S5BManager::Item::handleFast(const StreamHostList &hosts, const QString &iq void S5BManager::Item::doOutgoing() { StreamHostList hosts; - S5BServer *serv = m->server(); - if(serv && serv->isActive() && !haveHost(in_hosts, self)) { - QStringList hostList = serv->hostList(); - foreach (const QString & it, hostList) { + auto disco = m->client()->tcpPortReserver()->scope(QString::fromLatin1("s5b"))->disco(); + if (!haveHost(in_hosts, self)) { + for (auto &c : disco->takeServers()) { + auto server = c.staticCast(); + server->registerKey(key); + relatedServers.append(server); + connect(server.data(), &S5BServer::incomingConnection, this, [this](SocksClient *c, const QString &key) { + if (key == this->key) { + m->srv_incomingReady(c, key); + } + }); + connect(server.data(), &S5BServer::incomingUdp, this, + [this](bool isInit, const QHostAddress &addr, int sourcePort, const QString &key, + const QByteArray &data) { + if (key == this->key) { + m->srv_incomingUDP(isInit, addr, sourcePort, key, data); + } + }); StreamHost h; h.setJid(self); - h.setHost(it); - h.setPort(serv->port()); + h.setHost(c->publishHost()); + h.setPort(c->publishPort()); hosts += h; } } + delete disco; // FIXME we could start listening for signals instead. it may send us upnp candidate // if the proxy is valid, then it's ok to add (the manager already ensured that it doesn't conflict) - if(proxy.jid().isValid()) + if (proxy.jid().isValid()) hosts += proxy; // if we're the target and we have no streamhosts of our own, then don't even bother with fast-mode - if(state == Target && hosts.isEmpty()) { + if (state == Target && hosts.isEmpty()) { fast = false; return; } @@ -1238,40 +1137,38 @@ void S5BManager::Item::doOutgoing() void S5BManager::Item::doIncoming() { - if(in_hosts.isEmpty()) { + if (in_hosts.isEmpty()) { doConnectError(); return; } StreamHostList list; - if(lateProxy) { + if (lateProxy) { // take just the proxy streamhosts - foreach (const StreamHost& it, in_hosts) { + for (const StreamHost &it : std::as_const(in_hosts)) { if (it.isProxy()) list += it; } lateProxy = false; - } - else { + } else { // only try doing the late proxy trick if using fast mode AND we did not offer a proxy - if((state == Requester || (state == Target && fast)) && !proxy.jid().isValid()) { + if ((state == Requester || (state == Target && fast)) && !proxy.jid().isValid()) { // take just the non-proxy streamhosts bool hasProxies = false; - foreach (const StreamHost& it, in_hosts) { + for (const StreamHost &it : std::as_const(in_hosts)) { if (it.isProxy()) hasProxies = true; else list += it; } - if(hasProxies) { + if (hasProxies) { lateProxy = true; // no regular streamhosts? wait for remote error - if(list.isEmpty()) + if (list.isEmpty()) return; } - } - else + } else list = in_hosts; } @@ -1279,8 +1176,8 @@ void S5BManager::Item::doIncoming() connect(conn, SIGNAL(result(bool)), SLOT(conn_result(bool))); QPointer self = this; - tryingHosts(list); - if(!self) + emit tryingHosts(list); + if (!self) return; conn->start(this->self, list, out_key, udp, lateProxy ? 10 : 30); @@ -1296,13 +1193,14 @@ void S5BManager::Item::setIncomingClient(SocksClient *sc) connect(sc, SIGNAL(bytesWritten(qint64)), SLOT(sc_bytesWritten(qint64))); connect(sc, SIGNAL(error(int)), SLOT(sc_error(int))); - client = sc; + sc->setParent(nullptr); // avoid deleting it by SocksServer destructor + client = sc; allowIncoming = false; } void S5BManager::Item::incomingActivate(const Jid &streamHost) { - if(!activated) { + if (!activated) { activatedStream = streamHost; checkForActivation(); } @@ -1311,60 +1209,58 @@ void S5BManager::Item::incomingActivate(const Jid &streamHost) void S5BManager::Item::jt_finished() { JT_S5B *j = task; - task = 0; + task = nullptr; #ifdef S5B_DEBUG qDebug("jt_finished: state=%s, %s\n", state == Requester ? "requester" : "target", j->success() ? "ok" : "fail"); #endif - if(state == Requester) { - if(targetMode == Unknown) { - targetMode = NotFast; + if (state == Requester) { + if (targetMode == Unknown) { + targetMode = NotFast; QPointer self = this; - emit accepted(); - if(!self) + emit accepted(); + if (!self) return; } } // if we've already reported successfully connecting to them, then this response doesn't matter - if(state == Requester && connSuccess) { + if (state == Requester && connSuccess) { tryActivation(); return; } - if(j->success()) { + if (j->success()) { // stop connecting out - if(conn || lateProxy) { + if (conn || lateProxy) { delete conn; - conn = 0; + conn = nullptr; doConnectError(); } Jid streamHost = j->streamHostUsed(); // they connected to us? - if(streamHost.compare(self)) { - if(client) { - if(state == Requester) { + if (streamHost.compare(self)) { + if (client) { + if (state == Requester) { activatedStream = streamHost; tryActivation(); - } - else + } else checkForActivation(); - } - else { + } else { #ifdef S5B_DEBUG - qDebug("S5BManager::Item %s claims to have connected to us, but we don't see this\n", qPrintable(peer.full())); + qDebug("S5BManager::Item %s claims to have connected to us, but we don't see this\n", + qPrintable(peer.full())); #endif resetConnection(); - error(ErrWrongHost); + emit error(ErrWrongHost); } - } - else if(streamHost.compare(proxy.jid())) { + } else if (streamHost.compare(proxy.jid())) { // toss out any direct incoming, since it won't be used delete client; - client = 0; + client = nullptr; allowIncoming = false; #ifdef S5B_DEBUG @@ -1377,34 +1273,32 @@ void S5BManager::Item::jt_finished() list += proxy; QPointer self = this; - proxyConnect(); - if(!self) + emit proxyConnect(); + if (!self) return; proxy_conn->start(this->self, list, key, udp, 30); - } - else { + } else { #ifdef S5B_DEBUG - qDebug("S5BManager::Item %s claims to have connected to a streamhost we never offered\n", qPrintable(peer.full())); + qDebug("S5BManager::Item %s claims to have connected to a streamhost we never offered\n", + qPrintable(peer.full())); #endif resetConnection(); - error(ErrWrongHost); + emit error(ErrWrongHost); } - } - else { + } else { #ifdef S5B_DEBUG qDebug("S5BManager::Item %s [%s] error\n", qPrintable(peer.full()), qPrintable(sid)); #endif remoteFailed = true; - statusCode = j->statusCode(); + statusCode = j->statusCode(); - if(lateProxy) { - if(!conn) + if (lateProxy) { + if (!conn) doIncoming(); - } - else { + } else { // if connSuccess is true at this point, then we're a Target - if(connSuccess) + if (connSuccess) checkForActivation(); else checkFailure(); @@ -1414,17 +1308,16 @@ void S5BManager::Item::jt_finished() void S5BManager::Item::conn_result(bool b) { - if(b) { - SocksClient *sc = conn->takeClient(); - SocksUDP *sc_udp = conn->takeUDP(); - StreamHost h = conn->streamHostUsed(); + if (b) { + SocksClient *sc = conn->takeClient(); + SocksUDP *sc_udp = conn->takeUDP(); + StreamHost h = conn->streamHostUsed(); delete conn; - conn = 0; + conn = nullptr; connSuccess = true; #ifdef S5B_DEBUG - qDebug("S5BManager::Item: %s [%s] successful outgoing connection\n", - qPrintable(peer.full()), qPrintable(sid)); + qDebug("S5BManager::Item: %s [%s] successful outgoing connection\n", qPrintable(peer.full()), qPrintable(sid)); #endif connect(sc, SIGNAL(readyRead()), SLOT(sc_readyRead())); @@ -1437,32 +1330,29 @@ void S5BManager::Item::conn_result(bool b) lateProxy = false; // if requester, run with this one - if(state == Requester) { + if (state == Requester) { // if we had an incoming one, toss it delete client_udp; client_udp = sc_udp; delete client; - client = sc; - allowIncoming = false; + client = sc; + allowIncoming = false; activatedStream = peer; tryActivation(); - } - else { + } else { client_out_udp = sc_udp; - client_out = sc; + client_out = sc; checkForActivation(); } - } - else { + } else { delete conn; - conn = 0; + conn = nullptr; // if we delayed the proxies for later, try now - if(lateProxy) { - if(remoteFailed) + if (lateProxy) { + if (remoteFailed) doIncoming(); - } - else + } else doConnectError(); } } @@ -1472,17 +1362,17 @@ void S5BManager::Item::proxy_result(bool b) #ifdef S5B_DEBUG qDebug("proxy_result: %s\n", b ? "ok" : "fail"); #endif - if(b) { - SocksClient *sc = proxy_conn->takeClient(); - SocksUDP *sc_udp = proxy_conn->takeUDP(); + if (b) { + SocksClient *sc = proxy_conn->takeClient(); + SocksUDP *sc_udp = proxy_conn->takeUDP(); delete proxy_conn; - proxy_conn = 0; + proxy_conn = nullptr; connect(sc, SIGNAL(readyRead()), SLOT(sc_readyRead())); connect(sc, SIGNAL(bytesWritten(qint64)), SLOT(sc_bytesWritten(qint64))); connect(sc, SIGNAL(error(int)), SLOT(sc_error(int))); - client = sc; + client = sc; client_udp = sc_udp; // activate @@ -1493,34 +1383,31 @@ void S5BManager::Item::proxy_result(bool b) connect(proxy_task, SIGNAL(finished()), SLOT(proxy_finished())); proxy_task->requestActivation(proxy.jid(), sid, peer); proxy_task->go(true); - } - else { + } else { delete proxy_conn; - proxy_conn = 0; + proxy_conn = nullptr; resetConnection(); - error(ErrProxy); + emit error(ErrProxy); } } void S5BManager::Item::proxy_finished() { - JT_S5B *j = proxy_task; - proxy_task = 0; + JT_S5B *j = proxy_task; + proxy_task = nullptr; - if(j->success()) { + if (j->success()) { #ifdef S5B_DEBUG qDebug("proxy stream activated\n"); #endif - if(state == Requester) { + if (state == Requester) { activatedStream = proxy.jid(); tryActivation(); - } - else + } else checkForActivation(); - } - else { + } else { resetConnection(); - error(ErrProxy); + emit error(ErrProxy); } } @@ -1530,7 +1417,7 @@ void S5BManager::Item::sc_readyRead() qDebug("sc_readyRead\n"); #endif // only targets check for activation, and only should do it if there is no pending outgoing iq-set - if(state == Target && !task && !proxy_task) + if (state == Target && !task && !proxy_task) checkForActivation(); } @@ -1549,14 +1436,13 @@ void S5BManager::Item::sc_error(int) qDebug("sc_error\n"); #endif resetConnection(); - error(ErrConnect); + emit error(ErrConnect); } void S5BManager::Item::doConnectError() { localFailed = true; - m->doError(peer, in_id, Stanza::Error::RemoteServerNotFound, - "Could not connect to given hosts"); + m->doError(peer, in_id, Stanza::Error::RemoteServerNotFound, "Could not connect to given hosts"); checkFailure(); } @@ -1565,32 +1451,30 @@ void S5BManager::Item::tryActivation() #ifdef S5B_DEBUG qDebug("tryActivation\n"); #endif - if(activated) { + if (activated) { #ifdef S5B_DEBUG qDebug("already activated !?\n"); #endif return; } - if(targetMode == NotFast) { + if (targetMode == NotFast) { #ifdef S5B_DEBUG qDebug("tryActivation: NotFast\n"); #endif // nothing to activate, we're done finished(); - } - else if(targetMode == Fast) { + } else if (targetMode == Fast) { // with fast mode, we don't wait for the iq reply, so delete the task (if any) delete task; - task = 0; + task = nullptr; activated = true; // if udp, activate using special stanza - if(udp) { + if (udp) { m->doActivate(peer, sid, activatedStream); - } - else { + } else { #ifdef S5B_DEBUG qDebug("sending extra CR\n"); #endif @@ -1602,31 +1486,31 @@ void S5BManager::Item::tryActivation() void S5BManager::Item::checkForActivation() { - QList clientList; - if(client) + QList clientList; + if (client) clientList.append(client); - if(client_out) + if (client_out) clientList.append(client_out); - foreach(SocksClient *sc, clientList) { + for (SocksClient *sc : clientList) { #ifdef S5B_DEBUG qDebug("checking for activation\n"); #endif - if(fast) { + if (fast) { bool ok = false; - if(udp) { - if((sc == client_out && activatedStream.compare(self)) || (sc == client && !activatedStream.compare(self))) { + if (udp) { + if ((sc == client_out && activatedStream.compare(self)) + || (sc == client && !activatedStream.compare(self))) { clientList.removeAll(sc); ok = true; } - } - else { + } else { #ifdef S5B_DEBUG qDebug("need CR\n"); #endif - if(sc->bytesAvailable() >= 1) { + if (sc->bytesAvailable() >= 1) { clientList.removeAll(sc); char c; - if(!sc->getChar(&c) || c != '\r') { + if (!sc->getChar(&c) || c != '\r') { delete sc; // FIXME breaks S5BManager::Item destructor? return; } @@ -1634,34 +1518,32 @@ void S5BManager::Item::checkForActivation() } } - if(ok) { - SocksUDP *sc_udp = 0; - if(sc == client) { + if (ok) { + SocksUDP *sc_udp = nullptr; + if (sc == client) { delete client_out_udp; - client_out_udp = 0; - sc_udp = client_udp; - } - else if(sc == client_out) { + client_out_udp = nullptr; + sc_udp = client_udp; + } else if (sc == client_out) { delete client_udp; - client_udp = 0; - sc_udp = client_out_udp; + client_udp = nullptr; + sc_udp = client_out_udp; } sc->disconnect(this); while (!clientList.isEmpty()) { delete clientList.takeFirst(); } - client = sc; - client_out = 0; + client = sc; + client_out = nullptr; client_udp = sc_udp; - activated = true; + activated = true; #ifdef S5B_DEBUG qDebug("activation success\n"); #endif break; } - } - else { + } else { #ifdef S5B_DEBUG qDebug("not fast mode, no need to wait for anything\n"); #endif @@ -1670,50 +1552,47 @@ void S5BManager::Item::checkForActivation() while (!clientList.isEmpty()) { delete clientList.takeFirst(); } - client = sc; - client_out = 0; - activated = true; + client = sc; + client_out = nullptr; + activated = true; break; } } - if(activated) { + if (activated) { finished(); - } - else { + } else { // only emit waitingForActivation if there is nothing left to do - if((connSuccess || localFailed) && !proxy_task && !proxy_conn) - waitingForActivation(); + if ((connSuccess || localFailed) && !proxy_task && !proxy_conn) + emit waitingForActivation(); } } void S5BManager::Item::checkFailure() { bool failed = false; - if(state == Requester) { - if(remoteFailed) { - if((localFailed && targetMode == Fast) || targetMode == NotFast) + if (state == Requester) { + if (remoteFailed) { + if ((localFailed && targetMode == Fast) || targetMode == NotFast) failed = true; } - } - else { - if(localFailed) { - if((remoteFailed && fast) || !fast) + } else { + if (localFailed) { + if ((remoteFailed && fast) || !fast) failed = true; } } - if(failed) { - if(state == Requester) { + if (failed) { + if (state == Requester) { resetConnection(); - if(statusCode == 404) - error(ErrConnect); + if (statusCode == 404) + emit error(ErrConnect); else - error(ErrRefused); - } - else { + emit error(ErrRefused); + } else { resetConnection(); - error(ErrConnect); + emit error(ErrConnect); } } } @@ -1731,43 +1610,30 @@ void S5BManager::Item::finished() //---------------------------------------------------------------------------- // S5BConnector //---------------------------------------------------------------------------- -class S5BConnector::Item : public QObject -{ +class S5BConnector::Item : public QObject { Q_OBJECT public: SocksClient *client; - SocksUDP *client_udp; - StreamHost host; - QString key; - bool udp; - int udp_tries; - QTimer t; - Jid jid; - - Item(const Jid &self, const StreamHost &_host, const QString &_key, bool _udp) - : QObject(0) - , client(new SocksClient) - , client_udp(0) - , host(_host) - , key(_key) - , udp(_udp) - , udp_tries(0) - , jid(self) + SocksUDP *client_udp; + StreamHost host; + QString key; + bool udp; + int udp_tries; + QTimer t; + Jid jid; + + Item(const Jid &self, const StreamHost &_host, const QString &_key, bool _udp) : + QObject(nullptr), client(new SocksClient), client_udp(nullptr), host(_host), key(_key), udp(_udp), udp_tries(0), + jid(self) { connect(client, SIGNAL(connected()), SLOT(sc_connected())); connect(client, SIGNAL(error(int)), SLOT(sc_error(int))); connect(&t, SIGNAL(timeout()), SLOT(trySendUDP())); } - ~Item() - { - cleanup(); - } + ~Item() { cleanup(); } - void start() - { - client->connectToHost(host.host(), host.port(), key, 0, udp); - } + void start() { client->connectToHost(host.host(), host.port(), key, 0, udp); } void udpSuccess() { @@ -1783,10 +1649,10 @@ private slots: void sc_connected() { // if udp, need to send init packet before we are good - if(udp) { + if (udp) { // port 1 is init client_udp = client->createUDP(key, 1, client->peerAddress(), client->peerPort()); - udp_tries = 0; + udp_tries = 0; t.start(5000); trySendUDP(); return; @@ -1801,15 +1667,15 @@ private slots: qDebug("S5BConnector[%s]: error\n", qPrintable(host.host())); #endif cleanup(); - result(false); + emit result(false); } void trySendUDP() { - if(udp_tries == 5) { + if (udp_tries == 5) { t.stop(); cleanup(); - result(false); + emit result(false); return; } @@ -1823,9 +1689,9 @@ private slots: void cleanup() { delete client_udp; - client_udp = 0; + client_udp = nullptr; delete client; - client = 0; + client = nullptr; } void success() @@ -1834,27 +1700,25 @@ private slots: qDebug("S5BConnector[%s]: success\n", qPrintable(host.host())); #endif client->disconnect(this); - result(true); + emit result(true); } }; -class S5BConnector::Private -{ +class S5BConnector::Private { public: - SocksClient *active; - SocksUDP *active_udp; - QList itemList; - QString key; - StreamHost activeHost; - QTimer t; + SocksClient *active; + SocksUDP *active_udp; + QList itemList; + QString key; + StreamHost activeHost; + QTimer t; }; -S5BConnector::S5BConnector(QObject *parent) -:QObject(parent) +S5BConnector::S5BConnector(QObject *parent) : QObject(parent) { - d = new Private; - d->active = 0; - d->active_udp = 0; + d = new Private; + d->active = nullptr; + d->active_udp = nullptr; connect(&d->t, SIGNAL(timeout()), SLOT(t_timeout())); } @@ -1868,9 +1732,9 @@ void S5BConnector::resetConnection() { d->t.stop(); delete d->active_udp; - d->active_udp = 0; + d->active_udp = nullptr; delete d->active; - d->active = 0; + d->active = nullptr; while (!d->itemList.empty()) { delete d->itemList.takeFirst(); } @@ -1883,8 +1747,8 @@ void S5BConnector::start(const Jid &self, const StreamHostList &hosts, const QSt #ifdef S5B_DEBUG qDebug("S5BConnector: starting [%p]!\n", this); #endif - for(StreamHostList::ConstIterator it = hosts.begin(); it != hosts.end(); ++it) { - Item *i = new Item(self, *it, key, udp); + for (const auto &host : hosts) { + Item *i = new Item(self, host, key, udp); connect(i, SIGNAL(result(bool)), SLOT(item_result(bool))); d->itemList.append(i); i->start(); @@ -1895,30 +1759,27 @@ void S5BConnector::start(const Jid &self, const StreamHostList &hosts, const QSt SocksClient *S5BConnector::takeClient() { SocksClient *c = d->active; - d->active = 0; + d->active = nullptr; return c; } SocksUDP *S5BConnector::takeUDP() { - SocksUDP *c = d->active_udp; - d->active_udp = 0; + SocksUDP *c = d->active_udp; + d->active_udp = nullptr; return c; } -StreamHost S5BConnector::streamHostUsed() const -{ - return d->activeHost; -} +StreamHost S5BConnector::streamHostUsed() const { return d->activeHost; } void S5BConnector::item_result(bool b) { Item *i = static_cast(sender()); - if(b) { - d->active = i->client; - i->client = 0; + if (b) { + d->active = i->client; + i->client = nullptr; d->active_udp = i->client_udp; - i->client_udp = 0; + i->client_udp = nullptr; d->activeHost = i->host; while (!d->itemList.isEmpty()) { delete d->itemList.takeFirst(); @@ -1928,11 +1789,10 @@ void S5BConnector::item_result(bool b) qDebug("S5BConnector: complete! [%p]\n", this); #endif emit result(true); - } - else { + } else { d->itemList.removeAll(i); delete i; - if(d->itemList.isEmpty()) { + if (d->itemList.isEmpty()) { d->t.stop(); #ifdef S5B_DEBUG qDebug("S5BConnector: failed! [%p]\n", this); @@ -1948,312 +1808,70 @@ void S5BConnector::t_timeout() #ifdef S5B_DEBUG qDebug("S5BConnector: failed! (timeout)\n"); #endif - result(false); + emit result(false); } void S5BConnector::man_udpSuccess(const Jid &streamHost) { // was anyone sending to this streamhost? - foreach(Item *i, d->itemList) { - if(i->host.jid().compare(streamHost) && i->client_udp) { + for (Item *i : std::as_const(d->itemList)) { + if (i->host.jid().compare(streamHost) && i->client_udp) { i->udpSuccess(); return; } } } -//---------------------------------------------------------------------------- -// S5BServer -//---------------------------------------------------------------------------- -class S5BServer::Item : public QObject -{ - Q_OBJECT -public: - SocksClient *client; - QString host; - QTimer expire; - - Item(SocksClient *c) : QObject(0) - { - client = c; - connect(client, SIGNAL(incomingMethods(int)), SLOT(sc_incomingMethods(int))); - connect(client, SIGNAL(incomingConnectRequest(QString,int)), SLOT(sc_incomingConnectRequest(QString,int))); - connect(client, SIGNAL(error(int)), SLOT(sc_error(int))); - - connect(&expire, SIGNAL(timeout()), SLOT(doError())); - resetExpiration(); - } - - ~Item() - { - delete client; - } - - void resetExpiration() - { - expire.start(30000); - } - -signals: - void result(bool); - -private slots: - void doError() - { - expire.stop(); - delete client; - client = 0; - result(false); - } - - void sc_incomingMethods(int m) - { - if(m & SocksClient::AuthNone) - client->chooseMethod(SocksClient::AuthNone); - else - doError(); - } - - void sc_incomingConnectRequest(const QString &_host, int port) - { - if(port == 0) { - host = _host; - client->disconnect(this); - emit result(true); - } - else - doError(); - } - - void sc_error(int) - { - doError(); - } -}; - -class S5BServer::Private -{ -public: - SocksServer serv; - QStringList hostList; - QList manList; - QList jingleManagerList; - QList itemList; -}; - -S5BServer::S5BServer(QObject *parent) -:QObject(parent) -{ - d = new Private; - connect(&d->serv, SIGNAL(incomingReady()), SLOT(ss_incomingReady())); - connect(&d->serv, SIGNAL(incomingUDP(QString,int,QHostAddress,int,QByteArray)), SLOT(ss_incomingUDP(QString,int,QHostAddress,int,QByteArray))); -} - -S5BServer::~S5BServer() -{ - unlinkAll(); - delete d; -} - -bool S5BServer::isActive() const -{ - return d->serv.isActive(); -} - -bool S5BServer::start(int port) -{ - d->serv.stop(); - //return d->serv.listen(port, true); - return d->serv.listen(port); -} - -void S5BServer::stop() -{ - d->serv.stop(); -} - -void S5BServer::setHostList(const QStringList &list) -{ - d->hostList = list; -} - -QStringList S5BServer::hostList() const -{ - return d->hostList; -} - -int S5BServer::port() const -{ - return d->serv.port(); -} - -void S5BServer::ss_incomingReady() -{ - Item *i = new Item(d->serv.takeIncoming()); -#ifdef S5B_DEBUG - qDebug("S5BServer: incoming connection from %s:%d\n", qPrintable(i->client->peerAddress().toString()), i->client->peerPort()); -#endif - connect(i, SIGNAL(result(bool)), SLOT(item_result(bool))); - d->itemList.append(i); -} - -void S5BServer::ss_incomingUDP(const QString &host, int port, const QHostAddress &addr, int sourcePort, const QByteArray &data) -{ - if(port != 0 && port != 1) - return; - - // TODO check jingle managers too - foreach(S5BManager* m, d->manList) { - if(m->srv_ownsHash(host)) { - m->srv_incomingUDP(port == 1 ? true : false, addr, sourcePort, host, data); - return; - } - } -} - -void S5BServer::item_result(bool b) -{ - Item *i = static_cast(sender()); -#ifdef S5B_DEBUG - qDebug("S5BServer item result: %d\n", b); -#endif - if(!b) { - d->itemList.removeAll(i); - delete i; - return; - } - - SocksClient *c = i->client; - i->client = 0; - QString key = i->host; - d->itemList.removeAll(i); - delete i; - - - for (Jingle::S5B::Manager *m: d->jingleManagerList) { - if (m->incomingConnection(c, key)) { - return; - } - } - - // find the appropriate manager for this incoming connection - foreach(S5BManager *m, d->manList) { - if(m->srv_ownsHash(key)) { - m->srv_incomingReady(c, key); - return; - } - } - -#ifdef S5B_DEBUG - qDebug("S5BServer item result: unknown hash [%s]\n", qPrintable(key)); -#endif - - // throw it away - delete c; -} - -void S5BServer::link(S5BManager *m) -{ - d->manList.append(m); -} - -void S5BServer::unlink(S5BManager *m) -{ - d->manList.removeAll(m); -} - -void S5BServer::link(Jingle::S5B::Manager *m) -{ - d->jingleManagerList.append(m); -} - -void S5BServer::unlink(Jingle::S5B::Manager *m) -{ - d->jingleManagerList.removeAll(m); -} - -void S5BServer::unlinkAll() -{ - auto jl = d->jingleManagerList; - d->jingleManagerList.clear(); // clear early for setServer optimization - for(Jingle::S5B::Manager *m : jl) { - m->setServer(nullptr); - } - - foreach(S5BManager *m, d->manList) { - m->srv_unlink(); - } - d->manList.clear(); -} - -const QList & S5BServer::managerList() const -{ - return d->manList; -} - -void S5BServer::writeUDP(const QHostAddress &addr, int port, const QByteArray &data) -{ - d->serv.writeUDP(addr, port, data); -} - //---------------------------------------------------------------------------- // JT_S5B //---------------------------------------------------------------------------- -class JT_S5B::Private -{ +class JT_S5B::Private { public: QDomElement iq; - Jid to; - Jid streamHost; - StreamHost proxyInfo; - int mode; - QTimer t; + Jid to; + Jid streamHost; + StreamHost proxyInfo; + int mode; + QTimer t; }; -JT_S5B::JT_S5B(Task *parent) -:Task(parent) +JT_S5B::JT_S5B(Task *parent) : Task(parent) { - d = new Private; + d = new Private; d->mode = -1; connect(&d->t, SIGNAL(timeout()), SLOT(t_timeout())); } -JT_S5B::~JT_S5B() -{ - delete d; -} +JT_S5B::~JT_S5B() { delete d; } -void JT_S5B::request(const Jid &to, const QString &sid, const QString &dstaddr, - const StreamHostList &hosts, bool fast, bool udp) +void JT_S5B::request(const Jid &to, const QString &sid, const QString &dstaddr, const StreamHostList &hosts, bool fast, + bool udp) { d->mode = 0; QDomElement iq; - d->to = to; - iq = createIQ(doc(), "set", to.full(), id()); - QDomElement query = doc()->createElement("query"); - query.setAttribute("xmlns", S5B_NS); + d->to = to; + iq = createIQ(doc(), "set", to.full(), id()); + QDomElement query = doc()->createElementNS(S5B_NS, "query"); query.setAttribute("sid", sid); if (!client()->groupChatNick(to.domain(), to.node()).isEmpty()) { query.setAttribute("dstaddr", dstaddr); // special case for muc as in xep-0065rc3 } - query.setAttribute("mode", udp ? "udp" : "tcp" ); + query.setAttribute("mode", udp ? "udp" : "tcp"); iq.appendChild(query); - for(StreamHostList::ConstIterator it = hosts.begin(); it != hosts.end(); ++it) { + for (const auto &host : hosts) { QDomElement shost = doc()->createElement("streamhost"); - shost.setAttribute("jid", (*it).jid().full()); - shost.setAttribute("host", (*it).host()); - shost.setAttribute("port", QString::number((*it).port())); - if((*it).isProxy()) { - QDomElement p = doc()->createElement("proxy"); - p.setAttribute("xmlns", "http://affinix.com/jabber/stream"); + shost.setAttribute("jid", host.jid().full()); + shost.setAttribute("host", host.host()); + shost.setAttribute("port", QString::number(host.port())); + if (host.isProxy()) { + QDomElement p = doc()->createElementNS("http://affinix.com/jabber/stream", "proxy"); shost.appendChild(p); } query.appendChild(shost); } - if(fast) { - QDomElement e = doc()->createElement("fast"); - e.setAttribute("xmlns", "http://affinix.com/jabber/stream"); + if (fast) { + QDomElement e = doc()->createElementNS("http://affinix.com/jabber/stream", "fast"); query.appendChild(e); } d->iq = iq; @@ -2264,10 +1882,9 @@ void JT_S5B::requestProxyInfo(const Jid &to) d->mode = 1; QDomElement iq; - d->to = to; - iq = createIQ(doc(), "get", to.full(), id()); - QDomElement query = doc()->createElement("query"); - query.setAttribute("xmlns", S5B_NS); + d->to = to; + iq = createIQ(doc(), "get", to.full(), id()); + QDomElement query = doc()->createElementNS(S5B_NS, "query"); iq.appendChild(query); d->iq = iq; } @@ -2277,10 +1894,9 @@ void JT_S5B::requestActivation(const Jid &to, const QString &sid, const Jid &tar d->mode = 2; QDomElement iq; - d->to = to; - iq = createIQ(doc(), "set", to.full(), id()); - QDomElement query = doc()->createElement("query"); - query.setAttribute("xmlns", S5B_NS); + d->to = to; + iq = createIQ(doc(), "set", to.full(), id()); + QDomElement query = doc()->createElementNS(S5B_NS, "query"); query.setAttribute("sid", sid); iq.appendChild(query); QDomElement act = doc()->createElement("activate"); @@ -2291,49 +1907,45 @@ void JT_S5B::requestActivation(const Jid &to, const QString &sid, const Jid &tar void JT_S5B::onGo() { - if(d->mode == 1) { + if (d->mode == 1) { d->t.setSingleShot(true); d->t.start(15000); } send(d->iq); } -void JT_S5B::onDisconnect() -{ - d->t.stop(); -} +void JT_S5B::onDisconnect() { d->t.stop(); } bool JT_S5B::take(const QDomElement &x) { - if(d->mode == -1) + if (d->mode == -1) return false; - if(!iqVerify(x, d->to, id())) + if (!iqVerify(x, d->to, id())) return false; d->t.stop(); - if(x.attribute("type") == "result") { + if (x.attribute("type") == "result") { QDomElement q = queryTag(x); - if(d->mode == 0) { + if (d->mode == 0) { d->streamHost = ""; - if(!q.isNull()) { + if (!q.isNull()) { QDomElement shost = q.elementsByTagName("streamhost-used").item(0).toElement(); - if(!shost.isNull()) + if (!shost.isNull()) d->streamHost = shost.attribute("jid"); } setSuccess(); - } - else if(d->mode == 1) { - if(!q.isNull()) { + } else if (d->mode == 1) { + if (!q.isNull()) { QDomElement shost = q.elementsByTagName("streamhost").item(0).toElement(); - if(!shost.isNull()) { + if (!shost.isNull()) { Jid j = shost.attribute("jid"); - if(j.isValid()) { + if (j.isValid()) { QString host = shost.attribute("host"); - if(!host.isEmpty()) { - int port = shost.attribute("port").toInt(); + if (!host.isEmpty()) { + int port = shost.attribute("port").toInt(); StreamHost h; h.setJid(j); h.setHost(host); @@ -2346,12 +1958,10 @@ bool JT_S5B::take(const QDomElement &x) } setSuccess(); - } - else { + } else { setSuccess(); } - } - else { + } else { setError(x); } @@ -2364,77 +1974,63 @@ void JT_S5B::t_timeout() setError(500, "Timed out"); } -Jid JT_S5B::streamHostUsed() const -{ - return d->streamHost; -} +Jid JT_S5B::streamHostUsed() const { return d->streamHost; } -StreamHost JT_S5B::proxyInfo() const -{ - return d->proxyInfo; -} +StreamHost JT_S5B::proxyInfo() const { return d->proxyInfo; } //---------------------------------------------------------------------------- // JT_PushS5B //---------------------------------------------------------------------------- -JT_PushS5B::JT_PushS5B(Task *parent) -:Task(parent) -{ -} +JT_PushS5B::JT_PushS5B(Task *parent) : Task(parent) { } -JT_PushS5B::~JT_PushS5B() -{ -} +JT_PushS5B::~JT_PushS5B() { } -int JT_PushS5B::priority() const -{ - return 1; -} +int JT_PushS5B::priority() const { return 1; } bool JT_PushS5B::take(const QDomElement &e) { // look for udpsuccess - if(e.tagName() == "message") { + if (e.tagName() == "message") { QDomElement x = e.elementsByTagName("udpsuccess").item(0).toElement(); - if(!x.isNull() && x.attribute("xmlns") == S5B_NS) { - incomingUDPSuccess(Jid(x.attribute("from")), x.attribute("dstaddr")); + if (!x.isNull() && x.namespaceURI() == S5B_NS) { + emit incomingUDPSuccess(Jid(x.attribute("from")), x.attribute("dstaddr")); return true; } x = e.elementsByTagName("activate").item(0).toElement(); - if(!x.isNull() && x.attribute("xmlns") == "http://affinix.com/jabber/stream") { - incomingActivate(Jid(x.attribute("from")), x.attribute("sid"), Jid(x.attribute("jid"))); + if (!x.isNull() && x.namespaceURI() == "http://affinix.com/jabber/stream") { + emit incomingActivate(Jid(x.attribute("from")), x.attribute("sid"), Jid(x.attribute("jid"))); return true; } return false; } // must be an iq-set tag - if(e.tagName() != "iq") + if (e.tagName() != "iq") return false; - if(e.attribute("type") != "set") + if (e.attribute("type") != "set") return false; - if(queryNS(e) != S5B_NS) + if (queryNS(e) != S5B_NS) return false; - Jid from(e.attribute("from")); - QDomElement q = queryTag(e); - QString sid = q.attribute("sid"); + Jid from(e.attribute("from")); + QDomElement q = queryTag(e); + QString sid = q.attribute("sid"); StreamHostList hosts; - QDomNodeList nl = q.elementsByTagName("streamhost"); - for(int n = 0; n < nl.count(); ++n) { + QDomNodeList nl = q.elementsByTagName("streamhost"); + for (int n = 0; n < nl.count(); ++n) { QDomElement shost = nl.item(n).toElement(); - if(hosts.count() < MAXSTREAMHOSTS) { + if (hosts.count() < MAXSTREAMHOSTS) { Jid j = shost.attribute("jid"); - if(!j.isValid()) + if (!j.isValid()) continue; QString host = shost.attribute("host"); - if(host.isEmpty()) + if (host.isEmpty()) continue; - int port = shost.attribute("port").toInt(); - QDomElement p = shost.elementsByTagName("proxy").item(0).toElement(); - bool isProxy = false; - if(!p.isNull() && p.attribute("xmlns") == "http://affinix.com/jabber/stream") + int port = shost.attribute("port").toInt(); + QDomElement p = shost.elementsByTagName("proxy").item(0).toElement(); + bool isProxy = false; + if (!p.isNull() && p.namespaceURI() == "http://affinix.com/jabber/stream") isProxy = true; StreamHost h; @@ -2446,20 +2042,20 @@ bool JT_PushS5B::take(const QDomElement &e) } } - bool fast = false; + bool fast = false; QDomElement t; t = q.elementsByTagName("fast").item(0).toElement(); - if(!t.isNull() && t.attribute("xmlns") == "http://affinix.com/jabber/stream") + if (!t.isNull() && t.namespaceURI() == "http://affinix.com/jabber/stream") fast = true; S5BRequest r; - r.from = from; - r.id = e.attribute("id"); - r.sid = sid; + r.from = from; + r.id = e.attribute("id"); + r.sid = sid; r.dstaddr = q.attribute("dstaddr"); // special case for muc as in xep-0065rc3 - r.hosts = hosts; - r.fast = fast; - r.udp = q.attribute("mode") == "udp" ? true: false; + r.hosts = hosts; + r.fast = fast; + r.udp = q.attribute("mode") == "udp"; emit incoming(r); return true; @@ -2467,9 +2063,8 @@ bool JT_PushS5B::take(const QDomElement &e) void JT_PushS5B::respondSuccess(const Jid &to, const QString &id, const Jid &streamHost) { - QDomElement iq = createIQ(doc(), "result", to.full(), id); - QDomElement query = doc()->createElement("query"); - query.setAttribute("xmlns", S5B_NS); + QDomElement iq = createIQ(doc(), "result", to.full(), id); + QDomElement query = doc()->createElementNS(S5B_NS, "query"); iq.appendChild(query); QDomElement shost = doc()->createElement("streamhost-used"); shost.setAttribute("jid", streamHost.full()); @@ -2477,10 +2072,9 @@ void JT_PushS5B::respondSuccess(const Jid &to, const QString &id, const Jid &str send(iq); } -void JT_PushS5B::respondError(const Jid &to, const QString &id, - Stanza::Error::ErrorCond cond, const QString &str) +void JT_PushS5B::respondError(const Jid &to, const QString &id, Stanza::Error::ErrorCond cond, const QString &str) { - QDomElement iq = createIQ(doc(), "error", to.full(), id); + QDomElement iq = createIQ(doc(), "error", to.full(), id); Stanza::Error error(Stanza::Error::Cancel, cond, str); iq.appendChild(error.toXml(*client()->doc(), client()->stream().baseNS())); send(iq); @@ -2490,8 +2084,7 @@ void JT_PushS5B::sendUDPSuccess(const Jid &to, const QString &dstaddr) { QDomElement m = doc()->createElement("message"); m.setAttribute("to", to.full()); - QDomElement u = doc()->createElement("udpsuccess"); - u.setAttribute("xmlns", S5B_NS); + QDomElement u = doc()->createElementNS(S5B_NS, "udpsuccess"); u.setAttribute("dstaddr", dstaddr); m.appendChild(u); send(m); @@ -2501,8 +2094,7 @@ void JT_PushS5B::sendActivate(const Jid &to, const QString &sid, const Jid &stre { QDomElement m = doc()->createElement("message"); m.setAttribute("to", to.full()); - QDomElement act = doc()->createElement("activate"); - act.setAttribute("xmlns", "http://affinix.com/jabber/stream"); + QDomElement act = doc()->createElementNS("http://affinix.com/jabber/stream", "activate"); act.setAttribute("sid", sid); act.setAttribute("jid", streamHost.full()); m.appendChild(act); @@ -2515,49 +2107,143 @@ void JT_PushS5B::sendActivate(const Jid &to, const QString &sid, const Jid &stre StreamHost::StreamHost() { v_port = -1; - proxy = false; + proxy = false; } -const Jid & StreamHost::jid() const -{ - return j; -} +const Jid &StreamHost::jid() const { return j; } -const QString & StreamHost::host() const -{ - return v_host; -} +const QString &StreamHost::host() const { return v_host; } -int StreamHost::port() const -{ - return v_port; -} +int StreamHost::port() const { return v_port; } -bool StreamHost::isProxy() const -{ - return proxy; -} +bool StreamHost::isProxy() const { return proxy; } -void StreamHost::setJid(const Jid &_j) -{ - j = _j; -} +void StreamHost::setJid(const Jid &_j) { j = _j; } + +void StreamHost::setHost(const QString &host) { v_host = host; } + +void StreamHost::setPort(int port) { v_port = port; } + +void StreamHost::setIsProxy(bool b) { proxy = b; } + +//---------------------------------------------------------------------------- +// S5BServersProducer +//---------------------------------------------------------------------------- +TcpPortServer *S5BServersProducer::makeServer(QTcpServer *socket) { return new S5BServer(socket); } -void StreamHost::setHost(const QString &host) +//---------------------------------------------------------------------------- +// S5BIncomingConnection +//---------------------------------------------------------------------------- +class S5BIncomingConnection : public QObject { + Q_OBJECT +public: + SocksClient *client; + QString host; + QTimer expire; + + S5BIncomingConnection(SocksClient *c) : QObject(nullptr) + { + client = c; + connect(client, &SocksClient::incomingMethods, this, [this](int methods) { + if (methods & SocksClient::AuthNone) + client->chooseMethod(SocksClient::AuthNone); + else + doError(); + }); + connect(client, &SocksClient::incomingConnectRequest, this, [this](const QString &_host, int port) { + if (port == 0) { + host = _host; + client->disconnect(this); + expire.stop(); + emit result(true); + } else + doError(); + }); + connect(client, &SocksClient::error, this, [this]() { doError(); }); + connect(&expire, SIGNAL(timeout()), SLOT(doError())); + resetExpiration(); + } + + ~S5BIncomingConnection() { delete client; } + + void resetExpiration() { expire.start(30000); } + +signals: + void result(bool); + +private slots: + void doError() + { + expire.stop(); + delete client; + client = nullptr; + emit result(false); + } +}; + +//---------------------------------------------------------------------------- +// S5BServer +//---------------------------------------------------------------------------- +struct S5BServer::Private { + SocksServer serv; + QSet keys; +}; + +S5BServer::S5BServer(QTcpServer *serverSocket) : TcpPortServer(serverSocket), d(new Private) { - v_host = host; + d->serv.setServerSocket(serverSocket); + connect(&d->serv, &SocksServer::incomingReady, this, [this]() { + S5BIncomingConnection *inConn = new S5BIncomingConnection(d->serv.takeIncoming()); +#ifdef S5B_DEBUG + qDebug("S5BServer: incoming connection from %s:%d\n", qPrintable(inConn->client->peerAddress().toString()), + inConn->client->peerPort()); +#endif + connect(inConn, &S5BIncomingConnection::result, this, [this, inConn](bool success) { +#ifdef S5B_DEBUG + qDebug("S5BServer item result: %d\n", success); +#endif + if (!success) { + delete inConn; + return; + } + + SocksClient *c = inConn->client; + inConn->client = nullptr; + QString key = inConn->host; + delete inConn; + + emit incomingConnection(c, key); + if (!c->isOpen()) { + delete c; + } + }); + }); + connect(&d->serv, &SocksServer::incomingUDP, this, + [this](const QString &host, int port, const QHostAddress &addr, int sourcePort, const QByteArray &data) { + if (port != 0 && port != 1) + return; + bool isInit = port == 1; + emit incomingUdp(isInit, addr, sourcePort, host, data); + }); } -void StreamHost::setPort(int port) +S5BServer::~S5BServer() { - v_port = port; + // basically to make std::unique_ptr happy } -void StreamHost::setIsProxy(bool b) +void S5BServer::writeUDP(const QHostAddress &addr, quint16 port, const QByteArray &data) { - proxy = b; + d->serv.writeUDP(addr, port, data); } -} +bool S5BServer::isActive() const { return d->serv.isActive(); } + +bool S5BServer::hasKey(const QString &key) { return d->keys.contains(key); } + +void S5BServer::registerKey(const QString &key) { d->keys.insert(key); } + +void S5BServer::unregisterKey(const QString &key) { d->keys.remove(key); } +} // namespace XMPP #include "s5b.moc" diff --git a/src/xmpp/xmpp-im/s5b.h b/src/xmpp/xmpp-im/s5b.h index 1cd62515..5fff63b4 100644 --- a/src/xmpp/xmpp-im/s5b.h +++ b/src/xmpp/xmpp-im/s5b.h @@ -20,341 +20,316 @@ #ifndef XMPP_S5B_H #define XMPP_S5B_H -#include -#include -#include - -#include "bytestream.h" -#include "xmpp_bytestream.h" +#include "iris/bytestream.h" +#include "iris/tcpportreserver.h" +#include "iris/xmpp_bytestream.h" +#include "iris/xmpp_stanza.h" +#include "iris/xmpp_task.h" #include "xmpp/jid/jid.h" -#include "xmpp_task.h" -#include "xmpp_stanza.h" + +#include +#include +#include class SocksClient; class SocksUDP; -namespace XMPP -{ - namespace Jingle { - namespace S5B { - class Manager; - } - } - - class StreamHost; - class Client; - class S5BConnection; - class S5BManager; - class S5BServer; - struct S5BRequest; - typedef QList StreamHostList; - typedef QList S5BConnectionList; - - class S5BDatagram - { - public: - S5BDatagram(); - S5BDatagram(int source, int dest, const QByteArray &data); - - int sourcePort() const; - int destPort() const; - QByteArray data() const; - - private: - int _source = 0; - int _dest = 0; - QByteArray _buf; - }; - - class S5BConnection : public BSConnection - { - Q_OBJECT - public: - enum Mode { Stream, Datagram }; - - ~S5BConnection(); - - Jid proxy() const; - void setProxy(const Jid &proxy); - - void connectToJid(const Jid &peer, const QString &sid, Mode m = Stream); - void connectToJid(const Jid &peer, const QString &sid) { - connectToJid(peer, sid, Stream); - } - - void accept(); - void close(); - - Jid peer() const; - QString sid() const; - BytestreamManager* manager() const; - bool isRemote() const; - Mode mode() const; - int state() const; - - qint64 bytesAvailable() const; - qint64 bytesToWrite() const; - - void writeDatagram(const S5BDatagram &); - S5BDatagram readDatagram(); - int datagramsAvailable() const; - - protected: - qint64 writeData(const char *data, qint64 maxSize); - qint64 readData(char * data, qint64 maxSize); - - signals: - void proxyQuery(); // querying proxy for streamhost information - void proxyResult(bool b); // query success / fail - void requesting(); // sent actual S5B request (requester only) - void accepted(); // target accepted (requester only - void tryingHosts(const StreamHostList &hosts); // currently connecting to these hosts - void proxyConnect(); // connecting to proxy - void waitingForActivation(); // waiting for activation (target only) - void connected(); // connection active - void datagramReady(); - - private slots: - void doPending(); - - void sc_connectionClosed(); - void sc_delayedCloseFinished(); - void sc_readyRead(); - void sc_bytesWritten(qint64); - void sc_error(int); - - void su_packetReady(const QByteArray &buf); - - private: - class Private; - Private *d; - - void resetConnection(bool clear=false); - void handleUDP(const QByteArray &buf); - void sendUDP(const QByteArray &buf); - - friend class S5BManager; - void man_waitForAccept(const S5BRequest &r); - void man_clientReady(SocksClient *, SocksUDP *); - void man_udpReady(const QByteArray &buf); - void man_failed(int); - S5BConnection(S5BManager *, QObject *parent=0); - }; - - class S5BManager : public BytestreamManager - { - Q_OBJECT - public: - S5BManager(Client *); - ~S5BManager(); - - static const char* ns(); - Client *client() const; - S5BServer *server() const; - void setServer(S5BServer *s); - - bool isAcceptableSID(const Jid &peer, const QString &sid) const; - - BSConnection *createConnection(); - S5BConnection *takeIncoming(); - - class Item; - class Entry; - - protected: - const char* sidPrefix() const; - - private slots: - void ps_incoming(const S5BRequest &req); - void ps_incomingUDPSuccess(const Jid &from, const QString &dstaddr); - void ps_incomingActivate(const Jid &from, const QString &sid, const Jid &streamHost); - void item_accepted(); - void item_tryingHosts(const StreamHostList &list); - void item_proxyConnect(); - void item_waitingForActivation(); - void item_connected(); - void item_error(int); - void query_finished(); - - private: - class Private; - Private *d; - - S5BConnection *findIncoming(const Jid &from, const QString &sid) const; - Entry *findEntry(S5BConnection *) const; - Entry *findEntry(Item *) const; - Entry *findEntryByHash(const QString &key) const; - Entry *findEntryBySID(const Jid &peer, const QString &sid) const; - Entry *findServerEntryByHash(const QString &key) const; - - void entryContinue(Entry *e); - void queryProxy(Entry *e); - bool targetShouldOfferProxy(Entry *e); - - friend class S5BConnection; - void con_connect(S5BConnection *); - void con_accept(S5BConnection *); - void con_reject(S5BConnection *); - void con_unlink(S5BConnection *); - void con_sendUDP(S5BConnection *, const QByteArray &buf); - - friend class S5BServer; - bool srv_ownsHash(const QString &key) const; - void srv_incomingReady(SocksClient *sc, const QString &key); - void srv_incomingUDP(bool init, const QHostAddress &addr, int port, const QString &key, const QByteArray &data); - void srv_unlink(); - - friend class Item; - void doSuccess(const Jid &peer, const QString &id, const Jid &streamHost); - void doError(const Jid &peer, const QString &id, Stanza::Error::ErrorCond, const QString &); - void doActivate(const Jid &peer, const QString &sid, const Jid &streamHost); - }; - - class S5BConnector : public QObject - { - Q_OBJECT - public: - S5BConnector(QObject *parent=0); - ~S5BConnector(); - - void resetConnection(); - void start(const Jid &self, const StreamHostList &hosts, const QString &key, bool udp, int timeout); - SocksClient *takeClient(); - SocksUDP *takeUDP(); - StreamHost streamHostUsed() const; - - class Item; - - signals: - void result(bool); - - private slots: - void item_result(bool); - void t_timeout(); - - private: - class Private; - Private *d; - - friend class S5BManager; - void man_udpSuccess(const Jid &streamHost); - }; - - // listens on a port for serving - class S5BServer : public QObject - { - Q_OBJECT - public: - S5BServer(QObject *par=0); - ~S5BServer(); - - bool isActive() const; - bool start(int port); - void stop(); - int port() const; - void setHostList(const QStringList &); - QStringList hostList() const; - - class Item; - - private slots: - void ss_incomingReady(); - void ss_incomingUDP(const QString &host, int port, const QHostAddress &addr, int sourcePort, const QByteArray &data); - void item_result(bool); - - private: - class Private; - Private *d; - - friend class S5BManager; - friend class Jingle::S5B::Manager; - void link(S5BManager *); - void unlink(S5BManager *); - void link(Jingle::S5B::Manager *m); - void unlink(Jingle::S5B::Manager *m); - void unlinkAll(); - const QList & managerList() const; - void writeUDP(const QHostAddress &addr, int port, const QByteArray &data); - }; - - class JT_S5B : public Task - { - Q_OBJECT - public: - JT_S5B(Task *); - ~JT_S5B(); - - void request(const Jid &to, const QString &sid, const QString &dstaddr, - const StreamHostList &hosts, bool fast, bool udp=false); - void requestProxyInfo(const Jid &to); - void requestActivation(const Jid &to, const QString &sid, const Jid &target); - - void onGo(); - void onDisconnect(); - bool take(const QDomElement &); - - Jid streamHostUsed() const; - StreamHost proxyInfo() const; - - private slots: - void t_timeout(); - - private: - class Private; - Private *d; - }; - - struct S5BRequest - { - Jid from; - QString id, sid, dstaddr; - StreamHostList hosts; - bool fast; - bool udp; - }; - class JT_PushS5B : public Task - { - Q_OBJECT - public: - JT_PushS5B(Task *); - ~JT_PushS5B(); - - int priority() const; - - void respondSuccess(const Jid &to, const QString &id, const Jid &streamHost); - void respondError(const Jid &to, const QString &id, - Stanza::Error::ErrorCond cond, const QString &str); - void sendUDPSuccess(const Jid &to, const QString &dstaddr); - void sendActivate(const Jid &to, const QString &sid, const Jid &streamHost); - - bool take(const QDomElement &); - - signals: - void incoming(const S5BRequest &req); - void incomingUDPSuccess(const Jid &from, const QString &dstaddr); - void incomingActivate(const Jid &from, const QString &sid, const Jid &streamHost); - }; - - class StreamHost - { - public: - StreamHost(); - - const Jid & jid() const; - const QString & host() const; - int port() const; - bool isProxy() const; - void setJid(const Jid &); - void setHost(const QString &); - void setPort(int); - void setIsProxy(bool); - - private: - Jid j; - QString v_host; - int v_port; - bool proxy; - }; -} - -#endif +namespace XMPP { +namespace Jingle { namespace S5B { + class Manager; +}} + +class Client; +class JT_PushS5B; +class S5BConnection; +class S5BManager; +class StreamHost; +class TcpPortReserver; +struct S5BRequest; +typedef QList StreamHostList; +typedef QList S5BConnectionList; + +class S5BDatagram { +public: + S5BDatagram(); + S5BDatagram(int source, int dest, const QByteArray &data); + + int sourcePort() const; + int destPort() const; + QByteArray data() const; + +private: + int _source = 0; + int _dest = 0; + QByteArray _buf; +}; + +class S5BConnection : public BSConnection { + Q_OBJECT +public: + enum Mode { Stream, Datagram }; + + ~S5BConnection(); + + Jid proxy() const; + void setProxy(const Jid &proxy); + + void connectToJid(const Jid &peer, const QString &sid, Mode m = Stream); + void connectToJid(const Jid &peer, const QString &sid) { connectToJid(peer, sid, Stream); } + + void accept(); + void close(); + + Jid peer() const; + QString sid() const; + BytestreamManager *manager() const; + bool isRemote() const; + Mode mode() const; + int state() const; + + qint64 bytesAvailable() const; + qint64 bytesToWrite() const; + + void writeDatagram(const S5BDatagram &); + S5BDatagram readDatagram(); + int datagramsAvailable() const; + +protected: + qint64 writeData(const char *data, qint64 maxSize); + qint64 readData(char *data, qint64 maxSize); + +signals: + void proxyQuery(); // querying proxy for streamhost information + void proxyResult(bool b); // query success / fail + void requesting(); // sent actual S5B request (requester only) + void accepted(); // target accepted (requester only + void tryingHosts(const StreamHostList &hosts); // currently connecting to these hosts + void proxyConnect(); // connecting to proxy + void waitingForActivation(); // waiting for activation (target only) + void connected(); // connection active + void datagramReady(); + +private slots: + void doPending(); + + void sc_connectionClosed(); + void sc_delayedCloseFinished(); + void sc_readyRead(); + void sc_bytesWritten(qint64); + void sc_error(int); + + void su_packetReady(const QByteArray &buf); + +private: + class Private; + Private *d; + + void resetConnection(bool clear = false); + void handleUDP(const QByteArray &buf); + void sendUDP(const QByteArray &buf); + + friend class S5BManager; + void man_waitForAccept(const S5BRequest &r); + void man_clientReady(SocksClient *, SocksUDP *); + void man_udpReady(const QByteArray &buf); + void man_failed(int); + S5BConnection(S5BManager *, QObject *parent = nullptr); +}; + +class S5BManager : public BytestreamManager { + Q_OBJECT +public: + S5BManager(Client *); + ~S5BManager(); + + static const char *ns(); + Client *client() const; + JT_PushS5B *jtPush() const; + + bool isAcceptableSID(const Jid &peer, const QString &sid) const; + + BSConnection *createConnection(); + S5BConnection *takeIncoming(); + + class Item; + class Entry; + +protected: + const char *sidPrefix() const; + +private slots: + void ps_incoming(const S5BRequest &req); + void ps_incomingUDPSuccess(const Jid &from, const QString &dstaddr); + void ps_incomingActivate(const Jid &from, const QString &sid, const Jid &streamHost); + void item_accepted(); + void item_tryingHosts(const StreamHostList &list); + void item_proxyConnect(); + void item_waitingForActivation(); + void item_connected(); + void item_error(int); + void query_finished(); + +private: + class Private; + Private *d; + + S5BConnection *findIncoming(const Jid &from, const QString &sid) const; + Entry *findEntry(S5BConnection *) const; + Entry *findEntry(Item *) const; + Entry *findEntryByHash(const QString &key) const; + Entry *findEntryBySID(const Jid &peer, const QString &sid) const; + + void entryContinue(Entry *e); + void queryProxy(Entry *e); + bool targetShouldOfferProxy(Entry *e); + + friend class S5BConnection; + void con_connect(S5BConnection *); + void con_accept(S5BConnection *); + void con_reject(S5BConnection *); + void con_unlink(S5BConnection *); + void con_sendUDP(S5BConnection *, const QByteArray &buf); + + friend class S5BServer; + bool srv_ownsHash(const QString &key) const; + void srv_incomingReady(SocksClient *sc, const QString &key); + void srv_incomingUDP(bool init, const QHostAddress &addr, int port, const QString &key, const QByteArray &data); + + friend class Item; + void doSuccess(const Jid &peer, const QString &id, const Jid &streamHost); + void doError(const Jid &peer, const QString &id, Stanza::Error::ErrorCond, const QString &); + void doActivate(const Jid &peer, const QString &sid, const Jid &streamHost); +}; + +class S5BConnector : public QObject { + Q_OBJECT +public: + S5BConnector(QObject *parent = nullptr); + ~S5BConnector(); + + void resetConnection(); + void start(const Jid &self, const StreamHostList &hosts, const QString &key, bool udp, int timeout); + SocksClient *takeClient(); + SocksUDP *takeUDP(); + StreamHost streamHostUsed() const; + + class Item; + +signals: + void result(bool); + +private slots: + void item_result(bool); + void t_timeout(); + +private: + class Private; + Private *d; + + friend class S5BManager; + void man_udpSuccess(const Jid &streamHost); +}; + +class S5BServersProducer : public TcpPortScope { +protected: + TcpPortServer *makeServer(QTcpServer *socket); // in fact returns S5BServer +}; + +class S5BServer : public TcpPortServer { + Q_OBJECT + +public: + S5BServer(QTcpServer *serverSocket); + ~S5BServer(); + + void writeUDP(const QHostAddress &addr, quint16 port, const QByteArray &data); + bool isActive() const; + bool hasKey(const QString &key); + void registerKey(const QString &key); + void unregisterKey(const QString &key); + +signals: + void incomingConnection(SocksClient *c, const QString &key); + void incomingUdp(bool isInit, const QHostAddress &addr, int sourcePort, const QString &key, const QByteArray &data); + +private: + struct Private; + std::unique_ptr d; +}; + +class JT_S5B : public Task { + Q_OBJECT +public: + JT_S5B(Task *); + ~JT_S5B(); + + void request(const Jid &to, const QString &sid, const QString &dstaddr, const StreamHostList &hosts, bool fast, + bool udp = false); + void requestProxyInfo(const Jid &to); + void requestActivation(const Jid &to, const QString &sid, const Jid &target); + + void onGo(); + void onDisconnect(); + bool take(const QDomElement &); + + Jid streamHostUsed() const; + StreamHost proxyInfo() const; + +private slots: + void t_timeout(); + +private: + class Private; + Private *d; +}; + +struct S5BRequest { + Jid from; + QString id, sid, dstaddr; + StreamHostList hosts; + bool fast; + bool udp; +}; +class JT_PushS5B : public Task { + Q_OBJECT +public: + JT_PushS5B(Task *); + ~JT_PushS5B(); + + int priority() const; + + void respondSuccess(const Jid &to, const QString &id, const Jid &streamHost); + void respondError(const Jid &to, const QString &id, Stanza::Error::ErrorCond cond, const QString &str); + void sendUDPSuccess(const Jid &to, const QString &dstaddr); + void sendActivate(const Jid &to, const QString &sid, const Jid &streamHost); + + bool take(const QDomElement &); + +signals: + void incoming(const S5BRequest &req); + void incomingUDPSuccess(const Jid &from, const QString &dstaddr); + void incomingActivate(const Jid &from, const QString &sid, const Jid &streamHost); +}; + +class StreamHost { +public: + StreamHost(); + + const Jid &jid() const; + const QString &host() const; + int port() const; + bool isProxy() const; + void setJid(const Jid &); + void setHost(const QString &); + void setPort(int); + void setIsProxy(bool); + +private: + Jid j; + QString v_host; + int v_port; + bool proxy; +}; +} // namespace XMPP + +#endif // XMPP_S5B_H diff --git a/src/xmpp/xmpp-im/stundisco.cpp b/src/xmpp/xmpp-im/stundisco.cpp new file mode 100644 index 00000000..0ef2405c --- /dev/null +++ b/src/xmpp/xmpp-im/stundisco.cpp @@ -0,0 +1,233 @@ +/* + * stundisco.cpp - STUN/TURN service discoverer + * Copyright (C) 2021 Sergey Ilinykh + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library. If not, see . + * + */ + +#include "stundisco.h" + +#include "netnames.h" +#include "xmpp_client.h" +#include "xmpp_externalservicediscovery.h" + +#include + +namespace XMPP { + +class StunDiscoMonitor : public AbstractStunDisco { + Q_OBJECT +public: + using StunList = QList; + + StunDiscoMonitor(StunDiscoManager *manager) : AbstractStunDisco(manager), manager_(manager) + { + auto extdisco = manager->client()->externalServiceDiscovery(); + connect(extdisco, &ExternalServiceDiscovery::serviceAdded, this, + [this](const ExternalServiceList &l) { addNewServices(l); }); + connect(extdisco, &ExternalServiceDiscovery::serviceModified, this, + [this](const ExternalServiceList &l) { modifyServices(l); }); + connect(extdisco, &ExternalServiceDiscovery::serviceDeleted, this, + [this](const ExternalServiceList &l) { deleteServices(l); }); + + QTimer::singleShot(0, this, &StunDiscoMonitor::disco); + } + + bool isDiscoInProgress() const { return inProgress_; } + +private: + static QStringList supportedServiceTypes() + { + return QStringList( + { QStringLiteral("stun"), QStringLiteral("stuns"), QStringLiteral("turn"), QStringLiteral("turns") }); + } + + void disco() + { + using namespace std::chrono_literals; + manager_->client()->externalServiceDiscovery()->services( + this, [this](auto const &list) { addNewServices(list); }, 5min, supportedServiceTypes()); + } + + void addNewServices(const ExternalServiceList &list) + { + StunList needCreds; + StunList needResolve; + for (auto const &l : list) { + auto s = std::make_shared(); + if (l->type.startsWith(QLatin1String("turn"))) + s->flags |= AbstractStunDisco::Relay; + else if (!l->type.startsWith(QLatin1String("stun"))) + continue; + if (l->type.endsWith(QLatin1Char('s'))) + s->flags |= AbstractStunDisco::Tls; + if (l->restricted) + s->flags |= AbstractStunDisco::Restricted; + s->transport = l->transport == QLatin1String("tcp") ? AbstractStunDisco::Tcp : AbstractStunDisco::Udp; + s->expires = l->expires; // it's definitely not expired. no need to check + s->port = l->port; + s->name = l->name; + s->username = l->username; + s->password = l->password; + s->host = l->host; + QHostAddress addr(l->host); + if (addr.isNull()) + needResolve.append(s); + else + s->addresses.append(addr); + if (l->restricted && s->password.isEmpty()) { + needCreds.append(s); + } else if (!s->addresses.isEmpty()) { + emit serviceAdded(s); + } else + pendingWork_.append(s); + } + bool final = needResolve.isEmpty() && needCreds.isEmpty(); + if (!needResolve.isEmpty()) + resolve(needResolve); + if (!needCreds.isEmpty()) + getCreds(needCreds); + if (final) { + emit discoFinished(); + deleteLater(); + } + } + + void modifyServices(const ExternalServiceList &list) + { + Q_UNUSED(list); + // TODO + } + void deleteServices(const ExternalServiceList &list) + { + Q_UNUSED(list); + // TODO + } + + static QString extType(AbstractStunDisco::Service::Ptr s) + { + bool isTls = s->flags & AbstractStunDisco::Tls; + return QLatin1String(s->flags & AbstractStunDisco::Relay ? (isTls ? "turns" : "turn") + : (isTls ? "stuns" : "stun")); + } + + void resolve(const StunList &services) + { + QSet names; + for (auto const &s : services) + names.insert(s->host); + + for (auto const &name : names) { + auto *dns = new NameResolver(this); + + connect(dns, &NameResolver::resultsReady, this, [this, name](const QList &records) { + QList addresses; + for (const auto &r : records) + addresses.append(r.address()); + setAddresses(name, addresses); + }); + connect(dns, &NameResolver::error, this, + [this, name](XMPP::NameResolver::Error) { setAddresses(name, {}); }); + + dns->start(name.toLatin1()); + } + } + + void setAddresses(const QString &host, const QList &addresses) + { + for (auto const &s : std::as_const(pendingWork_)) { + if (s->host == host && s->addresses.isEmpty()) { + if (addresses.isEmpty()) + s->expires = QDeadlineTimer(); // expired + else + s->addresses = addresses; + } + } + tryFinish(); + } + + void getCreds(const StunList &services) + { + QSet ids; + for (auto const &s : services) { + ExternalServiceId id; + id.host = s->host; + id.port = s->port; + id.type = extType(s); + ids.insert(id); + } + manager_->client()->externalServiceDiscovery()->credentials( + this, + [services, this](const ExternalServiceList &resolved) { + if (pendingWork_.isEmpty()) + return; // we are finsihed already + for (auto const &s : services) { + if (s->expires.hasExpired()) + continue; // ditch it. either really expired or failed on dns + QString etype = extType(s); + auto it + = std::find_if(resolved.begin(), resolved.end(), [&s, &etype](ExternalService::Ptr const &r) { + return s->host == r->host && etype == r->type && s->port == r->port; + }); + if (it == resolved.end()) { + s->expires = QDeadlineTimer(); // expired timer + qDebug("no creds from server for %s:%hu %s", qPrintable(s->host), s->port, qPrintable(etype)); + continue; // failed to get creds? weird + } + s->username = (*it)->username; + s->password = (*it)->password; + } + tryFinish(); + }, + ids); + } + + void tryFinish() + { + Q_ASSERT(!pendingWork_.isEmpty()); + auto it = pendingWork_.begin(); + while (it != pendingWork_.end()) { + auto s = **it; + if (s.expires.hasExpired()) { // was marked invalid or really expired + it = pendingWork_.erase(it); + continue; + } + if (s.addresses.isEmpty() || (s.flags & AbstractStunDisco::Restricted && s.password.isEmpty())) { + ++it; + continue; // in progress yet. + } + emit serviceAdded(*it); + it = pendingWork_.erase(it); + } + if (pendingWork_.isEmpty()) { + if (inProgress_) + emit discoFinished(); + } + } + + StunDiscoManager *manager_; + bool inProgress_ = false; // initial disco + QList pendingWork_; +}; + +StunDiscoManager::StunDiscoManager(Client *client) : QObject(client) { client_ = client; } + +StunDiscoManager::~StunDiscoManager() { } + +AbstractStunDisco *StunDiscoManager::createMonitor() { return new StunDiscoMonitor(this); } + +} // namespace XMPP + +#include "stundisco.moc" diff --git a/src/xmpp/base/randomnumbergenerator.cpp b/src/xmpp/xmpp-im/stundisco.h similarity index 56% rename from src/xmpp/base/randomnumbergenerator.cpp rename to src/xmpp/xmpp-im/stundisco.h index 4ac131b0..17d645da 100644 --- a/src/xmpp/base/randomnumbergenerator.cpp +++ b/src/xmpp/xmpp-im/stundisco.h @@ -1,5 +1,6 @@ /* - * Copyright (C) 2008 Remko Troncon + * stundisco.h - STUN/TURN service discoverer + * Copyright (C) 2021 Sergey Ilinykh * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -16,20 +17,31 @@ * */ -#include "xmpp/base/randomnumbergenerator.h" +#ifndef XMPP_STUNDISCO_H +#define XMPP_STUNDISCO_H -#include +#include "iceabstractstundisco.h" +#include "xmpp_client.h" + +#include namespace XMPP { -RandomNumberGenerator::~RandomNumberGenerator() -{ -} +class Client; + +class StunDiscoManager : public QObject { + Q_OBJECT +public: + StunDiscoManager(Client *client); + ~StunDiscoManager(); + + AbstractStunDisco *createMonitor(); + inline Client *client() const { return client_; } + +private: + Client *client_; +}; -double RandomNumberGenerator::generateNumberBetween(double a, double b) const -{ - assert(b > a); - return a + (generateNumber()/getMaximumGeneratedNumber())*(b-a); -} +} // namespace XMPP -} +#endif // XMPP_STUNDISCO_H diff --git a/src/xmpp/xmpp-im/types.cpp b/src/xmpp/xmpp-im/types.cpp index 9386a018..0d2e5c93 100644 --- a/src/xmpp/xmpp-im/types.cpp +++ b/src/xmpp/xmpp-im/types.cpp @@ -17,29 +17,28 @@ * */ -#include -#include - #include "im.h" -#include "xmpp_features.h" -#include "xmpp_xmlcommon.h" +#include "xmpp/xmpp-core/protocol.h" #include "xmpp_bitsofbinary.h" -#include "xmpp_ibb.h" #include "xmpp_captcha.h" -#include "xmpp_forwarding.h" #include "xmpp_carbons.h" -#include "protocol.h" -#include "xmpp/blake2/blake2qt.h" -#define NS_XML "http://www.w3.org/XML/1998/namespace" +#include "xmpp_features.h" +#include "xmpp_forwarding.h" +#include "xmpp_ibb.h" +#include "xmpp_reference.h" +#include "xmpp_xmlcommon.h" -namespace XMPP -{ +#include +#include +#define NS_XML "http://www.w3.org/XML/1998/namespace" + +namespace XMPP { +QString HASH_NS = QStringLiteral("urn:xmpp:hashes:2"); //---------------------------------------------------------------------------- // Url //---------------------------------------------------------------------------- -class Url::Private -{ +class Url::Private { public: QString url; QString desc; @@ -53,8 +52,8 @@ class Url::Private //! \sa setUrl() setDesc() Url::Url(const QString &url, const QString &desc) { - d = new Private; - d->url = url; + d = new Private; + d->url = url; d->desc = desc; } @@ -64,56 +63,41 @@ Url::Url(const QString &url, const QString &desc) //! \param Url - Url Object Url::Url(const Url &from) { - d = new Private; + d = new Private; *this = from; } //! \brief operator overloader needed for d pointer (Internel). -Url & Url::operator=(const Url &from) +Url &Url::operator=(const Url &from) { *d = *from.d; return *this; } //! \brief destroy Url object. -Url::~Url() -{ - delete d; -} +Url::~Url() { delete d; } //! \brief Get url information. //! //! Returns url information. -QString Url::url() const -{ - return d->url; -} +QString Url::url() const { return d->url; } //! \brief Get Description information. //! //! Returns desction of the URL. -QString Url::desc() const -{ - return d->desc; -} +QString Url::desc() const { return d->desc; } //! \brief Set Url information. //! //! Set url information. //! \param url - url string (eg: http://psi.affinix.com/) -void Url::setUrl(const QString &url) -{ - d->url = url; -} +void Url::setUrl(const QString &url) { d->url = url; } //! \brief Set Description information. //! //! Set description of the url. //! \param desc - description of url -void Url::setDesc(const QString &desc) -{ - d->desc = desc; -} +void Url::setDesc(const QString &desc) { d->desc = desc; } //---------------------------------------------------------------------------- // Address @@ -125,20 +109,11 @@ void Url::setDesc(const QString &desc) //! \param Type - type (default: Unknown) //! \param Jid - specify address (default: empty string) //! \sa setType() setJid() -Address::Address(Type type, const Jid & jid) - : v_jid(jid) - , v_delivered(false) - , v_type(type) -{ -} +Address::Address(Type type, const Jid &jid) : v_jid(jid), v_delivered(false), v_type(type) { } -Address::Address(const QDomElement& e) - : v_delivered(false) -{ - fromXml(e); -} +Address::Address(const QDomElement &e) : v_delivered(false) { fromXml(e); } -void Address::fromXml(const QDomElement& t) +void Address::fromXml(const QDomElement &t) { setJid(t.attribute("jid")); setUri(t.attribute("uri")); @@ -164,18 +139,18 @@ void Address::fromXml(const QDomElement& t) setType(OriginalTo); } -QDomElement Address::toXml(Stanza& s) const +QDomElement Address::toXml(Stanza &s) const { QDomElement e = s.createElement("http://jabber.org/protocol/address", "address"); - if(!jid().isEmpty()) + if (!jid().isEmpty()) e.setAttribute("jid", jid().full()); - if(!uri().isEmpty()) + if (!uri().isEmpty()) e.setAttribute("uri", uri()); - if(!node().isEmpty()) + if (!node().isEmpty()) e.setAttribute("node", node()); - if(!desc().isEmpty()) + if (!desc().isEmpty()) e.setAttribute("desc", desc()); - if(delivered()) + if (delivered()) e.setAttribute("delivered", "true"); switch (type()) { case To: @@ -212,301 +187,134 @@ QDomElement Address::toXml(Stanza& s) const //! \brief Get Jid information. //! //! Returns jid information. -const Jid& Address::jid() const -{ - return v_jid; -} +const Jid &Address::jid() const { return v_jid; } //! \brief Get Uri information. //! //! Returns desction of the Address. -const QString& Address::uri() const -{ - return v_uri; -} +const QString &Address::uri() const { return v_uri; } //! \brief Get Node information. //! //! Returns node of the Address. -const QString& Address::node() const -{ - return v_node; -} +const QString &Address::node() const { return v_node; } //! \brief Get Description information. //! //! Returns desction of the Address. -const QString& Address::desc() const -{ - return v_desc; -} +const QString &Address::desc() const { return v_desc; } //! \brief Get Delivered information. //! //! Returns delivered of the Address. -bool Address::delivered() const -{ - return v_delivered; -} +bool Address::delivered() const { return v_delivered; } //! \brief Get Type information. //! //! Returns type of the Address. -Address::Type Address::type() const -{ - return v_type; -} +Address::Type Address::type() const { return v_type; } //! \brief Set Address information. //! //! Set jid information. //! \param jid - jid -void Address::setJid(const Jid &jid) -{ - v_jid = jid; -} +void Address::setJid(const Jid &jid) { v_jid = jid; } //! \brief Set Address information. //! //! Set uri information. //! \param uri - url string (eg: http://psi.affinix.com/) -void Address::setUri(const QString &uri) -{ - v_uri = uri; -} +void Address::setUri(const QString &uri) { v_uri = uri; } //! \brief Set Node information. //! //! Set node information. //! \param node - node string -void Address::setNode(const QString &node) -{ - v_node = node; -} +void Address::setNode(const QString &node) { v_node = node; } //! \brief Set Description information. //! //! Set description of the url. //! \param desc - description of url -void Address::setDesc(const QString &desc) -{ - v_desc = desc; -} +void Address::setDesc(const QString &desc) { v_desc = desc; } //! \brief Set delivered information. //! //! Set delivered information. //! \param delivered - delivered flag -void Address::setDelivered(bool delivered) -{ - v_delivered = delivered; -} +void Address::setDelivered(bool delivered) { v_delivered = delivered; } //! \brief Set Type information. //! //! Set type information. //! \param type - type -void Address::setType(Type type) -{ - v_type = type; -} - - -//---------------------------------------------------------------------------- -// Hash -//---------------------------------------------------------------------------- -static const struct { - const char *text; - Hash::Type hashType; -} hashTypes[] = -{ -{ "sha-1", Hash::Type::Sha1 }, -{ "sha-256", Hash::Type::Sha256 }, -{ "sha-512", Hash::Type::Sha512 }, -{ "sha3-256", Hash::Type::Sha3_256 }, -{ "sha3-512", Hash::Type::Sha3_512 }, -{ "blake2b-256", Hash::Type::Blake2b256 }, -{ "blake2b-512", Hash::Type::Blake2b512 } -}; - - -Hash::Hash(const QDomElement &el) -{ - QString algo = el.attribute(QLatin1String("algo")); - if (!algo.isEmpty()) { - for(size_t n = 0; n < sizeof(hashTypes) / sizeof(hashTypes[0]); ++n) { - if(algo == QLatin1String(hashTypes[n].text)) { - v_type = hashTypes[n].hashType; - if (el.tagName() == QLatin1String("hash")) { - v_data = QByteArray::fromBase64(el.text().toLatin1()); - if (v_data.isEmpty()) { - v_type = Type::Unknown; - } - } // else hash-used - break; - } - } - } -} - -bool Hash::computeFromData(const QByteArray &ba) -{ - v_data.clear(); - switch (v_type) { - case Type::Sha1: v_data = QCryptographicHash::hash(ba, QCryptographicHash::Sha1); break; - case Type::Sha256: v_data = QCryptographicHash::hash(ba, QCryptographicHash::Sha256); break; - case Type::Sha512: v_data = QCryptographicHash::hash(ba, QCryptographicHash::Sha512); break; - case Type::Sha3_256: v_data = QCryptographicHash::hash(ba, QCryptographicHash::Sha3_256); break; - case Type::Sha3_512: v_data = QCryptographicHash::hash(ba, QCryptographicHash::Sha3_512); break; - case Type::Blake2b256: v_data = computeBlake2Hash(ba, Blake2Digest256); break; - case Type::Blake2b512: v_data = computeBlake2Hash(ba, Blake2Digest512); break; - case Type::Unknown: - default: - qDebug("invalid hash type"); - return false; - } - return !v_data.isEmpty(); -} - -bool Hash::computeFromDevice(QIODevice *dev) -{ - switch (v_type) { - case Type::Sha1: { QCryptographicHash h(QCryptographicHash::Sha1); h.addData(dev); v_data = h.result(); break; } - case Type::Sha256: { QCryptographicHash h(QCryptographicHash::Sha256); h.addData(dev); v_data = h.result(); break; } - case Type::Sha512: { QCryptographicHash h(QCryptographicHash::Sha512); h.addData(dev); v_data = h.result(); break; } - case Type::Sha3_256: { QCryptographicHash h(QCryptographicHash::Sha3_256); h.addData(dev); v_data = h.result(); break; } - case Type::Sha3_512: { QCryptographicHash h(QCryptographicHash::Sha3_512); h.addData(dev); v_data = h.result(); break; } - case Type::Blake2b256: v_data = computeBlake2Hash(dev, Blake2Digest256); break; - case Type::Blake2b512: v_data = computeBlake2Hash(dev, Blake2Digest512); break; - case Type::Unknown: - default: - qDebug("invalid hash type"); - return false; - } - return !v_data.isEmpty(); -} - -QDomElement Hash::toXml(QDomDocument *doc) const -{ - if (v_type != Type::Unknown) { - for(size_t n = 0; n < sizeof(hashTypes) / sizeof(hashTypes[0]); ++n) { - if(v_type == hashTypes[n].hashType) { - auto el = doc->createElementNS(XMPP_HASH_NS, QLatin1String(v_data.isEmpty()? "hash-used": "hash")); - el.setAttribute(QLatin1String("algo"), QLatin1String(hashTypes[n].text)); - if (!v_data.isEmpty()) { - XMLHelper::setTagText(el, v_data.toBase64()); - } - return el; - } - } - } - return QDomElement(); -} - -void Hash::populateFeatures(Features &features) -{ - for(size_t n = 0; n < sizeof(hashTypes) / sizeof(hashTypes[0]); ++n) { - features.addFeature(QLatin1String("urn:xmpp:hash-function-text-names:") + - QLatin1String(hashTypes[n].text)); - } -} +void Address::setType(Type type) { v_type = type; } //---------------------------------------------------------------------------- // RosterExchangeItem //---------------------------------------------------------------------------- -RosterExchangeItem::RosterExchangeItem(const Jid& jid, const QString& name, const QStringList& groups, Action action) : jid_(jid), name_(name), groups_(groups), action_(action) +RosterExchangeItem::RosterExchangeItem(const Jid &jid, const QString &name, const QStringList &groups, Action action) : + jid_(jid), name_(name), groups_(groups), action_(action) { } -RosterExchangeItem::RosterExchangeItem(const QDomElement& el) : action_(Add) -{ - fromXml(el); -} +RosterExchangeItem::RosterExchangeItem(const QDomElement &el) : action_(Add) { fromXml(el); } -const Jid& RosterExchangeItem::jid() const -{ - return jid_; -} +const Jid &RosterExchangeItem::jid() const { return jid_; } -RosterExchangeItem::Action RosterExchangeItem::action() const -{ - return action_; -} +RosterExchangeItem::Action RosterExchangeItem::action() const { return action_; } -const QString& RosterExchangeItem::name() const -{ - return name_; -} +const QString &RosterExchangeItem::name() const { return name_; } -const QStringList& RosterExchangeItem::groups() const -{ - return groups_; -} +const QStringList &RosterExchangeItem::groups() const { return groups_; } -bool RosterExchangeItem::isNull() const -{ - return jid_.isEmpty(); -} +bool RosterExchangeItem::isNull() const { return jid_.isEmpty(); } -void RosterExchangeItem::setJid(const Jid& jid) -{ - jid_ = jid; -} +void RosterExchangeItem::setJid(const Jid &jid) { jid_ = jid; } -void RosterExchangeItem::setAction(Action action) -{ - action_ = action; -} +void RosterExchangeItem::setAction(Action action) { action_ = action; } -void RosterExchangeItem::setName(const QString& name) -{ - name_ = name; -} +void RosterExchangeItem::setName(const QString &name) { name_ = name; } -void RosterExchangeItem::setGroups(const QStringList& groups) -{ - groups_ = groups; -} +void RosterExchangeItem::setGroups(const QStringList &groups) { groups_ = groups; } -QDomElement RosterExchangeItem::toXml(Stanza& s) const +QDomElement RosterExchangeItem::toXml(Stanza &s) const { QDomElement e = s.createElement("http://jabber.org/protocol/rosterx", "item"); e.setAttribute("jid", jid().full()); if (!name().isEmpty()) e.setAttribute("name", name()); - switch(action()) { + switch (action()) { case Add: - e.setAttribute("action","add"); + e.setAttribute("action", "add"); break; case Delete: - e.setAttribute("action","delete"); + e.setAttribute("action", "delete"); break; case Modify: - e.setAttribute("action","modify"); + e.setAttribute("action", "modify"); break; } - foreach(QString group, groups_) { - e.appendChild(s.createTextElement("http://jabber.org/protocol/rosterx", "group",group)); + for (const QString &group : groups_) { + e.appendChild(s.createTextElement("http://jabber.org/protocol/rosterx", "group", group)); } return e; } -void RosterExchangeItem::fromXml(const QDomElement& e) +void RosterExchangeItem::fromXml(const QDomElement &e) { setJid(e.attribute("jid")); setName(e.attribute("name")); if (e.attribute("action") == "delete") { setAction(Delete); - } - else if (e.attribute("action") == "modify") { + } else if (e.attribute("action") == "modify") { setAction(Modify); - } - else { + } else { setAction(Add); } QDomNodeList nl = e.childNodes(); - for(int n = 0; n < nl.count(); ++n) { + for (int n = 0; n < nl.count(); ++n) { QDomElement g = nl.item(n).toElement(); if (!g.isNull() && g.tagName() == "group") { groups_ += g.text(); @@ -514,174 +322,122 @@ void RosterExchangeItem::fromXml(const QDomElement& e) } } - //---------------------------------------------------------------------------- // MUCItem //---------------------------------------------------------------------------- -MUCItem::MUCItem(Role r, Affiliation a) : affiliation_(a), role_(r) -{ -} +MUCItem::MUCItem(Role r, Affiliation a) : affiliation_(a), role_(r) { } -MUCItem::MUCItem(const QDomElement& el) : affiliation_(UnknownAffiliation), role_(UnknownRole) -{ - fromXml(el); -} +MUCItem::MUCItem(const QDomElement &el) : affiliation_(UnknownAffiliation), role_(UnknownRole) { fromXml(el); } -void MUCItem::setNick(const QString& n) -{ - nick_ = n; -} +void MUCItem::setNick(const QString &n) { nick_ = n; } -void MUCItem::setJid(const Jid& j) -{ - jid_ = j; -} +void MUCItem::setJid(const Jid &j) { jid_ = j; } -void MUCItem::setAffiliation(Affiliation a) -{ - affiliation_ = a; -} +void MUCItem::setAffiliation(Affiliation a) { affiliation_ = a; } -void MUCItem::setRole(Role r) -{ - role_ = r; -} +void MUCItem::setRole(Role r) { role_ = r; } -void MUCItem::setActor(const Jid& a) -{ - actor_ = a; -} +void MUCItem::setActor(const Actor &a) { actor_ = a; } -void MUCItem::setReason(const QString& r) -{ - reason_ = r; -} +void MUCItem::setReason(const QString &r) { reason_ = r; } -const QString& MUCItem::nick() const -{ - return nick_; -} +const QString &MUCItem::nick() const { return nick_; } -const Jid& MUCItem::jid() const -{ - return jid_; -} +const Jid &MUCItem::jid() const { return jid_; } -MUCItem::Affiliation MUCItem::affiliation() const -{ - return affiliation_; -} +MUCItem::Affiliation MUCItem::affiliation() const { return affiliation_; } -MUCItem::Role MUCItem::role() const -{ - return role_; -} +MUCItem::Role MUCItem::role() const { return role_; } -const Jid& MUCItem::actor() const -{ - return actor_; -} +const MUCItem::Actor &MUCItem::actor() const { return actor_; } -const QString& MUCItem::reason() const -{ - return reason_; -} +const QString &MUCItem::reason() const { return reason_; } -void MUCItem::fromXml(const QDomElement& e) +void MUCItem::fromXml(const QDomElement &e) { if (e.tagName() != QLatin1String("item")) return; - jid_ = Jid(e.attribute("jid")); + jid_ = Jid(e.attribute("jid")); nick_ = e.attribute(QLatin1String("nick")); // Affiliation if (e.attribute(QLatin1String("affiliation")) == QLatin1String("owner")) { affiliation_ = Owner; - } - else if (e.attribute(QLatin1String("affiliation")) == QLatin1String("admin")) { + } else if (e.attribute(QLatin1String("affiliation")) == QLatin1String("admin")) { affiliation_ = Admin; - } - else if (e.attribute(QLatin1String("affiliation")) == QLatin1String("member")) { + } else if (e.attribute(QLatin1String("affiliation")) == QLatin1String("member")) { affiliation_ = Member; - } - else if (e.attribute(QLatin1String("affiliation")) == QLatin1String("outcast")) { + } else if (e.attribute(QLatin1String("affiliation")) == QLatin1String("outcast")) { affiliation_ = Outcast; - } - else if (e.attribute(QLatin1String("affiliation")) == QLatin1String("none")) { + } else if (e.attribute(QLatin1String("affiliation")) == QLatin1String("none")) { affiliation_ = NoAffiliation; } // Role if (e.attribute(QLatin1String("role")) == QLatin1String("moderator")) { role_ = Moderator; - } - else if (e.attribute(QLatin1String("role")) == QLatin1String("participant")) { + } else if (e.attribute(QLatin1String("role")) == QLatin1String("participant")) { role_ = Participant; - } - else if (e.attribute(QLatin1String("role")) == QLatin1String("visitor")) { + } else if (e.attribute(QLatin1String("role")) == QLatin1String("visitor")) { role_ = Visitor; - } - else if (e.attribute(QLatin1String("role")) == QLatin1String("none")) { + } else if (e.attribute(QLatin1String("role")) == QLatin1String("none")) { role_ = NoRole; } - for(QDomNode n = e.firstChild(); !n.isNull(); n = n.nextSibling()) { - QDomElement i = n.toElement(); - if(i.isNull()) - continue; - - if (i.tagName() == QLatin1String("actor")) - actor_ = Jid(i.attribute(QLatin1String("jid"))); - else if (i.tagName() == QLatin1String("reason")) + for (QDomElement i = e.firstChildElement(); !i.isNull(); i = i.nextSiblingElement()) { + if (i.tagName() == QLatin1String("actor")) { + actor_.jid = Jid(i.attribute(QLatin1String("jid"))); + actor_.nick = i.attribute(QLatin1String("nick")); + } else if (i.tagName() == QLatin1String("reason")) reason_ = i.text(); } } -QDomElement MUCItem::toXml(QDomDocument& d) +QDomElement MUCItem::toXml(QDomDocument &d) { QDomElement e = d.createElement("item"); if (!nick_.isEmpty()) - e.setAttribute("nick",nick_); + e.setAttribute("nick", nick_); if (!jid_.isEmpty()) - e.setAttribute("jid",jid_.full()); + e.setAttribute("jid", jid_.full()); if (!reason_.isEmpty()) - e.appendChild(textTag(&d,"reason",reason_)); + e.appendChild(textTag(&d, "reason", reason_)); switch (affiliation_) { case NoAffiliation: - e.setAttribute("affiliation","none"); + e.setAttribute("affiliation", "none"); break; case Owner: - e.setAttribute("affiliation","owner"); + e.setAttribute("affiliation", "owner"); break; case Admin: - e.setAttribute("affiliation","admin"); + e.setAttribute("affiliation", "admin"); break; case Member: - e.setAttribute("affiliation","member"); + e.setAttribute("affiliation", "member"); break; case Outcast: - e.setAttribute("affiliation","outcast"); + e.setAttribute("affiliation", "outcast"); break; default: break; } switch (role_) { case NoRole: - e.setAttribute("role","none"); + e.setAttribute("role", "none"); break; case Moderator: - e.setAttribute("role","moderator"); + e.setAttribute("role", "moderator"); break; case Participant: - e.setAttribute("role","participant"); + e.setAttribute("role", "participant"); break; case Visitor: - e.setAttribute("role","visitor"); + e.setAttribute("role", "visitor"); break; default: break; @@ -690,78 +446,53 @@ QDomElement MUCItem::toXml(QDomDocument& d) return e; } -bool MUCItem::operator==(const MUCItem& o) +bool MUCItem::Actor::operator==(const Actor &o) const +{ + return ((!jid.isValid() && !o.jid.isValid()) || jid.compare(o.jid, true)) && (nick == o.nick); +} + +bool MUCItem::operator==(const MUCItem &o) const { - return !nick_.compare(o.nick_) && ((!jid_.isValid() && !o.jid_.isValid()) || jid_.compare(o.jid_,true)) && ((!actor_.isValid() && !o.actor_.isValid()) || actor_.compare(o.actor_,true)) && affiliation_ == o.affiliation_ && role_ == o.role_ && !reason_.compare(o.reason_); + return !nick_.compare(o.nick_) && ((!jid_.isValid() && !o.jid_.isValid()) || jid_.compare(o.jid_, true)) + && (actor_ == o.actor_) && affiliation_ == o.affiliation_ && role_ == o.role_ && !reason_.compare(o.reason_); } //---------------------------------------------------------------------------- // MUCInvite //---------------------------------------------------------------------------- -MUCInvite::MUCInvite() : cont_(false) -{ -} +MUCInvite::MUCInvite() : cont_(false) { } -MUCInvite::MUCInvite(const Jid& to, const QString& reason) : to_(to), reason_(reason), cont_(false) -{ -} +MUCInvite::MUCInvite(const Jid &to, const QString &reason) : to_(to), reason_(reason), cont_(false) { } -MUCInvite::MUCInvite(const QDomElement& e) : cont_(false) -{ - fromXml(e); -} +MUCInvite::MUCInvite(const QDomElement &e) : cont_(false) { fromXml(e); } -const Jid& MUCInvite::from() const -{ - return from_; -} +const Jid &MUCInvite::from() const { return from_; } -void MUCInvite::setFrom(const Jid& j) -{ - from_ = j; -} +void MUCInvite::setFrom(const Jid &j) { from_ = j; } -const Jid& MUCInvite::to() const -{ - return to_; -} +const Jid &MUCInvite::to() const { return to_; } -void MUCInvite::setTo(const Jid& j) -{ - to_ = j; -} +void MUCInvite::setTo(const Jid &j) { to_ = j; } -const QString& MUCInvite::reason() const -{ - return reason_; -} +const QString &MUCInvite::reason() const { return reason_; } -void MUCInvite::setReason(const QString& r) -{ - reason_ = r; -} +void MUCInvite::setReason(const QString &r) { reason_ = r; } -bool MUCInvite::cont() const -{ - return cont_; -} +bool MUCInvite::cont() const { return cont_; } -void MUCInvite::setCont(bool b) -{ - cont_ = b; -} +void MUCInvite::setCont(bool b) { cont_ = b; } -void MUCInvite::fromXml(const QDomElement& e) +void MUCInvite::fromXml(const QDomElement &e) { if (e.tagName() != "invite") return; from_ = e.attribute("from"); - to_ = e.attribute("to"); - for(QDomNode n = e.firstChild(); !n.isNull(); n = n.nextSibling()) { + to_ = e.attribute("to"); + for (QDomNode n = e.firstChild(); !n.isNull(); n = n.nextSibling()) { QDomElement i = n.toElement(); - if(i.isNull()) + if (i.isNull()) continue; if (i.tagName() == "continue") @@ -771,14 +502,14 @@ void MUCInvite::fromXml(const QDomElement& e) } } -QDomElement MUCInvite::toXml(QDomDocument& d) const +QDomElement MUCInvite::toXml(QDomDocument &d) const { QDomElement invite = d.createElement("invite"); if (!to_.isEmpty()) { - invite.setAttribute("to",to_.full()); + invite.setAttribute("to", to_.full()); } if (!from_.isEmpty()) { - invite.setAttribute("from",from_.full()); + invite.setAttribute("from", from_.full()); } if (!reason_.isEmpty()) { invite.appendChild(textTag(&d, "reason", reason_)); @@ -789,64 +520,38 @@ QDomElement MUCInvite::toXml(QDomDocument& d) const return invite; } -bool MUCInvite::isNull() const -{ - return to_.isEmpty() && from_.isEmpty(); -} +bool MUCInvite::isNull() const { return to_.isEmpty() && from_.isEmpty(); } //---------------------------------------------------------------------------- // MUCDecline //---------------------------------------------------------------------------- -MUCDecline::MUCDecline() -{ -} +MUCDecline::MUCDecline() { } -MUCDecline::MUCDecline(const QDomElement& e) -{ - fromXml(e); -} +MUCDecline::MUCDecline(const QDomElement &e) { fromXml(e); } -const Jid& MUCDecline::from() const -{ - return from_; -} +const Jid &MUCDecline::from() const { return from_; } -void MUCDecline::setFrom(const Jid& j) -{ - from_ = j; -} +void MUCDecline::setFrom(const Jid &j) { from_ = j; } -const Jid& MUCDecline::to() const -{ - return to_; -} +const Jid &MUCDecline::to() const { return to_; } -void MUCDecline::setTo(const Jid& j) -{ - to_ = j; -} +void MUCDecline::setTo(const Jid &j) { to_ = j; } -const QString& MUCDecline::reason() const -{ - return reason_; -} +const QString &MUCDecline::reason() const { return reason_; } -void MUCDecline::setReason(const QString& r) -{ - reason_ = r; -} +void MUCDecline::setReason(const QString &r) { reason_ = r; } -void MUCDecline::fromXml(const QDomElement& e) +void MUCDecline::fromXml(const QDomElement &e) { if (e.tagName() != "decline") return; from_ = e.attribute("from"); - to_ = e.attribute("to"); - for(QDomNode n = e.firstChild(); !n.isNull(); n = n.nextSibling()) { + to_ = e.attribute("to"); + for (QDomNode n = e.firstChild(); !n.isNull(); n = n.nextSibling()) { QDomElement i = n.toElement(); - if(i.isNull()) + if (i.isNull()) continue; if (i.tagName() == "reason") @@ -854,14 +559,14 @@ void MUCDecline::fromXml(const QDomElement& e) } } -QDomElement MUCDecline::toXml(QDomDocument& d) const +QDomElement MUCDecline::toXml(QDomDocument &d) const { QDomElement decline = d.createElement("decline"); if (!to_.isEmpty()) { - decline.setAttribute("to",to_.full()); + decline.setAttribute("to", to_.full()); } if (!from_.isEmpty()) { - decline.setAttribute("from",from_.full()); + decline.setAttribute("from", from_.full()); } if (!reason_.isEmpty()) { decline.appendChild(textTag(&d, "reason", reason_)); @@ -869,53 +574,33 @@ QDomElement MUCDecline::toXml(QDomDocument& d) const return decline; } -bool MUCDecline::isNull() const -{ - return to_.isEmpty() && from_.isEmpty(); -} +bool MUCDecline::isNull() const { return to_.isEmpty() && from_.isEmpty(); } //---------------------------------------------------------------------------- // MUCDestroy //---------------------------------------------------------------------------- -MUCDestroy::MUCDestroy() -{ -} +MUCDestroy::MUCDestroy() { } -MUCDestroy::MUCDestroy(const QDomElement& e) -{ - fromXml(e); -} +MUCDestroy::MUCDestroy(const QDomElement &e) { fromXml(e); } -const Jid& MUCDestroy::jid() const -{ - return jid_; -} +const Jid &MUCDestroy::jid() const { return jid_; } -void MUCDestroy::setJid(const Jid& j) -{ - jid_ = j; -} +void MUCDestroy::setJid(const Jid &j) { jid_ = j; } -const QString& MUCDestroy::reason() const -{ - return reason_; -} +const QString &MUCDestroy::reason() const { return reason_; } -void MUCDestroy::setReason(const QString& r) -{ - reason_ = r; -} +void MUCDestroy::setReason(const QString &r) { reason_ = r; } -void MUCDestroy::fromXml(const QDomElement& e) +void MUCDestroy::fromXml(const QDomElement &e) { if (e.tagName() != "destroy") return; jid_ = e.attribute("jid"); - for(QDomNode n = e.firstChild(); !n.isNull(); n = n.nextSibling()) { + for (QDomNode n = e.firstChild(); !n.isNull(); n = n.nextSibling()) { QDomElement i = n.toElement(); - if(i.isNull()) + if (i.isNull()) continue; if (i.tagName() == "reason") @@ -923,11 +608,11 @@ void MUCDestroy::fromXml(const QDomElement& e) } } -QDomElement MUCDestroy::toXml(QDomDocument& d) const +QDomElement MUCDestroy::toXml(QDomDocument &d) const { QDomElement destroy = d.createElement("destroy"); if (!jid_.isEmpty()) { - destroy.setAttribute("jid",jid_.full()); + destroy.setAttribute("jid", jid_.full()); } if (!reason_.isEmpty()) { destroy.appendChild(textTag(&d, "reason", reason_)); @@ -938,21 +623,13 @@ QDomElement MUCDestroy::toXml(QDomDocument& d) const //---------------------------------------------------------------------------- // HTMLElement //---------------------------------------------------------------------------- -HTMLElement::HTMLElement() -{ -} +HTMLElement::HTMLElement() { } HTMLElement::HTMLElement(const QDomElement &body) { setBody(body); } -void HTMLElement::setBody(const QDomElement &body) -{ - body_ = doc_.importNode(body, true).toElement(); -} +void HTMLElement::setBody(const QDomElement &body) { body_ = doc_.importNode(body, true).toElement(); } -const QDomElement& HTMLElement::body() const -{ - return body_; -} +const QDomElement &HTMLElement::body() const { return body_; } /** * Returns the string reperesentation of the HTML element. @@ -974,44 +651,40 @@ QString HTMLElement::toString(const QString &rootTagName) const // msg.appendChild(e); // return msg.toString(); // call Stream::xmlToString, to remove unwanted namespace attributes - return(Stream::xmlToString(e)); + return (Stream::xmlToString(e)); } -QString HTMLElement::text() const -{ - return body_.text(); -} +QString HTMLElement::text() const { return body_.text(); } void HTMLElement::filterOutUnwanted(bool strict) { - Q_UNUSED(strict) //TODO filter out not xhtml-im elements + Q_UNUSED(strict) // TODO filter out not xhtml-im elements filterOutUnwantedRecursive(body_, strict); } void HTMLElement::filterOutUnwantedRecursive(QDomElement &el, bool strict) { - Q_UNUSED(strict) //TODO filter out not xhtml-im elements + Q_UNUSED(strict) // TODO filter out not xhtml-im elements - static QSet unwanted = QSet()<<"script"<<"iframe"; - QDomNode child = el.firstChild(); + static QSet unwanted = QSet() << "script" << "iframe"; + QDomNode child = el.firstChild(); while (!child.isNull()) { QDomNode sibling = child.nextSibling(); if (child.isElement()) { QDomElement childEl = child.toElement(); if (unwanted.contains(childEl.tagName())) { child.parentNode().removeChild(child); - } - else { + } else { QDomNamedNodeMap domAttrs = childEl.attributes(); - int acnt = domAttrs.count(); - QStringList attrs; //attributes for removing - for (int i=0; i pubsubItems; - QList pubsubRetractions; - QList eventList; - ChatState chatState = StateNone; - MessageReceipt messageReceipt = ReceiptNone; - HttpAuthRequest httpAuthRequest; - XData xdata; - IBBData ibbData; - QMap htmlElements; - QDomElement sxe; - QList bobDataList; - - QList mucStatuses; + QDateTime timeStamp; // local time + bool timeStampSend = false; + UrlList urlList; + AddressList addressList; + RosterExchangeItems rosterExchangeItems; + QString messageReceiptId; + QString nick; + QString eventId; + QString xsigned, xencrypted, invite; + QString pubsubNode; + QList pubsubItems; + QList pubsubRetractions; + QList eventList; + ChatState chatState = StateNone; + MessageReceipt messageReceipt = ReceiptNone; + HttpAuthRequest httpAuthRequest; + XData xdata; + IBBData ibbData; + QMap htmlElements; + QDomElement sxe; + QList bobDataList; + + QList mucStatuses; QList mucInvites; - MUCDecline mucDecline; - QString mucPassword; - bool hasMUCUser = false; + MUCDecline mucDecline; + QString mucPassword; + bool hasMUCUser = false; bool spooled = false, wasEncrypted = false; - //XEP-0280 Message Carbons - bool carbonsPrivate = false; + // XEP-0280 Message Carbons + bool carbonsPrivate = false; Message::ProcessingHints processingHints; - QString replaceId; - QString originId; // XEP-0359 - Message::StanzaId stanzaId; // XEP-0359 - Forwarding forwarding; // XEP-0297 + QString replaceId; + QString originId; // XEP-0359 + QString encryptionProtocol; // XEP-0380 + Message::StanzaId stanzaId; // XEP-0359 + QList references; // XEP-0385 and XEP-0372 + Forwarding forwarding; // XEP-0297 }; -#define MessageD() (d? d : (d = new Private)) +#define MessageD() (d ? d : (d = new Private)) //! \brief Constructs Message with given Jid information. //! //! This function will construct a Message container. //! \param to - specify receiver (default: empty string) -Message::Message() -{ - -} +Message::Message() { } -Message::Message(const Jid &to) - : d(new Private) -{ - d->to = to; -} +Message::Message(const Jid &to) : d(new Private) { d->to = to; } //! \brief Constructs a copy of Message object //! //! Overloaded constructor which will constructs a exact copy of the Message //! object that was passed to the constructor. //! \param from - Message object you want to copy -Message::Message(const Message &from) : - d(from.d) -{ -} +Message::Message(const Message &from) : d(from.d) { } //! \brief Required for internel use. -Message & Message::operator=(const Message &from) +Message &Message::operator=(const Message &from) { d = from.d; return *this; } //! \brief Destroy Message object. -Message::~Message() -{ -} +Message::~Message() { } //! \brief Check if it's exactly the same instance. -bool Message::operator==(const Message &from) const -{ - return d == from.d; -} +bool Message::operator==(const Message &from) const { return d == from.d; } + +bool Message::isPureSubject() const { return d && d->pureSubject; } //! \brief Return receiver's Jid information. -Jid Message::to() const -{ - return d? d->to: Jid(); -} +Jid Message::to() const { return d ? d->to : Jid(); } //! \brief Return sender's Jid information. -Jid Message::from() const -{ - return d? d->from: Jid(); -} +Jid Message::from() const { return d ? d->from : Jid(); } -QString Message::id() const -{ - return d? d->id: QString(); -} +QString Message::id() const { return d ? d->id : QString(); } //! \brief Return type information -QString Message::type() const -{ - return d? d->type: QString(); -} +QString Message::type() const { return d ? d->type : QString(); } -QString Message::lang() const -{ - return d? d->lang: QString(); -} +QString Message::lang() const { return d ? d->lang : QString(); } //! \brief Return subject information. -QString Message::subject(const QString &lang) const -{ - return d? d->subject.value(lang): QString(); -} +QString Message::subject(const QString &lang) const { return d ? d->subject.value(lang) : QString(); } //! \brief Return subject information. -QString Message::subject(const QLocale &lang) const -{ - return d? d->subject.value(lang.bcp47Name()): QString(); -} +QString Message::subject(const QLocale &lang) const { return d ? d->subject.value(lang.bcp47Name()) : QString(); } -StringMap Message::subjectMap() const -{ - return d? d->subject: StringMap(); -} +StringMap Message::subjectMap() const { return d ? d->subject : StringMap(); } //! \brief Return body information. //! @@ -1190,10 +827,7 @@ QString Message::body(const QString &lang) const //! //! \param lang - requested body's locale //! \note Returns first body if not found by locale. -QString Message::body(const QLocale &lang) const -{ - return body(lang.bcp47Name()); -} +QString Message::body(const QLocale &lang) const { return body(lang.bcp47Name()); } //! \brief Return xhtml body. //! @@ -1203,13 +837,12 @@ QString Message::body(const QLocale &lang) const //! \note The return string is in xhtml HTMLElement Message::html(const QString &lang) const { - if(containsHTML()) { + if (containsHTML()) { if (d->htmlElements.contains(lang)) return d->htmlElements[lang]; else return d->htmlElements.begin().value(); - } - else + } else return HTMLElement(); } @@ -1217,20 +850,11 @@ HTMLElement Message::html(const QString &lang) const //! //! Returns true if there is at least one xhtml-im body //! in the message. -bool Message::containsHTML() const -{ - return d && !(d->htmlElements.isEmpty()); -} +bool Message::containsHTML() const { return d && !(d->htmlElements.isEmpty()); } -QString Message::thread() const -{ - return d? d->thread: QString(); -} +QString Message::thread() const { return d ? d->thread : QString(); } -Stanza::Error Message::error() const -{ - return d? d->error: Stanza::Error(); -} +Stanza::Error Message::error() const { return d ? d->error : Stanza::Error(); } //! \brief Set receivers information //! @@ -1238,18 +862,19 @@ Stanza::Error Message::error() const void Message::setTo(const Jid &j) { MessageD()->to = j; - //d->flag = false; + // d->flag = false; } void Message::setFrom(const Jid &j) { MessageD()->from = j; - //d->flag = false; + // d->flag = false; } void Message::setId(const QString &s) { - MessageD()->id = s; + MessageD()->id = s; + MessageD()->originId = s; } //! \brief Set Type of message @@ -1258,13 +883,10 @@ void Message::setId(const QString &s) void Message::setType(const QString &s) { MessageD()->type = s; - //d->flag = false; + // d->flag = false; } -void Message::setLang(const QString &s) -{ - MessageD()->lang = s; -} +void Message::setLang(const QString &s) { MessageD()->lang = s; } //! \brief Set subject //! @@ -1272,7 +894,8 @@ void Message::setLang(const QString &s) void Message::setSubject(const QString &s, const QString &lang) { MessageD()->subject[lang] = s; - //d->flag = false; + d->pureSubject = !d->subject.isEmpty() && d->thread.isEmpty() && d->body.isEmpty(); + // d->flag = false; } //! \brief Set body @@ -1283,7 +906,8 @@ void Message::setSubject(const QString &s, const QString &lang) void Message::setBody(const QString &s, const QString &lang) { MessageD()->body[lang] = s; - //d->flag = false; + d->pureSubject = !d->subject.isEmpty() && d->thread.isEmpty() && d->body.isEmpty(); + // d->flag = false; } //! \brief Set xhtml body @@ -1291,61 +915,41 @@ void Message::setBody(const QString &s, const QString &lang) //! \param s - body node //! \param lang - body language //! \note The body should be in xhtml. -void Message::setHTML(const HTMLElement &e, const QString &lang) -{ - MessageD()->htmlElements[lang] = e; -} +void Message::setHTML(const HTMLElement &e, const QString &lang) { MessageD()->htmlElements[lang] = e; } void Message::setThread(const QString &s, bool send) { MessageD()->threadSend = send; - d->thread = s; + d->thread = s; + d->pureSubject = !d->subject.isEmpty() && d->thread.isEmpty() && d->body.isEmpty(); } -void Message::setError(const Stanza::Error &err) -{ - MessageD()->error = err; -} +void Message::setError(const Stanza::Error &err) { MessageD()->error = err; } -QString Message::pubsubNode() const -{ - return d? d->pubsubNode: QString(); -} +QString Message::pubsubNode() const { return d ? d->pubsubNode : QString(); } -QList Message::pubsubItems() const -{ - return d? d->pubsubItems: QList(); -} +QList Message::pubsubItems() const { return d ? d->pubsubItems : QList(); } QList Message::pubsubRetractions() const { - return d? d->pubsubRetractions: QList(); + return d ? d->pubsubRetractions : QList(); } -QDateTime Message::timeStamp() const -{ - return d? d->timeStamp: QDateTime(); -} +QDateTime Message::timeStamp() const { return d ? d->timeStamp : QDateTime(); } void Message::setTimeStamp(const QDateTime &ts, bool send) { MessageD()->timeStampSend = send; - d->timeStamp = ts; + d->timeStamp = ts; } //! \brief Return list of urls attached to message. -UrlList Message::urlList() const -{ - return d? d->urlList: UrlList(); -} +UrlList Message::urlList() const { return d ? d->urlList : UrlList(); } //! \brief Add Url to the url list. //! //! \param url - url to append -void Message::urlAdd(const Url &u) -{ - MessageD()->urlList += u; -} +void Message::urlAdd(const Url &u) { MessageD()->urlList += u; } //! \brief clear out the url list. void Message::urlsClear() @@ -1358,24 +962,15 @@ void Message::urlsClear() //! \brief Set urls to send //! //! \param urlList - list of urls to send -void Message::setUrlList(const UrlList &list) -{ - MessageD()->urlList = list; -} +void Message::setUrlList(const UrlList &list) { MessageD()->urlList = list; } //! \brief Return list of addresses attached to message. -AddressList Message::addresses() const -{ - return d? d->addressList : AddressList(); -} +AddressList Message::addresses() const { return d ? d->addressList : AddressList(); } //! \brief Add Address to the address list. //! //! \param address - address to append -void Message::addAddress(const Address &a) -{ - MessageD()->addressList += a; -} +void Message::addAddress(const Address &a) { MessageD()->addressList += a; } //! \brief clear out the address list. void Message::clearAddresses() @@ -1391,7 +986,7 @@ AddressList Message::findAddresses(Address::Type t) const return AddressList(); } AddressList matches; - foreach(Address a, d->addressList) { + for (const Address &a : std::as_const(d->addressList)) { if (a.type() == t) matches.append(a); } @@ -1401,40 +996,19 @@ AddressList Message::findAddresses(Address::Type t) const //! \brief Set addresses to send //! //! \param list - list of addresses to send -void Message::setAddresses(const AddressList &list) -{ - MessageD()->addressList = list; -} +void Message::setAddresses(const AddressList &list) { MessageD()->addressList = list; } -RosterExchangeItems Message::rosterExchangeItems() const -{ - return d? d->rosterExchangeItems : RosterExchangeItems(); -} +RosterExchangeItems Message::rosterExchangeItems() const { return d ? d->rosterExchangeItems : RosterExchangeItems(); } -void Message::setRosterExchangeItems(const RosterExchangeItems& items) -{ - MessageD()->rosterExchangeItems = items; -} +void Message::setRosterExchangeItems(const RosterExchangeItems &items) { MessageD()->rosterExchangeItems = items; } -QString Message::eventId() const -{ - return d? d->eventId: QString(); -} +QString Message::eventId() const { return d ? d->eventId : QString(); } -void Message::setEventId(const QString& id) -{ - MessageD()->eventId = id; -} +void Message::setEventId(const QString &id) { MessageD()->eventId = id; } -bool Message::containsEvents() const -{ - return d && !d->eventList.isEmpty(); -} +bool Message::containsEvents() const { return d && !d->eventList.isEmpty(); } -bool Message::containsEvent(MsgEvent e) const -{ - return d && d->eventList.contains(e); -} +bool Message::containsEvent(MsgEvent e) const { return d && d->eventList.contains(e); } void Message::addEvent(MsgEvent e) { @@ -1445,185 +1019,87 @@ void Message::addEvent(MsgEvent e) } } -ChatState Message::chatState() const -{ - return d? d->chatState : StateNone; -} +ChatState Message::chatState() const { return d ? d->chatState : StateNone; } -void Message::setChatState(ChatState state) -{ - MessageD()->chatState = state; -} +void Message::setChatState(ChatState state) { MessageD()->chatState = state; } -MessageReceipt Message::messageReceipt() const -{ - return d? d->messageReceipt: ReceiptNone; -} +MessageReceipt Message::messageReceipt() const { return d ? d->messageReceipt : ReceiptNone; } -void Message::setMessageReceipt(MessageReceipt messageReceipt) -{ - MessageD()->messageReceipt = messageReceipt; -} +void Message::setMessageReceipt(MessageReceipt messageReceipt) { MessageD()->messageReceipt = messageReceipt; } -QString Message::messageReceiptId() const -{ - return d? d->messageReceiptId: QString(); -} +QString Message::messageReceiptId() const { return d ? d->messageReceiptId : QString(); } -void Message::setMessageReceiptId(const QString &s) -{ - MessageD()->messageReceiptId = s; -} +void Message::setMessageReceiptId(const QString &s) { MessageD()->messageReceiptId = s; } -QString Message::xsigned() const -{ - return d? d->xsigned: QString(); -} +QString Message::xsigned() const { return d ? d->xsigned : QString(); } -void Message::setXSigned(const QString &s) -{ - MessageD()->xsigned = s; -} +void Message::setXSigned(const QString &s) { MessageD()->xsigned = s; } -QString Message::xencrypted() const -{ - return d? d->xencrypted: QString(); -} +QString Message::xencrypted() const { return d ? d->xencrypted : QString(); } -void Message::setXEncrypted(const QString &s) -{ - MessageD()->xencrypted = s; -} +void Message::setXEncrypted(const QString &s) { MessageD()->xencrypted = s; } -QList Message::getMUCStatuses() const -{ - return d? d->mucStatuses: QList(); -} +QList Message::getMUCStatuses() const { return d ? d->mucStatuses : QList(); } -void Message::addMUCStatus(int i) -{ - MessageD()->mucStatuses += i; -} +void Message::addMUCStatus(int i) { MessageD()->mucStatuses += i; } -void Message::addMUCInvite(const MUCInvite& i) -{ - MessageD()->mucInvites += i; -} +void Message::addMUCInvite(const MUCInvite &i) { MessageD()->mucInvites += i; } -QList Message::mucInvites() const -{ - return d? d->mucInvites: QList(); -} +QList Message::mucInvites() const { return d ? d->mucInvites : QList(); } -void Message::setMUCDecline(const MUCDecline& de) -{ - MessageD()->mucDecline = de; -} +void Message::setMUCDecline(const MUCDecline &de) { MessageD()->mucDecline = de; } -MUCDecline Message::mucDecline() const -{ - return d? d->mucDecline: MUCDecline(); -} +MUCDecline Message::mucDecline() const { return d ? d->mucDecline : MUCDecline(); } -QString Message::mucPassword() const -{ - return d? d->mucPassword: QString(); -} +QString Message::mucPassword() const { return d ? d->mucPassword : QString(); } -void Message::setMUCPassword(const QString& p) -{ - MessageD()->mucPassword = p; -} +void Message::setMUCPassword(const QString &p) { MessageD()->mucPassword = p; } -bool Message::hasMUCUser() const -{ - return d & d->hasMUCUser; -} +bool Message::hasMUCUser() const { return d & d->hasMUCUser; } -Message::StanzaId Message::stanzaId() const -{ - return d? d->stanzaId: StanzaId(); -} +Message::StanzaId Message::stanzaId() const { return d ? d->stanzaId : StanzaId(); } -void Message::setStanzaId(const Message::StanzaId &id) -{ - MessageD()->stanzaId = id; -} +void Message::setStanzaId(const Message::StanzaId &id) { MessageD()->stanzaId = id; } -QString Message::originId() const -{ - return d? d->originId: QString(); -} +QString Message::originId() const { return d ? d->originId : QString(); } -void Message::setOriginId(const QString &id) -{ - MessageD()->originId = id; -} +void Message::setOriginId(const QString &id) { MessageD()->originId = id; } -QString Message::invite() const -{ - return d? d->invite: QString(); -} +QString Message::encryptionProtocol() const { return d ? d->encryptionProtocol : QString(); } -void Message::setInvite(const QString &s) -{ - MessageD()->invite = s; -} +void Message::setEncryptionProtocol(const QString &protocol) { MessageD()->encryptionProtocol = protocol; } -QString Message::nick() const -{ - return d? d->nick: QString(); -} +QList Message::references() const { return d ? d->references : QList(); } -void Message::setNick(const QString& n) -{ - MessageD()->nick = n; -} +void Message::addReference(const Reference &r) { MessageD()->references.append(r); } -void Message::setHttpAuthRequest(const HttpAuthRequest &req) -{ - MessageD()->httpAuthRequest = req; -} +void Message::setReferences(const QList &r) { MessageD()->references = r; } -HttpAuthRequest Message::httpAuthRequest() const -{ - return d? d->httpAuthRequest: HttpAuthRequest(); -} +QString Message::invite() const { return d ? d->invite : QString(); } -void Message::setForm(const XData &form) -{ - MessageD()->xdata = form; -} +void Message::setInvite(const QString &s) { MessageD()->invite = s; } -XData Message::getForm() const -{ - return d? d->xdata: XData(); -} +QString Message::nick() const { return d ? d->nick : QString(); } -QDomElement Message::sxe() const -{ - return d? d->sxe: QDomElement(); -} +void Message::setNick(const QString &n) { MessageD()->nick = n; } -void Message::setSxe(const QDomElement& e) -{ - MessageD()->sxe = e; -} +void Message::setHttpAuthRequest(const HttpAuthRequest &req) { MessageD()->httpAuthRequest = req; } -void Message::addBoBData(const BoBData &bob) -{ - MessageD()->bobDataList.append(bob); -} +HttpAuthRequest Message::httpAuthRequest() const { return d ? d->httpAuthRequest : HttpAuthRequest(); } -QList Message::bobDataList() const -{ - return d? d->bobDataList: QList(); -} +void Message::setForm(const XData &form) { MessageD()->xdata = form; } -IBBData Message::ibbData() const -{ - return d? d->ibbData: IBBData(); -} +XData Message::getForm() const { return d ? d->xdata : XData(); } + +QDomElement Message::sxe() const { return d ? d->sxe : QDomElement(); } + +void Message::setSxe(const QDomElement &e) { MessageD()->sxe = e; } + +void Message::addBoBData(const BoBData &bob) { MessageD()->bobDataList.append(bob); } + +QList Message::bobDataList() const { return d ? d->bobDataList : QList(); } + +IBBData Message::ibbData() const { return d ? d->ibbData : IBBData(); } //! \brief Returns Jid of the remote contact //! @@ -1656,64 +1132,37 @@ Message Message::displayMessage() const return *this; } -void Message::setCarbonsPrivate(bool enable) -{ - MessageD()->carbonsPrivate = enable; -} +void Message::setCarbonsPrivate(bool enable) { MessageD()->carbonsPrivate = enable; } -bool Message::carbonsPrivate() const -{ - return (d && d->carbonsPrivate); -} +bool Message::carbonsPrivate() const { return (d && d->carbonsPrivate); } -void Message::setForwarded(const Forwarding &frw) -{ - MessageD()->forwarding = frw; -} +void Message::setForwarded(const Forwarding &frw) { MessageD()->forwarding = frw; } -const Forwarding &Message::forwarded() const -{ - return d->forwarding; -} +const Forwarding &Message::forwarded() const { return d->forwarding; } -bool Message::spooled() const -{ - return d && d->spooled; -} +void Message::setCarbonDirection(Message::CarbonDir cd) { MessageD()->carbonDir = cd; } -void Message::setSpooled(bool b) -{ - MessageD()->spooled = b; -} +Message::CarbonDir Message::carbonDirection() const { return d ? d->carbonDir : NoCarbon; } -bool Message::wasEncrypted() const -{ - return d && d->wasEncrypted; -} +void Message::setForwardedFrom(const Jid &jid) { MessageD()->forwardedFrom = jid; } -void Message::setWasEncrypted(bool b) -{ - MessageD()->wasEncrypted = b; -} +Jid Message::forwardedFrom() const { return d ? d->forwardedFrom : Jid(); } -QString Message::replaceId() const { - return d? d->replaceId: QString(); -} +bool Message::spooled() const { return d && d->spooled; } -void Message::setReplaceId(const QString& id) -{ - MessageD()->replaceId = id; -} +void Message::setSpooled(bool b) { MessageD()->spooled = b; } -void Message::setProcessingHints(const ProcessingHints &hints) -{ - MessageD()->processingHints = hints; -} +bool Message::wasEncrypted() const { return d && d->wasEncrypted; } -Message::ProcessingHints Message::processingHints() const -{ - return d? d->processingHints: ProcessingHints(); -} +void Message::setWasEncrypted(bool b) { MessageD()->wasEncrypted = b; } + +QString Message::replaceId() const { return d ? d->replaceId : QString(); } + +void Message::setReplaceId(const QString &id) { MessageD()->replaceId = id; } + +void Message::setProcessingHints(const ProcessingHints &hints) { MessageD()->processingHints = hints; } + +Message::ProcessingHints Message::processingHints() const { return d ? d->processingHints : ProcessingHints(); } Stanza Message::toStanza(Stream *stream) const { @@ -1721,28 +1170,28 @@ Stanza Message::toStanza(Stream *stream) const return Stanza(); } Stanza s = stream->createStanza(Stanza::Message, d->to, d->type); - if(!d->from.isEmpty()) + if (!d->from.isEmpty()) s.setFrom(d->from); - if(!d->id.isEmpty()) + if (!d->id.isEmpty()) s.setId(d->id); - if(!d->lang.isEmpty()) + if (!d->lang.isEmpty()) s.setLang(d->lang); StringMap::ConstIterator it; for (it = d->subject.constBegin(); it != d->subject.constEnd(); ++it) { const QString &str = (*it); - if(!str.isNull()) { + if (!str.isNull()) { QDomElement e = s.createTextElement(s.baseNS(), "subject", str); - if(!it.key().isEmpty()) + if (!it.key().isEmpty()) e.setAttributeNS(NS_XML, "xml:lang", it.key()); s.appendChild(e); } } for (it = d->body.constBegin(); it != d->body.constEnd(); ++it) { const QString &str = (*it); - if(!str.isEmpty()) { + if (!str.isEmpty()) { QDomElement e = s.createTextElement(s.baseNS(), "body", str); - if(!it.key().isEmpty()) + if (!it.key().isEmpty()) e.setAttributeNS(NS_XML, "xml:lang", it.key()); s.appendChild(e); } @@ -1751,22 +1200,22 @@ Stanza Message::toStanza(Stream *stream) const if (containsHTML()) { QDomElement html = s.createElement("http://jabber.org/protocol/xhtml-im", "html"); s.appendChild(html); - foreach (HTMLElement el, d->htmlElements) { + for (const HTMLElement &el : std::as_const(d->htmlElements)) { html.appendChild(s.doc().importNode(el.body(), true).toElement()); } } - if(d->type == "error") + if (d->type == "error") s.setError(d->error); // thread - if(d->threadSend && !d->thread.isEmpty()) { + if (d->threadSend && !d->thread.isEmpty()) { QDomElement e = s.createTextElement(s.baseNS(), "thread", d->thread); s.appendChild(e); } // timestamp - if(d->timeStampSend && !d->timeStamp.isNull()) { + if (d->timeStampSend && !d->timeStamp.isNull()) { QDomElement e = s.createElement("urn:xmpp:delay", "delay"); e.setAttribute("stamp", d->timeStamp.toUTC().toString(Qt::ISODate) + "Z"); s.appendChild(e); @@ -1777,7 +1226,7 @@ Stanza Message::toStanza(Stream *stream) const } // urls - foreach (const Url& uit, d->urlList) { + for (const Url &uit : std::as_const(d->urlList)) { QDomElement x = s.createElement("jabber:x:oob", "x"); x.appendChild(s.createTextElement("jabber:x:oob", "url", uit.url())); if (!uit.desc().isEmpty()) @@ -1791,12 +1240,12 @@ Stanza Message::toStanza(Stream *stream) const if (d->body.isEmpty()) { if (d->eventId.isEmpty()) - x.appendChild(s.createElement("jabber:x:event","id")); + x.appendChild(s.createElement("jabber:x:event", "id")); else - x.appendChild(s.createTextElement("jabber:x:event","id",d->eventId)); + x.appendChild(s.createTextElement("jabber:x:event", "id", d->eventId)); } - foreach (const MsgEvent& ev, d->eventList) { + for (const MsgEvent &ev : std::as_const(d->eventList)) { switch (ev) { case OfflineEvent: x.appendChild(s.createElement("jabber:x:event", "offline")); @@ -1821,7 +1270,7 @@ Stanza Message::toStanza(Stream *stream) const // chat state QString chatStateNS = "http://jabber.org/protocol/chatstates"; if (d->chatState != StateNone) { - switch(d->chatState) { + switch (d->chatState) { case StateActive: s.appendChild(s.createElement(chatStateNS, "active")); break; @@ -1845,36 +1294,45 @@ Stanza Message::toStanza(Stream *stream) const // message receipt QString messageReceiptNS = "urn:xmpp:receipts"; if (d->messageReceipt != ReceiptNone) { - switch(d->messageReceipt) { + switch (d->messageReceipt) { case ReceiptRequest: s.appendChild(s.createElement(messageReceiptNS, "request")); break; - case ReceiptReceived: - { + case ReceiptReceived: { QDomElement elem = s.createElement(messageReceiptNS, "received"); if (!d->messageReceiptId.isEmpty()) { elem.setAttribute("id", d->messageReceiptId); } s.appendChild(elem); - } - break; + } break; default: break; } } // xsigned - if(!d->xsigned.isEmpty()) + if (!d->xsigned.isEmpty()) s.appendChild(s.createTextElement("jabber:x:signed", "x", d->xsigned)); - // xencrypted - if(!d->xencrypted.isEmpty()) + // OpenPGP encrypted message + if (!d->xencrypted.isEmpty()) { + // See: XEP-0027: Current Jabber OpenPGP Usage s.appendChild(s.createTextElement("jabber:x:encrypted", "x", d->xencrypted)); + // See: XEP-0280: Message Carbons + QDomElement nc = s.createElement("urn:xmpp:hints", "no-copy"); + QDomElement pr = s.createElement("urn:xmpp:carbons:2", "private"); + s.appendChild(nc); + s.appendChild(pr); + // See: XEP-0380: Explicit Message Encryption + QDomElement en = s.createElement("urn:xmpp:eme:0", "encryption"); + en.setAttribute("namespace", "jabber:x:encrypted"); + s.appendChild(en); + } // addresses if (!d->addressList.isEmpty()) { - QDomElement as = s.createElement("http://jabber.org/protocol/address","addresses"); - foreach(Address a, d->addressList) { + QDomElement as = s.createElement("http://jabber.org/protocol/address", "addresses"); + for (const Address &a : std::as_const(d->addressList)) { as.appendChild(a.toXml(s)); } s.appendChild(as); @@ -1882,56 +1340,55 @@ Stanza Message::toStanza(Stream *stream) const // roster item exchange if (!d->rosterExchangeItems.isEmpty()) { - QDomElement rx = s.createElement("http://jabber.org/protocol/rosterx","x"); - foreach(RosterExchangeItem r, d->rosterExchangeItems) { + QDomElement rx = s.createElement("http://jabber.org/protocol/rosterx", "x"); + for (const RosterExchangeItem &r : std::as_const(d->rosterExchangeItems)) { rx.appendChild(r.toXml(s)); } s.appendChild(rx); } // invite - if(!d->invite.isEmpty()) { + if (!d->invite.isEmpty()) { QDomElement e = s.createElement("jabber:x:conference", "x"); e.setAttribute("jid", d->invite); s.appendChild(e); } // nick - if(!d->nick.isEmpty()) { + if (!d->nick.isEmpty()) { s.appendChild(s.createTextElement("http://jabber.org/protocol/nick", "nick", d->nick)); } // sxe - if(!d->sxe.isNull()) { + if (!d->sxe.isNull()) { s.appendChild(d->sxe); } // muc - if(!d->mucInvites.isEmpty()) { - QDomElement e = s.createElement("http://jabber.org/protocol/muc#user","x"); - foreach(MUCInvite i, d->mucInvites) { + if (!d->mucInvites.isEmpty()) { + QDomElement e = s.createElement("http://jabber.org/protocol/muc#user", "x"); + for (const MUCInvite &i : std::as_const(d->mucInvites)) { e.appendChild(i.toXml(s.doc())); } if (!d->mucPassword.isEmpty()) { - e.appendChild(s.createTextElement("http://jabber.org/protocol/muc#user","password",d->mucPassword)); + e.appendChild(s.createTextElement("http://jabber.org/protocol/muc#user", "password", d->mucPassword)); } s.appendChild(e); - } - else if(!d->mucDecline.isNull()) { - QDomElement e = s.createElement("http://jabber.org/protocol/muc#user","x"); + } else if (!d->mucDecline.isNull()) { + QDomElement e = s.createElement("http://jabber.org/protocol/muc#user", "x"); e.appendChild(d->mucDecline.toXml(s.doc())); s.appendChild(e); } // http auth - if(!d->httpAuthRequest.isEmpty()) { + if (!d->httpAuthRequest.isEmpty()) { s.appendChild(d->httpAuthRequest.toXml(s.doc())); } // data form - if(!d->xdata.fields().empty() || (d->xdata.type() == XData::Data_Cancel)) { - bool submit = (d->xdata.type() == XData::Data_Submit) || (d->xdata.type() == XData::Data_Cancel); - QDomElement dr = s.element(); + if (!d->xdata.fields().empty() || (d->xdata.type() == XData::Data_Cancel)) { + bool submit = (d->xdata.type() == XData::Data_Submit) || (d->xdata.type() == XData::Data_Cancel); + QDomElement dr = s.element(); if (d->xdata.registrarType() == "urn:xmpp:captcha") { dr = dr.appendChild(s.createElement("urn:xmpp:captcha", "captcha")).toElement(); } @@ -1939,12 +1396,12 @@ Stanza Message::toStanza(Stream *stream) const } // bits of binary - foreach(const BoBData &bd, d->bobDataList) { + for (const BoBData &bd : std::as_const(d->bobDataList)) { s.appendChild(bd.toXml(&s.doc())); } // Avoiding Carbons - if (d->carbonsPrivate || d->wasEncrypted) { + if (d->carbonsPrivate) { s.appendChild(CarbonsManager::privateElement(stream->doc())); } @@ -1988,24 +1445,23 @@ Stanza Message::toStanza(Stream *stream) const if (d->forwarding.type() != Forwarding::ForwardedNone) s.appendChild(d->forwarding.toXml(stream)); + // XEP-0372 and XEP-0385 + for (auto const &r : std::as_const(d->references)) { + s.appendChild(r.toXml(&s.doc())); + } + return s; } /** \brief Create Message from Stanza \a s, using given \a timeZoneOffset (old style) */ -bool Message::fromStanza(const Stanza &s, int timeZoneOffset) -{ - return fromStanza(s, true, timeZoneOffset); -} +bool Message::fromStanza(const Stanza &s, int timeZoneOffset) { return fromStanza(s, true, timeZoneOffset); } /** \brief Create Message from Stanza \a s */ -bool Message::fromStanza(const Stanza &s) -{ - return fromStanza(s, false, 0); -} +bool Message::fromStanza(const Stanza &s) { return fromStanza(s, false, 0); } /** \brief Create Message from Stanza \a s @@ -2017,7 +1473,7 @@ bool Message::fromStanza(const Stanza &s) */ bool Message::fromStanza(const Stanza &s, bool useTimeZoneOffset, int timeZoneOffset) { - if(s.kind() != Stanza::Message) + if (s.kind() != Stanza::Message) return false; d = new Private; @@ -2035,81 +1491,78 @@ bool Message::fromStanza(const Stanza &s, bool useTimeZoneOffset, int timeZoneOf QDomElement root = s.element(); XDomNodeList nl = root.childNodes(); - int n; - for(n = 0; n < nl.count(); ++n) { - QDomNode i = nl.item(n); - if(i.isElement()) { - QDomElement e = i.toElement(); - if(e.namespaceURI() == s.baseNS()) { - if(e.tagName() == QLatin1String("subject")) { - QString lang = e.attributeNS(NS_XML, "lang", ""); - if (lang.isEmpty() || !(lang = XMLHelper::sanitizedLang(lang)).isEmpty()) { - d->subject[lang] = e.text(); - } + int n; + bool hasBodyOrThread = false; + bool hasSubject = false; + for (QDomElement e = root.firstChildElement(); !e.isNull(); e = e.nextSiblingElement()) { + if (e.namespaceURI() == s.baseNS()) { + if (e.tagName() == QLatin1String("subject")) { + hasSubject = true; + QString lang = e.attributeNS(NS_XML, "lang", ""); + if (lang.isEmpty() || !(lang = XMLHelper::sanitizedLang(lang)).isEmpty()) { + d->subject[lang] = e.text(); } - else if(e.tagName() == QLatin1String("body")) { - QString lang = e.attributeNS(NS_XML, "lang", ""); - if (lang.isEmpty() || !(lang = XMLHelper::sanitizedLang(lang)).isEmpty()) { - d->body[lang] = e.text(); - } + } else if (e.tagName() == QLatin1String("body")) { + hasBodyOrThread = true; + QString lang = e.attributeNS(NS_XML, "lang", ""); + if (lang.isEmpty() || !(lang = XMLHelper::sanitizedLang(lang)).isEmpty()) { + d->body[lang] = e.text(); } - else if(e.tagName() == QLatin1String("thread")) - d->thread = e.text(); + } else if (e.tagName() == QLatin1String("thread")) { + hasBodyOrThread = true; + d->thread = e.text(); } - else if(e.tagName() == QLatin1String("event") && e.namespaceURI() == QLatin1String("http://jabber.org/protocol/pubsub#event")) { - for(QDomNode enode = e.firstChild(); !enode.isNull(); enode = enode.nextSibling()) { - QDomElement eel = enode.toElement(); - if (eel.tagName() == QLatin1String("items")) { - d->pubsubNode = eel.attribute("node"); - for(QDomNode inode = eel.firstChild(); !inode.isNull(); inode = inode.nextSibling()) { - QDomElement o = inode.toElement(); - if (o.tagName() == QLatin1String("item")) { - for(QDomNode j = o.firstChild(); !j.isNull(); j = j.nextSibling()) { - QDomElement item = j.toElement(); - if (!item.isNull()) { - d->pubsubItems += PubSubItem(o.attribute("id"),item); - } + } else if (e.tagName() == QLatin1String("event") + && e.namespaceURI() == QLatin1String("http://jabber.org/protocol/pubsub#event")) { + for (QDomNode enode = e.firstChild(); !enode.isNull(); enode = enode.nextSibling()) { + QDomElement eel = enode.toElement(); + if (eel.tagName() == QLatin1String("items")) { + d->pubsubNode = eel.attribute("node"); + for (QDomNode inode = eel.firstChild(); !inode.isNull(); inode = inode.nextSibling()) { + QDomElement o = inode.toElement(); + if (o.tagName() == QLatin1String("item")) { + for (QDomNode j = o.firstChild(); !j.isNull(); j = j.nextSibling()) { + QDomElement item = j.toElement(); + if (!item.isNull()) { + d->pubsubItems += PubSubItem(o.attribute("id"), item); } } - if (o.tagName() == "retract") { - d->pubsubRetractions += PubSubRetraction(o.attribute("id")); - } + } + if (o.tagName() == "retract") { + d->pubsubRetractions += PubSubRetraction(o.attribute("id")); } } } } - else if (e.tagName() == QLatin1String("no-permanent-store") && e.namespaceURI() == QLatin1String("urn:xmpp:hints")) { - d->processingHints |= NoPermanentStore; - } - else if (e.tagName() == QLatin1String("no-store") && e.namespaceURI() == QLatin1String("urn:xmpp:hints")) { - d->processingHints |= NoStore; - } - else if (e.tagName() == QLatin1String("no-copy") && e.namespaceURI() == QLatin1String("urn:xmpp:hints")) { - d->processingHints |= NoCopy; - } - else if (e.tagName() == QLatin1String("store") && e.namespaceURI() == QLatin1String("urn:xmpp:hints")) { - d->processingHints |= Store; - } - else if (e.tagName() == QLatin1String("origin-id") && e.namespaceURI() == QLatin1String("urn:xmpp:sid:0")) { - d->originId = e.attribute(QStringLiteral("id")); - } - else if (e.tagName() == QLatin1String("stanza-id") && e.namespaceURI() == QLatin1String("urn:xmpp:sid:0")) { - d->stanzaId.id = e.attribute(QStringLiteral("id")); - d->stanzaId.by = Jid(e.attribute(QStringLiteral("by"))); - } + } else if (e.tagName() == QLatin1String("no-permanent-store") + && e.namespaceURI() == QLatin1String("urn:xmpp:hints")) { + d->processingHints |= NoPermanentStore; + } else if (e.tagName() == QLatin1String("no-store") && e.namespaceURI() == QLatin1String("urn:xmpp:hints")) { + d->processingHints |= NoStore; + } else if (e.tagName() == QLatin1String("no-copy") && e.namespaceURI() == QLatin1String("urn:xmpp:hints")) { + d->processingHints |= NoCopy; + } else if (e.tagName() == QLatin1String("store") && e.namespaceURI() == QLatin1String("urn:xmpp:hints")) { + d->processingHints |= Store; + } else if (e.tagName() == QLatin1String("origin-id") && e.namespaceURI() == QLatin1String("urn:xmpp:sid:0")) { + d->originId = e.attribute(QStringLiteral("id")); + } else if (e.tagName() == QLatin1String("stanza-id") && e.namespaceURI() == QLatin1String("urn:xmpp:sid:0")) { + d->stanzaId.id = e.attribute(QStringLiteral("id")); + d->stanzaId.by = Jid(e.attribute(QStringLiteral("by"))); + } - else { - //printf("extension element: [%s]\n", e.tagName().latin1()); - } + else { + // printf("extension element: [%s]\n", e.tagName().latin1()); } } - if(s.type() == "error") + d->pureSubject = hasSubject && !hasBodyOrThread; // this is somewhat important for muc + + if (s.type() == "error") d->error = s.error(); // Bits of Binary XEP-0231 nl = childElementsByTagNameNS(root, "urn:xmpp:bob", "data"); - for(n = 0; n < nl.count(); ++n) { + for (n = 0; n < nl.count(); ++n) { addBoBData(BoBData(nl.item(n).toElement())); } @@ -2117,7 +1570,7 @@ bool Message::fromStanza(const Stanza &s, bool useTimeZoneOffset, int timeZoneOf nl = childElementsByTagNameNS(root, "http://jabber.org/protocol/xhtml-im", "html"); if (nl.count()) { nl = nl.item(0).childNodes(); - for(n = 0; n < nl.count(); ++n) { + for (n = 0; n < nl.count(); ++n) { QDomElement e = nl.item(n).toElement(); if (e.tagName() == "body" && e.namespaceURI() == "http://www.w3.org/1999/xhtml") { QString lang = e.attributeNS(NS_XML, "lang", ""); @@ -2131,7 +1584,7 @@ bool Message::fromStanza(const Stanza &s, bool useTimeZoneOffset, int timeZoneOf // timestamp QDomElement t = childElementsByTagNameNS(root, "urn:xmpp:delay", "delay").item(0).toElement(); - QDateTime stamp; + QDateTime stamp; if (!t.isNull()) { stamp = QDateTime::fromString(t.attribute("stamp").left(19), Qt::ISODate); } else { @@ -2148,20 +1601,19 @@ bool Message::fromStanza(const Stanza &s, bool useTimeZoneOffset, int timeZoneOf d->timeStamp = stamp.toLocalTime(); } d->timeStampSend = true; - d->spooled = true; - } - else { - d->timeStamp = QDateTime::currentDateTime(); + d->spooled = true; + } else { + d->timeStamp = QDateTime::currentDateTime(); d->timeStampSend = false; - d->spooled = false; + d->spooled = false; } // urls d->urlList.clear(); nl = childElementsByTagNameNS(root, "jabber:x:oob", "x"); - for(n = 0; n < nl.count(); ++n) { + for (n = 0; n < nl.count(); ++n) { QDomElement t = nl.item(n).toElement(); - Url u; + Url u; u.setUrl(t.elementsByTagName("url").item(0).toElement().text()); u.setDesc(t.elementsByTagName("desc").item(0).toElement().text()); d->urlList += u; @@ -2172,12 +1624,11 @@ bool Message::fromStanza(const Stanza &s, bool useTimeZoneOffset, int timeZoneOf nl = childElementsByTagNameNS(root, "jabber:x:event", "x"); if (nl.count()) { nl = nl.item(0).childNodes(); - for(n = 0; n < nl.count(); ++n) { + for (n = 0; n < nl.count(); ++n) { QString evtag = nl.item(n).toElement().tagName(); if (evtag == "id") { - d->eventId = nl.item(n).toElement().text(); - } - else if (evtag == "displayed") + d->eventId = nl.item(n).toElement().text(); + } else if (evtag == "displayed") d->eventList += DisplayedEvent; else if (evtag == "composing") d->eventList += ComposingEvent; @@ -2190,32 +1641,32 @@ bool Message::fromStanza(const Stanza &s, bool useTimeZoneOffset, int timeZoneOf // Chat states QString chatStateNS = "http://jabber.org/protocol/chatstates"; - t = childElementsByTagNameNS(root, chatStateNS, "active").item(0).toElement(); - if(!t.isNull()) + t = childElementsByTagNameNS(root, chatStateNS, "active").item(0).toElement(); + if (!t.isNull()) d->chatState = StateActive; t = childElementsByTagNameNS(root, chatStateNS, "composing").item(0).toElement(); - if(!t.isNull()) + if (!t.isNull()) d->chatState = StateComposing; t = childElementsByTagNameNS(root, chatStateNS, "paused").item(0).toElement(); - if(!t.isNull()) + if (!t.isNull()) d->chatState = StatePaused; t = childElementsByTagNameNS(root, chatStateNS, "inactive").item(0).toElement(); - if(!t.isNull()) + if (!t.isNull()) d->chatState = StateInactive; t = childElementsByTagNameNS(root, chatStateNS, "gone").item(0).toElement(); - if(!t.isNull()) + if (!t.isNull()) d->chatState = StateGone; // message receipts QString messageReceiptNS = "urn:xmpp:receipts"; - t = childElementsByTagNameNS(root, messageReceiptNS, "request").item(0).toElement(); - if(!t.isNull()) { + t = childElementsByTagNameNS(root, messageReceiptNS, "request").item(0).toElement(); + if (!t.isNull()) { d->messageReceipt = ReceiptRequest; d->messageReceiptId.clear(); } t = childElementsByTagNameNS(root, messageReceiptNS, "received").item(0).toElement(); - if(!t.isNull()) { - d->messageReceipt = ReceiptReceived; + if (!t.isNull()) { + d->messageReceipt = ReceiptReceived; d->messageReceiptId = t.attribute("id"); if (d->messageReceiptId.isEmpty()) d->messageReceiptId = id(); @@ -2223,14 +1674,14 @@ bool Message::fromStanza(const Stanza &s, bool useTimeZoneOffset, int timeZoneOf // xsigned t = childElementsByTagNameNS(root, "jabber:x:signed", "x").item(0).toElement(); - if(!t.isNull()) + if (!t.isNull()) d->xsigned = t.text(); else d->xsigned = QString(); // xencrypted t = childElementsByTagNameNS(root, "jabber:x:encrypted", "x").item(0).toElement(); - if(!t.isNull()) + if (!t.isNull()) d->xencrypted = t.text(); else d->xencrypted = QString(); @@ -2240,8 +1691,8 @@ bool Message::fromStanza(const Stanza &s, bool useTimeZoneOffset, int timeZoneOf nl = childElementsByTagNameNS(root, "http://jabber.org/protocol/address", "addresses"); if (nl.count()) { QDomElement t = nl.item(0).toElement(); - nl = t.elementsByTagName("address"); - for(n = 0; n < nl.count(); ++n) { + nl = t.elementsByTagName("address"); + for (n = 0; n < nl.count(); ++n) { d->addressList += Address(nl.item(n).toElement()); } } @@ -2251,8 +1702,8 @@ bool Message::fromStanza(const Stanza &s, bool useTimeZoneOffset, int timeZoneOf nl = childElementsByTagNameNS(root, "http://jabber.org/protocol/rosterx", "x"); if (nl.count()) { QDomElement t = nl.item(0).toElement(); - nl = t.elementsByTagName("item"); - for(n = 0; n < nl.count(); ++n) { + nl = t.elementsByTagName("item"); + for (n = 0; n < nl.count(); ++n) { RosterExchangeItem it = RosterExchangeItem(nl.item(n).toElement()); if (!it.isNull()) d->rosterExchangeItems += it; @@ -2261,44 +1712,41 @@ bool Message::fromStanza(const Stanza &s, bool useTimeZoneOffset, int timeZoneOf // invite t = childElementsByTagNameNS(root, "jabber:x:conference", "x").item(0).toElement(); - if(!t.isNull()) + if (!t.isNull()) d->invite = t.attribute("jid"); else d->invite = QString(); // nick t = childElementsByTagNameNS(root, "http://jabber.org/protocol/nick", "nick").item(0).toElement(); - if(!t.isNull()) + if (!t.isNull()) d->nick = t.text(); else d->nick = QString(); // sxe t = childElementsByTagNameNS(root, "http://jabber.org/protocol/sxe", "sxe").item(0).toElement(); - if(!t.isNull()) + if (!t.isNull()) d->sxe = t; else d->sxe = QDomElement(); t = childElementsByTagNameNS(root, "http://jabber.org/protocol/muc#user", "x").item(0).toElement(); - if(!t.isNull()) { + if (!t.isNull()) { d->hasMUCUser = true; - for(QDomNode muc_n = t.firstChild(); !muc_n.isNull(); muc_n = muc_n.nextSibling()) { + for (QDomNode muc_n = t.firstChild(); !muc_n.isNull(); muc_n = muc_n.nextSibling()) { QDomElement muc_e = muc_n.toElement(); - if(muc_e.isNull()) + if (muc_e.isNull()) continue; if (muc_e.tagName() == "status") { addMUCStatus(muc_e.attribute("code").toInt()); - } - else if (muc_e.tagName() == "invite") { + } else if (muc_e.tagName() == "invite") { MUCInvite inv(muc_e); if (!inv.isNull()) addMUCInvite(inv); - } - else if (muc_e.tagName() == "decline") { + } else if (muc_e.tagName() == "decline") { setMUCDecline(MUCDecline(muc_e)); - } - else if (muc_e.tagName() == "password") { + } else if (muc_e.tagName() == "password") { setMUCPassword(muc_e.text()); } } @@ -2306,15 +1754,13 @@ bool Message::fromStanza(const Stanza &s, bool useTimeZoneOffset, int timeZoneOf // http auth t = childElementsByTagNameNS(root, "http://jabber.org/protocol/http-auth", "confirm").item(0).toElement(); - if(!t.isNull()){ + if (!t.isNull()) { d->httpAuthRequest = HttpAuthRequest(t); - } - else { + } else { d->httpAuthRequest = HttpAuthRequest(); } - QDomElement captcha = childElementsByTagNameNS(root, "urn:xmpp:captcha", - "captcha").item(0).toElement(); + QDomElement captcha = childElementsByTagNameNS(root, "urn:xmpp:captcha", "captcha").item(0).toElement(); QDomElement xdataRoot = root; if (!captcha.isNull()) { xdataRoot = captcha; @@ -2334,6 +1780,16 @@ bool Message::fromStanza(const Stanza &s, bool useTimeZoneOffset, int timeZoneOf if (!t.isNull()) { d->replaceId = t.attribute("id"); } + + // XEP-0385 SIMS and XEP-0372 Reference + auto references = childElementsByTagNameNS(root, REFERENCE_NS, QString::fromLatin1("reference")); + for (int i = 0; i < references.size(); i++) { + Reference r; + if (r.fromXml(references.at(i).toElement())) { + d->references.append(r); + } + } + return true; } @@ -2345,90 +1801,65 @@ Stanza::Error HttpAuthRequest::denyError(Stanza::Error::Auth, Stanza::Error::Not /*! Constructs request of resource URL \a u, made by method \a m, with transaction id \a i. */ -HttpAuthRequest::HttpAuthRequest(const QString &m, const QString &u, const QString &i) : method_(m), url_(u), id_(i), hasId_(true) +HttpAuthRequest::HttpAuthRequest(const QString &m, const QString &u, const QString &i) : + method_(m), url_(u), id_(i), hasId_(true) { } /*! Constructs request of resource URL \a u, made by method \a m, without transaction id. */ -HttpAuthRequest::HttpAuthRequest(const QString &m, const QString &u) : method_(m), url_(u), hasId_(false) -{ -} +HttpAuthRequest::HttpAuthRequest(const QString &m, const QString &u) : method_(m), url_(u), hasId_(false) { } /*! Constructs request object by reading XML element \a e. */ -HttpAuthRequest::HttpAuthRequest(const QDomElement &e) -{ - fromXml(e); -} +HttpAuthRequest::HttpAuthRequest(const QDomElement &e) { fromXml(e); } /*! Returns true is object is empty (not valid). */ -bool HttpAuthRequest::isEmpty() const -{ - return method_.isEmpty() && url_.isEmpty(); -} +bool HttpAuthRequest::isEmpty() const { return method_.isEmpty() && url_.isEmpty(); } /*! Sets request method. */ -void HttpAuthRequest::setMethod(const QString& m) -{ - method_ = m; -} +void HttpAuthRequest::setMethod(const QString &m) { method_ = m; } /*! Sets requested URL. */ -void HttpAuthRequest::setUrl(const QString& u) -{ - url_ = u; -} +void HttpAuthRequest::setUrl(const QString &u) { url_ = u; } /*! Sets transaction identifier. */ -void HttpAuthRequest::setId(const QString& i) +void HttpAuthRequest::setId(const QString &i) { - id_ = i; + id_ = i; hasId_ = true; } /*! Returns request method. */ -QString HttpAuthRequest::method() const -{ - return method_; -} +QString HttpAuthRequest::method() const { return method_; } /*! Returns requested URL. */ -QString HttpAuthRequest::url() const -{ - return url_; -} +QString HttpAuthRequest::url() const { return url_; } /*! Returns transaction identifier. Empty QString may mean both empty id or no id. Use hasId() to tell the difference. */ -QString HttpAuthRequest::id() const -{ - return id_; -} +QString HttpAuthRequest::id() const { return id_; } /*! Returns true if the request contains transaction id. */ -bool HttpAuthRequest::hasId() const -{ - return hasId_; -} +bool HttpAuthRequest::hasId() const { return hasId_; } /*! Returns XML element representing the request. @@ -2437,13 +1868,12 @@ bool HttpAuthRequest::hasId() const QDomElement HttpAuthRequest::toXml(QDomDocument &doc) const { QDomElement e; - if(isEmpty()) + if (isEmpty()) return e; e = doc.createElementNS("http://jabber.org/protocol/http-auth", "confirm"); - e.setAttribute("xmlns", "http://jabber.org/protocol/http-auth"); - if(hasId_) + if (hasId_) e.setAttribute("id", id_); e.setAttribute("method", method_); e.setAttribute("url", url_); @@ -2456,15 +1886,15 @@ QDomElement HttpAuthRequest::toXml(QDomDocument &doc) const */ bool HttpAuthRequest::fromXml(const QDomElement &e) { - if(e.tagName() != "confirm") + if (e.tagName() != "confirm") return false; hasId_ = e.hasAttribute("id"); - if(hasId_) + if (hasId_) id_ = e.attribute("id"); method_ = e.attribute("method"); - url_ = e.attribute("url"); + url_ = e.attribute("url"); return true; } @@ -2472,19 +1902,13 @@ bool HttpAuthRequest::fromXml(const QDomElement &e) //--------------------------------------------------------------------------- // Subscription //--------------------------------------------------------------------------- -Subscription::Subscription(SubType type) -{ - value = type; -} +Subscription::Subscription(SubType type) { value = type; } -int Subscription::type() const -{ - return value; -} +int Subscription::type() const { return value; } QString Subscription::toString() const { - switch(value) { + switch (value) { case Remove: return "remove"; case Both: @@ -2501,15 +1925,15 @@ QString Subscription::toString() const bool Subscription::fromString(const QString &s) { - if(s == QLatin1String("remove")) + if (s == QLatin1String("remove")) value = Remove; - else if(s == QLatin1String("both")) + else if (s == QLatin1String("both")) value = Both; - else if(s == QLatin1String("from")) + else if (s == QLatin1String("from")) value = From; - else if(s == QLatin1String("to")) + else if (s == QLatin1String("to")) value = To; - else if(s.isEmpty() || s == QLatin1String("none")) + else if (s.isEmpty() || s == QLatin1String("none")) value = None; else return false; @@ -2517,18 +1941,13 @@ bool Subscription::fromString(const QString &s) return true; } - //--------------------------------------------------------------------------- // Status //--------------------------------------------------------------------------- /** * Default constructor. */ -CapsSpec::CapsSpec() : - hashAlgo_(CapsSpec::invalidAlgo) -{ -} - +CapsSpec::CapsSpec() : hashAlgo_(CapsSpec::invalidAlgo) { } /** * \brief Basic constructor. @@ -2536,78 +1955,65 @@ CapsSpec::CapsSpec() : * @param ven the version * @param ext the list of extensions (separated by spaces) */ -CapsSpec::CapsSpec(const QString& node, QCryptographicHash::Algorithm hashAlgo, const QString& ver) - : node_(node) - , ver_(ver) - , hashAlgo_(hashAlgo) -{} +CapsSpec::CapsSpec(const QString &node, QCryptographicHash::Algorithm hashAlgo, const QString &ver) : + node_(node), ver_(ver), hashAlgo_(hashAlgo) +{ +} CapsSpec::CapsSpec(const DiscoItem &disco, QCryptographicHash::Algorithm hashAlgo) : - node_(disco.node().section('#', 0, 0)), - ver_(disco.capsHash(hashAlgo)), - hashAlgo_(hashAlgo) -{} + node_(disco.node().section('#', 0, 0)), ver_(disco.capsHash(hashAlgo)), hashAlgo_(hashAlgo) +{ +} /** * @brief Checks for validity * @return true on valid */ -bool CapsSpec::isValid() const -{ - return !node_.isEmpty() && !ver_.isEmpty() && (hashAlgo_ != CapsSpec::invalidAlgo); -} - +bool CapsSpec::isValid() const { return !node_.isEmpty() && !ver_.isEmpty() && (hashAlgo_ != CapsSpec::invalidAlgo); } /** * \brief Returns the node of the capabilities specification. */ -const QString& CapsSpec::node() const -{ - return node_; -} - +const QString &CapsSpec::node() const { return node_; } /** * \brief Returns the version of the capabilities specification. */ -const QString& CapsSpec::version() const -{ - return ver_; -} +const QString &CapsSpec::version() const { return ver_; } -QCryptographicHash::Algorithm CapsSpec::hashAlgorithm() const -{ - return hashAlgo_; -} +QCryptographicHash::Algorithm CapsSpec::hashAlgorithm() const { return hashAlgo_; } QDomElement CapsSpec::toXml(QDomDocument *doc) const { - QDomElement c = doc->createElement("c"); - c.setAttribute("xmlns", NS_CAPS); - QString algo = cryptoMap().key(hashAlgo_); - c.setAttribute("hash",algo); - c.setAttribute("node",node_); - c.setAttribute("ver",ver_); + QDomElement c = doc->createElementNS(NS_CAPS, "c"); + QString algo = cryptoMap().key(hashAlgo_); + c.setAttribute("hash", algo); + c.setAttribute("node", node_); + c.setAttribute("ver", ver_); return c; } CapsSpec CapsSpec::fromXml(const QDomElement &e) { - QString node = e.attribute("node"); - QString ver = e.attribute("ver"); - QString hashAlgo = e.attribute("hash"); - QString ext = e.attribute("ext"); // deprecated. let it be here till 2018 - CryptoMap &cm = cryptoMap(); - CapsSpec cs; + QString node = e.attribute("node"); + QString ver = e.attribute("ver"); + QString hashAlgo = e.attribute("hash"); + QString ext = e.attribute("ext"); // deprecated. let it be here till 2018 + CryptoMap &cm = cryptoMap(); + CapsSpec cs; if (!node.isEmpty() && !ver.isEmpty()) { QCryptographicHash::Algorithm algo = CapsSpec::invalidAlgo; - CryptoMap::ConstIterator it; + CryptoMap::ConstIterator it; if (!hashAlgo.isEmpty() && (it = cm.constFind(hashAlgo)) != cm.constEnd()) { algo = it.value(); } cs = CapsSpec(node, algo, ver); if (!ext.isEmpty()) { +#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) + cs.ext_ = ext.split(" ", Qt::SkipEmptyParts); +#else cs.ext_ = ext.split(" ", QString::SkipEmptyParts); +#endif } } return cs; @@ -2617,8 +2023,8 @@ CapsSpec::CryptoMap &CapsSpec::cryptoMap() { static CryptoMap cm; if (cm.isEmpty()) { - cm.insert("md5", QCryptographicHash::Md5); - cm.insert("sha-1", QCryptographicHash::Sha1); + cm.insert("md5", QCryptographicHash::Md5); + cm.insert("sha-1", QCryptographicHash::Sha1); cm.insert("sha-224", QCryptographicHash::Sha224); cm.insert("sha-256", QCryptographicHash::Sha256); cm.insert("sha-384", QCryptographicHash::Sha384); @@ -2646,90 +2052,76 @@ QString CapsSpec::flatten() const return QString(); } -void CapsSpec::resetVersion() -{ - ver_.clear(); -} +void CapsSpec::resetVersion() { ver_.clear(); } -bool CapsSpec::operator==(const CapsSpec& s) const +bool CapsSpec::operator==(const CapsSpec &s) const { return (node() == s.node() && version() == s.version() && hashAlgorithm() == s.hashAlgorithm()); } -bool CapsSpec::operator!=(const CapsSpec& s) const -{ - return !((*this) == s); -} +bool CapsSpec::operator!=(const CapsSpec &s) const { return !((*this) == s); } -bool CapsSpec::operator<(const CapsSpec& s) const +bool CapsSpec::operator<(const CapsSpec &s) const { - return (node() != s.node() ? node() < s.node() : - (version() != s.version() ? version() < s.version() : - hashAlgorithm() < s.hashAlgorithm())); + return (node() != s.node() + ? node() < s.node() + : (version() != s.version() ? version() < s.version() : hashAlgorithm() < s.hashAlgorithm())); } - -class StatusPrivate : public QSharedData -{ +class StatusPrivate : public QSharedData { public: StatusPrivate() = default; - int priority = 0; - QString show, status, key; - QDateTime timeStamp; - bool isAvailable = false; - bool isInvisible = false; - QString photoHash; - bool hasPhotoHash = false; + int priority = 0; + QString show, status, key; + QDateTime timeStamp; + bool isAvailable = false; + bool isInvisible = false; + QByteArray photoHash; + bool hasPhotoHash = false; QString xsigned; // gabber song extension - QString songTitle; - CapsSpec caps; + QString songTitle; + CapsSpec caps; QList bobDataList; // MUC - bool isMUC = false; - bool hasMUCItem = false; - bool hasMUCDestroy = false; - MUCItem mucItem; + bool isMUC = false; + bool hasMUCItem = false; + bool hasMUCDestroy = false; + MUCItem mucItem; MUCDestroy mucDestroy; QList mucStatuses; - QString mucPassword; - int mucHistoryMaxChars = -1; - int mucHistoryMaxStanzas = -1; - int mucHistorySeconds = -1; - QDateTime mucHistorySince; + QString mucPassword; + int mucHistoryMaxChars = -1; + int mucHistoryMaxStanzas = -1; + int mucHistorySeconds = -1; + QDateTime mucHistorySince; - int ecode = -1; + int ecode = -1; QString estr; }; - -Status::Status(const QString &show, const QString &status, int priority, bool available) - : d(new StatusPrivate) +Status::Status(const QString &show, const QString &status, int priority, bool available) : d(new StatusPrivate) { d->isAvailable = available; - d->show = show; - d->status = status; - d->priority = priority; - d->timeStamp = QDateTime::currentDateTime(); + d->show = show; + d->status = status; + d->priority = priority; + d->timeStamp = QDateTime::currentDateTime(); d->isInvisible = false; } -Status::Status(Type type, const QString& status, int priority) - : d(new StatusPrivate) +Status::Status(Type type, const QString &status, int priority) : d(new StatusPrivate) { - d->status = status; - d->priority = priority; + d->status = status; + d->priority = priority; d->timeStamp = QDateTime::currentDateTime(); setType(type); } -Status::Status(const Status &other) : - d(other.d) -{ -} +Status::Status(const Status &other) : d(other.d) { } Status &Status::operator=(const Status &other) { @@ -2737,56 +2129,55 @@ Status &Status::operator=(const Status &other) return *this; } -Status::~Status() -{ -} +Status::~Status() { } -bool Status::hasError() const -{ - return (d->ecode != -1); -} +bool Status::hasError() const { return (d->ecode != -1); } void Status::setError(int code, const QString &str) { d->ecode = code; - d->estr = str; + d->estr = str; } -void Status::setIsAvailable(bool available) -{ - d->isAvailable = available; -} +void Status::setIsAvailable(bool available) { d->isAvailable = available; } -void Status::setIsInvisible(bool invisible) -{ - d->isInvisible = invisible; -} +void Status::setIsInvisible(bool invisible) { d->isInvisible = invisible; } -void Status::setPriority(int x) -{ - d->priority = x; -} +void Status::setPriority(int x) { d->priority = x; } void Status::setType(Status::Type _type) { - bool available = true; - bool invisible = false; + bool available = true; + bool invisible = false; QString show; - switch(_type) { - case Away: show = "away"; break; - case FFC: show = "chat"; break; - case XA: show = "xa"; break; - case DND: show = "dnd"; break; - case Offline: available = false; break; - case Invisible: invisible = true; break; - default: break; + switch (_type) { + case Away: + show = "away"; + break; + case FFC: + show = "chat"; + break; + case XA: + show = "xa"; + break; + case DND: + show = "dnd"; + break; + case Offline: + available = false; + break; + case Invisible: + invisible = true; + break; + default: + break; } setShow(show); setIsAvailable(available); setIsInvisible(invisible); } -Status::Type Status::txt2type(const QString& stat) +Status::Type Status::txt2type(const QString &stat) { if (stat == "offline") return XMPP::Status::Offline; @@ -2806,128 +2197,74 @@ Status::Type Status::txt2type(const QString& stat) return XMPP::Status::Away; } -void Status::setType(const QString &stat) -{ - setType(txt2type(stat)); -} +void Status::setType(const QString &stat) { setType(txt2type(stat)); } -void Status::setShow(const QString & _show) -{ - d->show = _show; -} +void Status::setShow(const QString &_show) { d->show = _show; } -void Status::setStatus(const QString & _status) -{ - d->status = _status; -} +void Status::setStatus(const QString &_status) { d->status = _status; } -void Status::setTimeStamp(const QDateTime & _timestamp) -{ - d->timeStamp = _timestamp; -} +void Status::setTimeStamp(const QDateTime &_timestamp) { d->timeStamp = _timestamp; } -void Status::setKeyID(const QString &key) -{ - d->key = key; -} +void Status::setKeyID(const QString &key) { d->key = key; } -void Status::setXSigned(const QString &s) -{ - d->xsigned = s; -} +void Status::setXSigned(const QString &s) { d->xsigned = s; } -void Status::setSongTitle(const QString & _songtitle) -{ - d->songTitle = _songtitle; -} +void Status::setSongTitle(const QString &_songtitle) { d->songTitle = _songtitle; } -void Status::setCaps(const CapsSpec & caps) -{ - d->caps = caps; -} +void Status::setCaps(const CapsSpec &caps) { d->caps = caps; } -void Status::setMUC() -{ - d->isMUC = true; -} +void Status::setMUC() { d->isMUC = true; } -void Status::setMUCItem(const MUCItem& i) +void Status::setMUCItem(const MUCItem &i) { d->hasMUCItem = true; - d->mucItem = i; + d->mucItem = i; } -void Status::setMUCDestroy(const MUCDestroy& i) +void Status::setMUCDestroy(const MUCDestroy &i) { d->hasMUCDestroy = true; - d->mucDestroy = i; + d->mucDestroy = i; } void Status::setMUCHistory(int maxchars, int maxstanzas, int seconds, const QDateTime &since) { - d->mucHistoryMaxChars = maxchars; + d->mucHistoryMaxChars = maxchars; d->mucHistoryMaxStanzas = maxstanzas; - d->mucHistorySeconds = seconds; - d->mucHistorySince = since; + d->mucHistorySeconds = seconds; + d->mucHistorySince = since; } +const QByteArray &Status::photoHash() const { return d->photoHash; } -const QString& Status::photoHash() const -{ - return d->photoHash; -} - -void Status::setPhotoHash(const QString& h) +void Status::setPhotoHash(const QByteArray &h) { - d->photoHash = h; + d->photoHash = h; d->hasPhotoHash = true; } -bool Status::hasPhotoHash() const -{ - return d->hasPhotoHash; -} +bool Status::hasPhotoHash() const { return d->hasPhotoHash; } -void Status::addBoBData(const BoBData &bob) -{ - d->bobDataList.append(bob); -} +void Status::addBoBData(const BoBData &bob) { d->bobDataList.append(bob); } -QList Status::bobDataList() const -{ - return d->bobDataList; -} +QList Status::bobDataList() const { return d->bobDataList; } -bool Status::isAvailable() const -{ - return d->isAvailable; -} +bool Status::isAvailable() const { return d->isAvailable; } -bool Status::isAway() const -{ - return (d->show == "away" || d->show == "xa" || d->show == "dnd"); -} +bool Status::isAway() const { return (d->show == "away" || d->show == "xa" || d->show == "dnd"); } -bool Status::isInvisible() const -{ - return d->isInvisible; -} +bool Status::isInvisible() const { return d->isInvisible; } -int Status::priority() const -{ - return d->priority; -} +int Status::priority() const { return d->priority; } Status::Type Status::type() const { Status::Type type = Status::Online; if (!isAvailable()) { type = Status::Offline; - } - else if (isInvisible()) { + } else if (isInvisible()) { type = Status::Invisible; - } - else { + } else { QString s = show(); if (s == "away") type = Status::Away; @@ -2944,185 +2281,109 @@ Status::Type Status::type() const QString Status::typeString() const { QString stat; - switch(type()) { - case XMPP::Status::Offline: stat = "offline"; break; - case XMPP::Status::Online: stat = "online"; break; - case XMPP::Status::Away: stat = "away"; break; - case XMPP::Status::XA: stat = "xa"; break; - case XMPP::Status::DND: stat = "dnd"; break; - case XMPP::Status::Invisible: stat = "invisible"; break; - case XMPP::Status::FFC: stat = "chat"; break; - default: stat = "away"; + switch (type()) { + case XMPP::Status::Offline: + stat = "offline"; + break; + case XMPP::Status::Online: + stat = "online"; + break; + case XMPP::Status::Away: + stat = "away"; + break; + case XMPP::Status::XA: + stat = "xa"; + break; + case XMPP::Status::DND: + stat = "dnd"; + break; + case XMPP::Status::Invisible: + stat = "invisible"; + break; + case XMPP::Status::FFC: + stat = "chat"; + break; + default: + stat = "away"; } return stat; } -const QString & Status::show() const -{ - return d->show; -} -const QString & Status::status() const -{ - return d->status; -} +const QString &Status::show() const { return d->show; } +const QString &Status::status() const { return d->status; } -QDateTime Status::timeStamp() const -{ - return d->timeStamp; -} +QDateTime Status::timeStamp() const { return d->timeStamp; } -const QString & Status::keyID() const -{ - return d->key; -} +const QString &Status::keyID() const { return d->key; } -const QString & Status::xsigned() const -{ - return d->xsigned; -} +const QString &Status::xsigned() const { return d->xsigned; } -const QString & Status::songTitle() const -{ - return d->songTitle; -} +const QString &Status::songTitle() const { return d->songTitle; } -const CapsSpec & Status::caps() const -{ - return d->caps; -} +const CapsSpec &Status::caps() const { return d->caps; } -bool Status::isMUC() const -{ - return d->isMUC || !d->mucPassword.isEmpty() || hasMUCHistory(); -} +bool Status::isMUC() const { return d->isMUC || !d->mucPassword.isEmpty() || hasMUCHistory(); } -bool Status::hasMUCItem() const -{ - return d->hasMUCItem; -} +bool Status::hasMUCItem() const { return d->hasMUCItem; } -const MUCItem& Status::mucItem() const -{ - return d->mucItem; -} +const MUCItem &Status::mucItem() const { return d->mucItem; } -bool Status::hasMUCDestroy() const -{ - return d->hasMUCDestroy; -} +bool Status::hasMUCDestroy() const { return d->hasMUCDestroy; } -const MUCDestroy& Status::mucDestroy() const -{ - return d->mucDestroy; -} +const MUCDestroy &Status::mucDestroy() const { return d->mucDestroy; } -const QList& Status::getMUCStatuses() const -{ - return d->mucStatuses; -} +const QList &Status::getMUCStatuses() const { return d->mucStatuses; } -void Status::addMUCStatus(int i) -{ - d->mucStatuses += i; -} +void Status::addMUCStatus(int i) { d->mucStatuses += i; } -const QString& Status::mucPassword() const -{ - return d->mucPassword; -} +const QString &Status::mucPassword() const { return d->mucPassword; } bool Status::hasMUCHistory() const { - return d->mucHistoryMaxChars >= 0 || d->mucHistoryMaxStanzas >= 0 || d->mucHistorySeconds >= 0 || !d->mucHistorySince.isNull(); + return d->mucHistoryMaxChars >= 0 || d->mucHistoryMaxStanzas >= 0 || d->mucHistorySeconds >= 0 + || !d->mucHistorySince.isNull(); } -int Status::mucHistoryMaxChars() const -{ - return d->mucHistoryMaxChars; -} +int Status::mucHistoryMaxChars() const { return d->mucHistoryMaxChars; } -int Status::mucHistoryMaxStanzas() const -{ - return d->mucHistoryMaxStanzas; -} +int Status::mucHistoryMaxStanzas() const { return d->mucHistoryMaxStanzas; } -int Status::mucHistorySeconds() const -{ - return d->mucHistorySeconds; -} +int Status::mucHistorySeconds() const { return d->mucHistorySeconds; } -const QDateTime & Status::mucHistorySince() const -{ - return d->mucHistorySince; -} - -void Status::setMUCPassword(const QString& i) -{ - d->mucPassword = i; -} +const QDateTime &Status::mucHistorySince() const { return d->mucHistorySince; } -int Status::errorCode() const -{ - return d->ecode; -} +void Status::setMUCPassword(const QString &i) { d->mucPassword = i; } -const QString & Status::errorString() const -{ - return d->estr; -} +int Status::errorCode() const { return d->ecode; } +const QString &Status::errorString() const { return d->estr; } //--------------------------------------------------------------------------- // Resource //--------------------------------------------------------------------------- -Resource::Resource(const QString &name, const Status &stat) - : v_name(name) - , v_status(stat) -{ -} +Resource::Resource(const QString &name, const Status &stat) : v_name(name), v_status(stat) { } -const QString & Resource::name() const -{ - return v_name; -} +const QString &Resource::name() const { return v_name; } -int Resource::priority() const -{ - return v_status.priority(); -} +int Resource::priority() const { return v_status.priority(); } -const Status & Resource::status() const -{ - return v_status; -} +const Status &Resource::status() const { return v_status; } -void Resource::setName(const QString & _name) -{ - v_name = _name; -} - -void Resource::setStatus(const Status & _status) -{ - v_status = _status; -} +void Resource::setName(const QString &_name) { v_name = _name; } +void Resource::setStatus(const Status &_status) { v_status = _status; } //--------------------------------------------------------------------------- // ResourceList //--------------------------------------------------------------------------- -ResourceList::ResourceList() - :QList() -{ -} +ResourceList::ResourceList() : QList() { } -ResourceList::~ResourceList() -{ -} +ResourceList::~ResourceList() { } -ResourceList::Iterator ResourceList::find(const QString & _find) +ResourceList::Iterator ResourceList::find(const QString &_find) { - for(ResourceList::Iterator it = begin(); it != end(); ++it) { - if((*it).name() == _find) + for (ResourceList::Iterator it = begin(); it != end(); ++it) { + if ((*it).name() == _find) return it; } @@ -3133,18 +2394,18 @@ ResourceList::Iterator ResourceList::priority() { ResourceList::Iterator highest = end(); - for(ResourceList::Iterator it = begin(); it != end(); ++it) { - if(highest == end() || (*it).priority() > (*highest).priority()) + for (ResourceList::Iterator it = begin(); it != end(); ++it) { + if (highest == end() || (*it).priority() > (*highest).priority()) highest = it; } return highest; } -ResourceList::ConstIterator ResourceList::find(const QString & _find) const +ResourceList::ConstIterator ResourceList::find(const QString &_find) const { - for(ResourceList::ConstIterator it = begin(); it != end(); ++it) { - if((*it).name() == _find) + for (ResourceList::ConstIterator it = begin(); it != end(); ++it) { + if ((*it).name() == _find) return it; } @@ -3155,110 +2416,63 @@ ResourceList::ConstIterator ResourceList::priority() const { ResourceList::ConstIterator highest = end(); - for(ResourceList::ConstIterator it = begin(); it != end(); ++it) { - if(highest == end() || (*it).priority() > (*highest).priority()) + for (ResourceList::ConstIterator it = begin(); it != end(); ++it) { + if (highest == end() || (*it).priority() > (*highest).priority()) highest = it; } return highest; } - //--------------------------------------------------------------------------- // RosterItem //--------------------------------------------------------------------------- -RosterItem::RosterItem(const Jid &_jid) : - v_jid(_jid), - v_push(false) -{ -} +RosterItem::RosterItem(const Jid &_jid) : v_jid(_jid), v_push(false) { } RosterItem::RosterItem(const RosterItem &item) : - v_jid(item.v_jid), - v_name(item.v_name), - v_groups(item.v_groups), - v_subscription(item.v_subscription), - v_ask(item.v_ask), - v_push(item.v_push) + v_jid(item.v_jid), v_name(item.v_name), v_groups(item.v_groups), v_subscription(item.v_subscription), + v_ask(item.v_ask), v_push(item.v_push) { } -RosterItem::~RosterItem() -{ -} +RosterItem::~RosterItem() { } -const Jid & RosterItem::jid() const -{ - return v_jid; -} +const Jid &RosterItem::jid() const { return v_jid; } -const QString & RosterItem::name() const -{ - return v_name; -} +const QString &RosterItem::name() const { return v_name; } -const QStringList & RosterItem::groups() const -{ - return v_groups; -} +const QStringList &RosterItem::groups() const { return v_groups; } -const Subscription & RosterItem::subscription() const -{ - return v_subscription; -} +const Subscription &RosterItem::subscription() const { return v_subscription; } -const QString & RosterItem::ask() const -{ - return v_ask; -} +const QString &RosterItem::ask() const { return v_ask; } -bool RosterItem::isPush() const -{ - return v_push; -} +bool RosterItem::isPush() const { return v_push; } bool RosterItem::inGroup(const QString &g) const { - for(QStringList::ConstIterator it = v_groups.begin(); it != v_groups.end(); ++it) { - if(*it == g) + for (const auto &vgroup : v_groups) { + if (vgroup == g) return true; } return false; } -void RosterItem::setJid(const Jid &_jid) -{ - v_jid = _jid; -} +void RosterItem::setJid(const Jid &_jid) { v_jid = _jid; } -void RosterItem::setName(const QString &_name) -{ - v_name = _name; -} +void RosterItem::setName(const QString &_name) { v_name = _name; } -void RosterItem::setGroups(const QStringList &_groups) -{ - v_groups = _groups; -} +void RosterItem::setGroups(const QStringList &_groups) { v_groups = _groups; } -void RosterItem::setSubscription(const Subscription &type) -{ - v_subscription = type; -} +void RosterItem::setSubscription(const Subscription &type) { v_subscription = type; } -void RosterItem::setAsk(const QString &_ask) -{ - v_ask = _ask; -} +void RosterItem::setAsk(const QString &_ask) { v_ask = _ask; } -void RosterItem::setIsPush(bool b) -{ - v_push = b; -} +void RosterItem::setIsPush(bool b) { v_push = b; } bool RosterItem::addGroup(const QString &g) { - if(inGroup(g)) + if (inGroup(g)) return false; v_groups += g; @@ -3267,8 +2481,8 @@ bool RosterItem::addGroup(const QString &g) bool RosterItem::removeGroup(const QString &g) { - for(QStringList::Iterator it = v_groups.begin(); it != v_groups.end(); ++it) { - if(*it == g) { + for (QStringList::Iterator it = v_groups.begin(); it != v_groups.end(); ++it) { + if (*it == g) { v_groups.erase(it); return true; } @@ -3283,65 +2497,76 @@ QDomElement RosterItem::toXml(QDomDocument *doc) const item.setAttribute("jid", v_jid.full()); item.setAttribute("name", v_name); item.setAttribute("subscription", v_subscription.toString()); - if(!v_ask.isEmpty()) + if (!v_ask.isEmpty()) item.setAttribute("ask", v_ask); - for(QStringList::ConstIterator it = v_groups.begin(); it != v_groups.end(); ++it) - item.appendChild(textTag(doc, "group", *it)); + for (const auto &vgroup : v_groups) + item.appendChild(textTag(doc, "group", vgroup)); return item; } bool RosterItem::fromXml(const QDomElement &item) { - if(item.tagName() != "item") + if (item.tagName() != "item") return false; Jid j(item.attribute("jid")); - if(!j.isValid()) + if (!j.isValid()) return false; QString na = item.attribute("name"); Subscription s; - if(!s.fromString(item.attribute("subscription")) ) + if (!s.fromString(item.attribute("subscription"))) return false; QStringList g; - for(QDomNode n = item.firstChild(); !n.isNull(); n = n.nextSibling()) { + for (QDomNode n = item.firstChild(); !n.isNull(); n = n.nextSibling()) { QDomElement i = n.toElement(); - if(i.isNull()) + if (i.isNull()) continue; - if(i.tagName() == "group") + if (i.tagName() == "group") g += tagContent(i); } QString a = item.attribute("ask"); - v_jid = j; - v_name = na; + v_jid = j; + v_name = na; v_subscription = s; - v_groups = g; - v_ask = a; + v_groups = g; + v_ask = a; return true; } - //--------------------------------------------------------------------------- // Roster //--------------------------------------------------------------------------- -Roster::Roster() - : QList() +class Roster::Private { +public: + QString groupsDelimiter; +}; + +Roster::Roster() : QList(), d(new Roster::Private) { } + +Roster::~Roster() { delete d; } + +Roster::Roster(const Roster &other) : QList(other), d(new Roster::Private) { + d->groupsDelimiter = other.d->groupsDelimiter; } -Roster::~Roster() +Roster &Roster::operator=(const Roster &other) { + QList::operator=(other); + d->groupsDelimiter = other.d->groupsDelimiter; + return *this; } Roster::Iterator Roster::find(const Jid &j) { - for(Roster::Iterator it = begin(); it != end(); ++it) { - if((*it).jid().compare(j)) + for (Roster::Iterator it = begin(); it != end(); ++it) { + if ((*it).jid().compare(j)) return it; } @@ -3350,14 +2575,17 @@ Roster::Iterator Roster::find(const Jid &j) Roster::ConstIterator Roster::find(const Jid &j) const { - for(Roster::ConstIterator it = begin(); it != end(); ++it) { - if((*it).jid().compare(j)) + for (Roster::ConstIterator it = begin(); it != end(); ++it) { + if ((*it).jid().compare(j)) return it; } return end(); } +void Roster::setGroupsDelimiter(const QString &groupsDelimiter) { d->groupsDelimiter = groupsDelimiter; } + +QString Roster::groupsDelimiter() const { return d->groupsDelimiter; } //--------------------------------------------------------------------------- // FormField @@ -3365,291 +2593,231 @@ Roster::ConstIterator Roster::find(const Jid &j) const FormField::FormField(const QString &type, const QString &value) { v_type = misc; - if(!type.isEmpty()) { + if (!type.isEmpty()) { int x = tagNameToType(type); - if(x != -1) + if (x != -1) v_type = x; } v_value = value; } -FormField::~FormField() -{ -} +FormField::~FormField() { } -int FormField::type() const -{ - return v_type; -} +int FormField::type() const { return v_type; } -QString FormField::realName() const -{ - return typeToTagName(v_type); -} +QString FormField::realName() const { return typeToTagName(v_type); } QString FormField::fieldName() const { - switch(v_type) { - case username: return QObject::tr("Username"); - case nick: return QObject::tr("Nickname"); - case password: return QObject::tr("Password"); - case name: return QObject::tr("Name"); - case first: return QObject::tr("First Name"); - case last: return QObject::tr("Last Name"); - case email: return QObject::tr("E-mail"); - case address: return QObject::tr("Address"); - case city: return QObject::tr("City"); - case state: return QObject::tr("State"); - case zip: return QObject::tr("Zipcode"); - case phone: return QObject::tr("Phone"); - case url: return QObject::tr("URL"); - case date: return QObject::tr("Date"); - case misc: return QObject::tr("Misc"); - default: return ""; + switch (v_type) { + case username: + return QObject::tr("Username"); + case nick: + return QObject::tr("Nickname"); + case password: + return QObject::tr("Password"); + case name: + return QObject::tr("Name"); + case first: + return QObject::tr("First Name"); + case last: + return QObject::tr("Last Name"); + case email: + return QObject::tr("E-mail"); + case address: + return QObject::tr("Address"); + case city: + return QObject::tr("City"); + case state: + return QObject::tr("State"); + case zip: + return QObject::tr("Zipcode"); + case phone: + return QObject::tr("Phone"); + case url: + return QObject::tr("URL"); + case date: + return QObject::tr("Date"); + case misc: + return QObject::tr("Misc"); + default: + return ""; }; } -bool FormField::isSecret() const -{ - return (type() == password); -} +bool FormField::isSecret() const { return (type() == password); } -const QString & FormField::value() const -{ - return v_value; -} +const QString &FormField::value() const { return v_value; } -void FormField::setType(int x) -{ - v_type = x; -} +void FormField::setType(int x) { v_type = x; } bool FormField::setType(const QString &in) { int x = tagNameToType(in); - if(x == -1) + if (x == -1) return false; v_type = x; return true; } -void FormField::setValue(const QString &in) -{ - v_value = in; -} +void FormField::setValue(const QString &in) { v_value = in; } int FormField::tagNameToType(const QString &in) const { - if(!in.compare("username")) return username; - if(!in.compare("nick")) return nick; - if(!in.compare("password")) return password; - if(!in.compare("name")) return name; - if(!in.compare("first")) return first; - if(!in.compare("last")) return last; - if(!in.compare("email")) return email; - if(!in.compare("address")) return address; - if(!in.compare("city")) return city; - if(!in.compare("state")) return state; - if(!in.compare("zip")) return zip; - if(!in.compare("phone")) return phone; - if(!in.compare("url")) return url; - if(!in.compare("date")) return date; - if(!in.compare("misc")) return misc; + if (!in.compare("username")) + return username; + if (!in.compare("nick")) + return nick; + if (!in.compare("password")) + return password; + if (!in.compare("name")) + return name; + if (!in.compare("first")) + return first; + if (!in.compare("last")) + return last; + if (!in.compare("email")) + return email; + if (!in.compare("address")) + return address; + if (!in.compare("city")) + return city; + if (!in.compare("state")) + return state; + if (!in.compare("zip")) + return zip; + if (!in.compare("phone")) + return phone; + if (!in.compare("url")) + return url; + if (!in.compare("date")) + return date; + if (!in.compare("misc")) + return misc; return -1; } QString FormField::typeToTagName(int type) const { - switch(type) { - case username: return "username"; - case nick: return "nick"; - case password: return "password"; - case name: return "name"; - case first: return "first"; - case last: return "last"; - case email: return "email"; - case address: return "address"; - case city: return "city"; - case state: return "state"; - case zip: return "zipcode"; - case phone: return "phone"; - case url: return "url"; - case date: return "date"; - case misc: return "misc"; - default: return ""; + switch (type) { + case username: + return "username"; + case nick: + return "nick"; + case password: + return "password"; + case name: + return "name"; + case first: + return "first"; + case last: + return "last"; + case email: + return "email"; + case address: + return "address"; + case city: + return "city"; + case state: + return "state"; + case zip: + return "zipcode"; + case phone: + return "phone"; + case url: + return "url"; + case date: + return "date"; + case misc: + return "misc"; + default: + return ""; }; } - //--------------------------------------------------------------------------- // Form //--------------------------------------------------------------------------- -Form::Form(const Jid &j) : QList() -{ - setJid(j); -} +Form::Form(const Jid &j) : QList() { setJid(j); } -Form::~Form() -{ -} +Form::~Form() { } -Jid Form::jid() const -{ - return v_jid; -} +Jid Form::jid() const { return v_jid; } -QString Form::instructions() const -{ - return v_instructions; -} +QString Form::instructions() const { return v_instructions; } -QString Form::key() const -{ - return v_key; -} - -void Form::setJid(const Jid &j) -{ - v_jid = j; -} +QString Form::key() const { return v_key; } -void Form::setInstructions(const QString &s) -{ - v_instructions = s; -} +void Form::setJid(const Jid &j) { v_jid = j; } -void Form::setKey(const QString &s) -{ - v_key = s; -} +void Form::setInstructions(const QString &s) { v_instructions = s; } +void Form::setKey(const QString &s) { v_key = s; } //--------------------------------------------------------------------------- // SearchResult //--------------------------------------------------------------------------- -SearchResult::SearchResult(const Jid &jid) -{ - setJid(jid); -} +SearchResult::SearchResult(const Jid &jid) { setJid(jid); } -SearchResult::~SearchResult() -{ -} +SearchResult::~SearchResult() { } -const Jid & SearchResult::jid() const -{ - return v_jid; -} +const Jid &SearchResult::jid() const { return v_jid; } -const QString & SearchResult::nick() const -{ - return v_nick; -} +const QString &SearchResult::nick() const { return v_nick; } -const QString & SearchResult::first() const -{ - return v_first; -} +const QString &SearchResult::first() const { return v_first; } -const QString & SearchResult::last() const -{ - return v_last; -} +const QString &SearchResult::last() const { return v_last; } -const QString & SearchResult::email() const -{ - return v_email; -} +const QString &SearchResult::email() const { return v_email; } -void SearchResult::setJid(const Jid &jid) -{ - v_jid = jid; -} +void SearchResult::setJid(const Jid &jid) { v_jid = jid; } -void SearchResult::setNick(const QString &nick) -{ - v_nick = nick; -} +void SearchResult::setNick(const QString &nick) { v_nick = nick; } -void SearchResult::setFirst(const QString &first) -{ - v_first = first; -} +void SearchResult::setFirst(const QString &first) { v_first = first; } -void SearchResult::setLast(const QString &last) -{ - v_last = last; -} +void SearchResult::setLast(const QString &last) { v_last = last; } -void SearchResult::setEmail(const QString &email) -{ - v_email = email; -} +void SearchResult::setEmail(const QString &email) { v_email = email; } -PubSubItem::PubSubItem() -{ -} +PubSubItem::PubSubItem() { } -PubSubItem::PubSubItem(const QString& id, const QDomElement& payload) : id_(id), payload_(payload) -{ -} +PubSubItem::PubSubItem(const QString &id, const QDomElement &payload) : id_(id), payload_(payload) { } -const QString& PubSubItem::id() const -{ - return id_; -} - -const QDomElement& PubSubItem::payload() const -{ - return payload_; -} - - -PubSubRetraction::PubSubRetraction() -{ -} +const QString &PubSubItem::id() const { return id_; } -PubSubRetraction::PubSubRetraction(const QString& id) : id_(id) -{ -} +const QDomElement &PubSubItem::payload() const { return payload_; } -const QString& PubSubRetraction::id() const -{ - return id_; -} +PubSubRetraction::PubSubRetraction() { } +PubSubRetraction::PubSubRetraction(const QString &id) : id_(id) { } +const QString &PubSubRetraction::id() const { return id_; } // ========================================= // CaptchaChallenge // ========================================= -class CaptchaChallengePrivate : public QSharedData -{ +class CaptchaChallengePrivate : public QSharedData { public: - CaptchaChallengePrivate() : - state(CaptchaChallenge::New) {} + CaptchaChallengePrivate() : state(CaptchaChallenge::New) { } CaptchaChallenge::State state; - Jid arbiter; - Jid offendedJid; - XData form; - QDateTime dt; - QString explanation; - UrlList urls; + Jid arbiter; + Jid offendedJid; + XData form; + QDateTime dt; + QString explanation; + UrlList urls; }; -CaptchaChallenge::CaptchaChallenge() : - d(new CaptchaChallengePrivate) -{} +CaptchaChallenge::CaptchaChallenge() : d(new CaptchaChallengePrivate) { } -CaptchaChallenge::CaptchaChallenge(const CaptchaChallenge &other) : - d(other.d) -{} +CaptchaChallenge::CaptchaChallenge(const CaptchaChallenge &other) : d(other.d) { } -CaptchaChallenge::CaptchaChallenge(const Message &m) : - d(new CaptchaChallengePrivate) +CaptchaChallenge::CaptchaChallenge(const Message &m) : d(new CaptchaChallengePrivate) { if (m.spooled()) { if (m.timeStamp().secsTo(QDateTime::currentDateTime()) < Timeout) { @@ -3663,23 +2831,20 @@ CaptchaChallenge::CaptchaChallenge(const Message &m) : if (m.getForm().registrarType() != "urn:xmpp:captcha" || m.getForm().type() != XData::Data_Form) return; - if (m.id().isEmpty() || m.getForm().getField("challenge").value().value(0) !=m.id()) + if (m.id().isEmpty() || m.getForm().getField("challenge").value().value(0) != m.id()) return; if (m.getForm().getField("from").value().value(0).isEmpty()) return; - d->form = m.getForm(); + d->form = m.getForm(); d->explanation = m.body(); - d->urls = m.urlList(); - d->arbiter = m.from(); + d->urls = m.urlList(); + d->arbiter = m.from(); d->offendedJid = Jid(m.getForm().getField("from").value().value(0)); } -CaptchaChallenge::~CaptchaChallenge() -{ - -} +CaptchaChallenge::~CaptchaChallenge() { } CaptchaChallenge &CaptchaChallenge::operator=(const CaptchaChallenge &from) { @@ -3687,25 +2852,13 @@ CaptchaChallenge &CaptchaChallenge::operator=(const CaptchaChallenge &from) return *this; } -const XData &CaptchaChallenge::form() const -{ - return d->form; -} +const XData &CaptchaChallenge::form() const { return d->form; } -QString CaptchaChallenge::explanation() const -{ - return d->explanation; -} +QString CaptchaChallenge::explanation() const { return d->explanation; } -const UrlList &CaptchaChallenge::urls() const -{ - return d->urls; -} +const UrlList &CaptchaChallenge::urls() const { return d->urls; } -CaptchaChallenge::State CaptchaChallenge::state() const -{ - return d->state; -} +CaptchaChallenge::State CaptchaChallenge::state() const { return d->state; } CaptchaChallenge::Result CaptchaChallenge::validateResponse(const XData &xd) { @@ -3716,28 +2869,21 @@ CaptchaChallenge::Result CaptchaChallenge::validateResponse(const XData &xd) bool CaptchaChallenge::isValid() const { - return d->dt.isValid() && - d->dt.secsTo(QDateTime::currentDateTime()) < Timeout && - d->form.fields().count() > 0; + return d->dt.isValid() && d->dt.secsTo(QDateTime::currentDateTime()) < Timeout && d->form.fields().count() > 0; } -const Jid &CaptchaChallenge::offendedJid() const -{ - return d->offendedJid; -} +const Jid &CaptchaChallenge::offendedJid() const { return d->offendedJid; } -const Jid &CaptchaChallenge::arbiter() const -{ - return d->arbiter; -} +const Jid &CaptchaChallenge::arbiter() const { return d->arbiter; } Thumbnail::Thumbnail(const QDomElement &el) { - if(el.attribute("xmlns") == QLatin1String(XMPP_THUMBS_NS)) { - uri = QUrl(el.attribute("uri"), QUrl::StrictMode); + QString ns(QLatin1String(XMPP_THUMBS_NS)); + if (el.namespaceURI() == ns) { + uri = QUrl(el.attribute("uri"), QUrl::StrictMode); mimeType = el.attribute("mime-type"); - width = el.attribute("width").toUInt(); - height = el.attribute("height").toUInt(); + width = el.attribute("width").toUInt(); + height = el.attribute("height").toUInt(); } } @@ -3752,5 +2898,4 @@ QDomElement Thumbnail::toXml(QDomDocument *doc) const } return el; } - -} +} // namespace XMPP diff --git a/src/xmpp/xmpp-im/xmpp_address.h b/src/xmpp/xmpp-im/xmpp_address.h index 8af4bab4..24115fda 100644 --- a/src/xmpp/xmpp-im/xmpp_address.h +++ b/src/xmpp/xmpp-im/xmpp_address.h @@ -19,47 +19,46 @@ #ifndef XMPP_ADDRESS_H #define XMPP_ADDRESS_H -#include - +#include "iris/xmpp_stanza.h" #include "xmpp/jid/jid.h" +#include + class QDomElement; -namespace XMPP -{ - class Address - { - public: - typedef enum { Unknown, To, Cc, Bcc, ReplyTo, ReplyRoom, NoReply, OriginalFrom, OriginalTo } Type; +namespace XMPP { +class Address { +public: + typedef enum { Unknown, To, Cc, Bcc, ReplyTo, ReplyRoom, NoReply, OriginalFrom, OriginalTo } Type; - Address(Type type = Unknown, const Jid& jid = Jid()); - Address(const QDomElement&); + Address(Type type = Unknown, const Jid &jid = Jid()); + Address(const QDomElement &); - const Jid& jid() const; - const QString& uri() const; - const QString& node() const; - const QString& desc() const; - bool delivered() const; - Type type() const; + const Jid &jid() const; + const QString &uri() const; + const QString &node() const; + const QString &desc() const; + bool delivered() const; + Type type() const; - QDomElement toXml(Stanza&) const; - void fromXml(const QDomElement& t); + QDomElement toXml(Stanza &) const; + void fromXml(const QDomElement &t); - void setJid(const Jid &); - void setUri(const QString &); - void setNode(const QString &); - void setDesc(const QString &); - void setDelivered(bool); - void setType(Type); + void setJid(const Jid &); + void setUri(const QString &); + void setNode(const QString &); + void setDesc(const QString &); + void setDelivered(bool); + void setType(Type); - private: - Jid v_jid; - QString v_uri, v_node, v_desc; - bool v_delivered; - Type v_type; - }; +private: + Jid v_jid; + QString v_uri, v_node, v_desc; + bool v_delivered; + Type v_type; +}; - typedef QList
AddressList; -} +typedef QList
AddressList; +} // namespace XMPP -#endif +#endif // XMPP_ADDRESS_H diff --git a/src/xmpp/xmpp-im/xmpp_agentitem.h b/src/xmpp/xmpp-im/xmpp_agentitem.h index 85107c13..fb5e4a4f 100644 --- a/src/xmpp/xmpp-im/xmpp_agentitem.h +++ b/src/xmpp/xmpp-im/xmpp_agentitem.h @@ -20,35 +20,33 @@ #ifndef XMPP_AGENTITEM #define XMPP_AGENTITEM -#include - #include "xmpp/jid/jid.h" #include "xmpp_features.h" -namespace XMPP { - class AgentItem - { - public: - AgentItem() { } - - const Jid & jid() const { return v_jid; } - const QString & name() const { return v_name; } - const QString & category() const { return v_category; } - const QString & type() const { return v_type; } - const Features & features() const { return v_features; } - - void setJid(const Jid &j) { v_jid = j; } - void setName(const QString &n) { v_name = n; } - void setCategory(const QString &c) { v_category = c; } - void setType(const QString &t) { v_type = t; } - void setFeatures(const Features &f) { v_features = f; } - - private: - Jid v_jid; - QString v_name, v_category, v_type; - Features v_features; - }; -} - -#endif +#include +namespace XMPP { +class AgentItem { +public: + AgentItem() { } + + const Jid &jid() const { return v_jid; } + const QString &name() const { return v_name; } + const QString &category() const { return v_category; } + const QString &type() const { return v_type; } + const Features &features() const { return v_features; } + + void setJid(const Jid &j) { v_jid = j; } + void setName(const QString &n) { v_name = n; } + void setCategory(const QString &c) { v_category = c; } + void setType(const QString &t) { v_type = t; } + void setFeatures(const Features &f) { v_features = f; } + +private: + Jid v_jid; + QString v_name, v_category, v_type; + Features v_features; +}; +} // namespace XMPP + +#endif // XMPP_AGENTITEM diff --git a/src/xmpp/xmpp-im/xmpp_bitsofbinary.cpp b/src/xmpp/xmpp-im/xmpp_bitsofbinary.cpp index a431568c..9cf164f7 100644 --- a/src/xmpp/xmpp-im/xmpp_bitsofbinary.cpp +++ b/src/xmpp/xmpp-im/xmpp_bitsofbinary.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010 Rion + * Copyright (C) 2010 Sergey Ilinykh * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public @@ -16,158 +16,122 @@ * */ -#include -#include - #include "xmpp_bitsofbinary.h" -#include "xmpp_xmlcommon.h" + #include "xmpp_client.h" +#include "xmpp_hash.h" #include "xmpp_tasks.h" +#include "xmpp_xmlcommon.h" + +#include +#include using namespace XMPP; -class BoBData::Private : public QSharedData -{ +class BoBData::Private : public QSharedData { public: - QByteArray data; - QString type; - QString cid; - unsigned int maxAge; + QByteArray data; // file data itself + QString type; // mime type. e.g. image/png + // QString cid; // content identifier without "cid:" + Hash hash; + unsigned int maxAge; // seconds to live }; -BoBData::BoBData() - : d(new Private) -{ +BoBData::BoBData() : d(new Private) { } -} +BoBData::BoBData(const BoBData &other) : d(other.d) { } -BoBData::BoBData(const BoBData &other) - : d(other.d) -{ +BoBData::BoBData(const QDomElement &e) : d(new Private) { fromXml(e); } -} - -BoBData::BoBData(const QDomElement &e) - : d(new Private) -{ - fromXml(e); -} - -BoBData::~BoBData() -{ +BoBData::~BoBData() { } -} - -BoBData & BoBData::operator=(const BoBData &other) +BoBData &BoBData::operator=(const BoBData &other) { - if (this==&other) return *this; //Protect against self-assignment + if (this == &other) + return *this; // Protect against self-assignment d = other.d; return *this; } -bool BoBData::isNull() const +bool BoBData::isNull() const { return !d->hash.isValid() || d->data.isNull(); } + +Hash BoBData::cidToHash(const QString &cid) { - return d->cid.isEmpty() || d->data.isNull(); + if (!cid.endsWith(QLatin1String("@bob.xmpp.org"))) + return Hash(); + return Hash::from(QStringView { cid }.left(cid.size() - sizeof("@bob.xmpp.org") + 1)); } QString BoBData::cid() const { - return d->cid; + if (isNull()) + return QString(); + return QString("%1+%2@bob.xmpp.org").arg(d->hash.stringType(), QString::fromLatin1(d->hash.data().toHex())); } -void BoBData::setCid(const QString &cid) -{ - d->cid = cid; -} +void BoBData::setCid(const QString &cid) { d->hash = cidToHash(cid); } -QByteArray BoBData::data() const -{ - return d->data; -} +const Hash &BoBData::hash() const { return d->hash; } -void BoBData::setData(const QByteArray &data) -{ - d->data = data; -} +void BoBData::setHash(const Hash &hash) { d->hash = hash; } -QString BoBData::type() const -{ - return d->type; -} +QByteArray BoBData::data() const { return d->data; } -void BoBData::setType(const QString &type) -{ - d->type = type; -} +void BoBData::setData(const QByteArray &data) { d->data = data; } -unsigned int BoBData::maxAge() const -{ - return d->maxAge; -} +QString BoBData::type() const { return d->type; } -void BoBData::setMaxAge(unsigned int maxAge) -{ - d->maxAge = maxAge; -} +void BoBData::setType(const QString &type) { d->type = type; } + +unsigned int BoBData::maxAge() const { return d->maxAge; } + +void BoBData::setMaxAge(unsigned int maxAge) { d->maxAge = maxAge; } void BoBData::fromXml(const QDomElement &data) { - d->cid = data.attribute("cid"); - d->maxAge = data.attribute("max-age").toInt(); - d->type = data.attribute("type"); - d->data = QCA::Base64().stringToArray(data.text().replace("\n","")) - .toByteArray(); + setCid(data.attribute("cid")); + d->maxAge = data.attribute("max-age").toUInt(); + d->type = data.attribute("type"); + d->data = QByteArray::fromBase64(data.text().replace("\n", "").toLatin1()); } QDomElement BoBData::toXml(QDomDocument *doc) const { - QDomElement data = doc->createElement("data"); - data.setAttribute("xmlns", "urn:xmpp:bob"); - data.setAttribute("cid", d->cid); + QDomElement data = doc->createElementNS("urn:xmpp:bob", "data"); + data.setAttribute("cid", cid()); data.setAttribute("max-age", d->maxAge); data.setAttribute("type", d->type); - data.appendChild(doc->createTextNode(QCA::Base64().arrayToString(d->data))); + data.appendChild(doc->createTextNode(QString::fromLatin1(d->data.toBase64()))); return data; } - - // --------------------------------------------------------- // BoBCache // --------------------------------------------------------- -BoBCache::BoBCache(QObject *parent) - : QObject(parent) -{ - -} - +BoBCache::BoBCache(QObject *parent) : QObject(parent) { } //------------------------------------------------------------------------------ // BoBManager //------------------------------------------------------------------------------ -BoBManager::BoBManager(Client *client) - : QObject(client) - , _cache(0) -{ - new JT_BoBServer(client->rootTask()); -} +BoBManager::BoBManager(Client *client) : QObject(client), _cache(nullptr) { new JT_BoBServer(client->rootTask()); } -void BoBManager::setCache(BoBCache *cache) -{ - _cache = cache; -} +void BoBManager::setCache(BoBCache *cache) { _cache = cache; } BoBData BoBManager::bobData(const QString &cid) { BoBData bd; + Hash h = BoBData::cidToHash(cid); if (_cache) { - bd = _cache->get(cid); + bd = _cache->get(h); } - if (bd.isNull() && _localFiles.contains(cid)) { - QPair fileData = _localFiles.value(cid); - QFile file(fileData.first); + if (!bd.isNull()) + return bd; + auto it = _localFiles.find(h); + if (it != _localFiles.end()) { + QPair fileData = it.value(); + QFile file(fileData.first); if (file.open(QIODevice::ReadOnly)) { - bd.setCid(cid); + bd.setHash(h); bd.setData(file.readAll()); bd.setMaxAge(0); bd.setType(fileData.second); @@ -176,12 +140,10 @@ BoBData BoBManager::bobData(const QString &cid) return bd; } -BoBData BoBManager::append(const QByteArray &data, const QString &type, - unsigned int maxAge) +BoBData BoBManager::append(const QByteArray &data, const QString &type, unsigned int maxAge) { BoBData b; - b.setCid(QString("sha1+%1@bob.xmpp.org").arg(QString( - QCryptographicHash::hash(data, QCryptographicHash::Sha1).toHex()))); + b.setHash(Hash::from(Hash::Sha1, data)); b.setData(data); b.setMaxAge(maxAge); b.setType(type); @@ -191,20 +153,20 @@ BoBData BoBManager::append(const QByteArray &data, const QString &type, return b; } -QString BoBManager::append(QFile &file, const QString &type) +XMPP::Hash BoBManager::append(QFile &file, const QString &type) { bool isOpen = file.isOpen(); if (isOpen || file.open(QIODevice::ReadOnly)) { - QString cid = QString("sha1+%1@bob.xmpp.org").arg( - QString(QCryptographicHash::hash(file.readAll(), - QCryptographicHash::Sha1).toHex())); - _localFiles[cid] = QPair(file.fileName(), type); + Hash h = Hash::from(Hash::Sha1, &file); + if (h.isValid()) { + _localFiles[h] = QPair(file.fileName(), type); + } if (!isOpen) { file.close(); } - return cid; + return h; } - return QString(); + return XMPP::Hash(); } void BoBManager::append(const BoBData &data) diff --git a/src/xmpp/xmpp-im/xmpp_bitsofbinary.h b/src/xmpp/xmpp-im/xmpp_bitsofbinary.h index 0399730e..9c9f45f0 100644 --- a/src/xmpp/xmpp-im/xmpp_bitsofbinary.h +++ b/src/xmpp/xmpp-im/xmpp_bitsofbinary.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010 Rion + * Copyright (C) 2010 Sergey Ilinykh * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public @@ -19,85 +19,82 @@ #ifndef XMPP_BITSOFBINARY_H #define XMPP_BITSOFBINARY_H +#include "xmpp/jid/jid.h" +#include "xmpp_hash.h" + #include -#include +#include #include +#include #include -#include - -#include "xmpp/jid/jid.h" - -namespace XMPP -{ - class JT_BitsOfBinary; - class Client; - - class BoBData - { - class Private; - public: - BoBData(); - BoBData(const BoBData &other); - BoBData(const QDomElement &); - ~BoBData(); - BoBData &operator=(const BoBData &other); - bool isNull() const; +namespace XMPP { +class Client; +class JT_BitsOfBinary; - QString cid() const; - void setCid(const QString &); +class BoBData { + class Private; - QByteArray data() const; - void setData(const QByteArray &); +public: + BoBData(); + BoBData(const BoBData &other); + BoBData(const QDomElement &); + ~BoBData(); + BoBData &operator=(const BoBData &other); - QString type() const; - void setType(const QString &); + bool isNull() const; - unsigned int maxAge() const; - void setMaxAge(unsigned int); + static Hash cidToHash(const QString &cid); - void fromXml(const QDomElement &); - QDomElement toXml(QDomDocument *doc) const; + QString cid() const; + void setCid(const QString &); - private: - QSharedDataPointer d; - }; + const Hash &hash() const; + void setHash(const Hash &hash); + QByteArray data() const; + void setData(const QByteArray &); + QString type() const; + void setType(const QString &); - class BoBCache : public QObject - { - Q_OBJECT + unsigned int maxAge() const; + void setMaxAge(unsigned int); - public: - BoBCache(QObject *parent); - virtual void put(const BoBData &) = 0; - virtual BoBData get(const QString &) = 0; - }; + void fromXml(const QDomElement &); + QDomElement toXml(QDomDocument *doc) const; +private: + QSharedDataPointer d; +}; +class BoBCache : public QObject { + Q_OBJECT - class BoBManager : public QObject - { - Q_OBJECT +public: + BoBCache(QObject *parent); + virtual void put(const BoBData &) = 0; + virtual BoBData get(const Hash &) = 0; +}; - public: - BoBManager(Client *); - void setCache(BoBCache*); +class BoBManager : public QObject { + Q_OBJECT - BoBData bobData(const QString &); - // file data, mime type, max age in seconds - BoBData append(const QByteArray &data, const QString &type, - unsigned int maxAge = 0); - QString append(QFile &file, - const QString &type = "application/octet-stream"); - void append(const BoBData &); +public: + BoBManager(Client *); + void setCache(BoBCache *); - private: - BoBCache *_cache; - QHash > _localFiles; //cid => (filename, mime) - }; + BoBData bobData(const QString &); + // file data, mime type, max age in seconds + BoBData append(const QByteArray &data, const QString &type, unsigned int maxAge = 0); + Hash append(QFile &file, + const QString &type = "application/octet-stream"); // this method adds just to runtime cache + void append(const BoBData &); -} +private: + BoBCache *_cache; + QHash> _localFiles; // cid => (filename, mime) +}; +} // namespace XMPP #endif // XMPP_BITSOFBINARY_H diff --git a/src/xmpp/xmpp-im/xmpp_bytestream.cpp b/src/xmpp/xmpp-im/xmpp_bytestream.cpp index 92dac0d8..caaadf7b 100644 --- a/src/xmpp/xmpp-im/xmpp_bytestream.cpp +++ b/src/xmpp/xmpp-im/xmpp_bytestream.cpp @@ -1,6 +1,6 @@ /* * bytestream_manager.cpp - base class for bytestreams over xmpp - * Copyright (C) 2003 Justin Karneges, Rion + * Copyright (C) 2003 Justin Karneges, Sergey Ilinykh * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -17,33 +17,31 @@ * */ -#include - #include "xmpp_bytestream.h" -#include "xmpp_client.h" -namespace XMPP -{ - -BytestreamManager::BytestreamManager(Client *parent) - : QObject(parent) -{ +#include "xmpp_client.h" -} +#include +#if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0) +#include +#endif -BytestreamManager::~BytestreamManager() -{ +namespace XMPP { +BytestreamManager::BytestreamManager(Client *parent) : QObject(parent) { } -} +BytestreamManager::~BytestreamManager() { } QString BytestreamManager::genUniqueSID(const Jid &peer) const { // get unused key QString sid; do { - sid = QString("%1%2").arg(sidPrefix()) - .arg(qrand() & 0xffff, 4, 16, QChar('0')); - } while(!isAcceptableSID(peer, sid)); +#if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0) + sid = QString("%1%2").arg(sidPrefix()).arg(QRandomGenerator::global()->generate() & 0xffff, 4, 16, QChar('0')); +#else + sid = QString("%1%2").arg(sidPrefix()).arg(qrand() & 0xffff, 4, 16, QChar('0')); +#endif + } while (!isAcceptableSID(peer, sid)); return sid; } @@ -54,10 +52,8 @@ void BytestreamManager::deleteConnection(BSConnection *c, int msec) { if (msec) { QTimer::singleShot(msec, c, SLOT(deleteLater())); - } - else { + } else { delete c; } } - -} +} // namespace XMPP diff --git a/src/xmpp/xmpp-im/xmpp_bytestream.h b/src/xmpp/xmpp-im/xmpp_bytestream.h index 7da4d0c3..d5c824a6 100644 --- a/src/xmpp/xmpp-im/xmpp_bytestream.h +++ b/src/xmpp/xmpp-im/xmpp_bytestream.h @@ -1,6 +1,6 @@ /* * bytestream_manager.h - base class for bytestreams over xmpp - * Copyright (C) 2003 Justin Karneges, Rion + * Copyright (C) 2003 Justin Karneges, Sergey Ilinykh * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -20,50 +20,48 @@ #ifndef BYTESTREAM_MANAGER_H #define BYTESTREAM_MANAGER_H -#include +#include "iris/bytestream.h" #include "xmpp/jid/jid.h" -#include "bytestream.h" -namespace XMPP -{ - class Client; - class BytestreamManager; +#include + +namespace XMPP { +class BytestreamManager; +class Client; - class BSConnection : public ByteStream - { - public: - enum Error { ErrRefused = ErrCustom, ErrConnect, ErrProxy, ErrSocket }; - enum State { Idle, Requesting, Connecting, WaitingForAccept, Active }; +class BSConnection : public ByteStream { +public: + enum Error { ErrRefused = ErrCustom, ErrConnect, ErrProxy, ErrSocket }; + enum State { Idle, Requesting, Connecting, WaitingForAccept, Active }; - BSConnection(QObject *parent = 0) : ByteStream(parent) {} + BSConnection(QObject *parent = nullptr) : ByteStream(parent) { } - virtual void connectToJid(const Jid &peer, const QString &sid) = 0; - virtual void accept() = 0; + virtual void connectToJid(const Jid &peer, const QString &sid) = 0; + virtual void accept() = 0; - virtual Jid peer() const = 0; - virtual QString sid() const = 0; - virtual BytestreamManager* manager() const = 0; - }; + virtual Jid peer() const = 0; + virtual QString sid() const = 0; + virtual BytestreamManager *manager() const = 0; +}; - class BytestreamManager : public QObject - { - Q_OBJECT +class BytestreamManager : public QObject { + Q_OBJECT - public: - BytestreamManager(Client *); - virtual ~BytestreamManager(); +public: + BytestreamManager(Client *); + virtual ~BytestreamManager(); - virtual bool isAcceptableSID(const Jid &peer, const QString &sid) const = 0; - QString genUniqueSID(const Jid &peer) const; - virtual BSConnection* createConnection() = 0; - virtual void deleteConnection(BSConnection *c, int msec = 0); + virtual bool isAcceptableSID(const Jid &peer, const QString &sid) const = 0; + QString genUniqueSID(const Jid &peer) const; + virtual BSConnection *createConnection() = 0; + virtual void deleteConnection(BSConnection *c, int msec = 0); - protected: - virtual const char* sidPrefix() const = 0; +protected: + virtual const char *sidPrefix() const = 0; - signals: - void incomingReady(); - }; -} +signals: + void incomingReady(); +}; +} // namespace XMPP -#endif +#endif // BYTESTREAM_MANAGER_H diff --git a/src/xmpp/xmpp-im/xmpp_caps.cpp b/src/xmpp/xmpp-im/xmpp_caps.cpp index 000ce489..5b53725b 100644 --- a/src/xmpp/xmpp-im/xmpp_caps.cpp +++ b/src/xmpp/xmpp-im/xmpp_caps.cpp @@ -1,6 +1,6 @@ /* * capsregistry.cpp - * Copyright (C) 2006-2016 Remko Troncon, Rion + * Copyright (C) 2006-2016 Remko Troncon, Sergey Ilinykh * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -24,21 +24,19 @@ * goes to CapsSpec. */ -#include -#include -#include -#include -#include - -#include "xmpp_features.h" #include "xmpp_caps.h" -#include "xmpp_discoinfotask.h" + #include "xmpp_client.h" +#include "xmpp_discoinfotask.h" +#include "xmpp_features.h" #include "xmpp_xmlcommon.h" -namespace XMPP { - +#include +#include +#include +#include +namespace XMPP { QDomElement CapsInfo::toXml(QDomDocument *doc) const { QDomElement caps = doc->createElement("info"); @@ -50,14 +48,13 @@ QDomElement CapsInfo::toXml(QDomDocument *doc) const CapsInfo CapsInfo::fromXml(const QDomElement &caps) { QDateTime lastSeen = QDateTime::fromString(caps.firstChildElement("atime").nodeValue(), Qt::ISODate); - DiscoItem item = DiscoItem::fromDiscoInfoResult(caps.firstChildElement("query")); + DiscoItem item = DiscoItem::fromDiscoInfoResult(caps.firstChildElement("query")); if (item.features().isEmpty()) { // it's hardly possible if client does not support anything. return CapsInfo(); } return CapsInfo(item, lastSeen); } - // ----------------------------------------------------------------------------- /** @@ -69,10 +66,7 @@ CapsRegistry *CapsRegistry::instance_ = nullptr; /** * \brief Default constructor. */ -CapsRegistry::CapsRegistry(QObject *parent) : - QObject(parent) -{ -} +CapsRegistry::CapsRegistry(QObject *parent) : QObject(parent) { } CapsRegistry *CapsRegistry::instance() { @@ -82,10 +76,7 @@ CapsRegistry *CapsRegistry::instance() return instance_; } -void CapsRegistry::setInstance(CapsRegistry *instance) -{ - instance_ = instance; -} +void CapsRegistry::setInstance(CapsRegistry *instance) { instance_ = instance; } /** * \brief Convert all capabilities info to XML. @@ -94,12 +85,12 @@ void CapsRegistry::save() { // Generate XML QDomDocument doc; - QDomElement capabilities = doc.createElement("capabilities"); + QDomElement capabilities = doc.createElement("capabilities"); doc.appendChild(capabilities); - QHash::ConstIterator i = capsInfo_.constBegin(); - for( ; i != capsInfo_.end(); i++) { + QHash::ConstIterator i = capsInfo_.constBegin(); + for (; i != capsInfo_.constEnd(); i++) { QDomElement info = i.value().toXml(&doc); - info.setAttribute("node",i.key()); + info.setAttribute("node", i.key()); capabilities.appendChild(info); } @@ -112,10 +103,7 @@ void CapsRegistry::saveData(const QByteArray &data) return; } -QByteArray CapsRegistry::loadData() -{ - return QByteArray(); -} +QByteArray CapsRegistry::loadData() { return QByteArray(); } /** * \brief Sets the file to save the capabilities info to @@ -143,28 +131,26 @@ void CapsRegistry::load() // keep unseen info for last 3 month. adjust if required QDateTime validTime = QDateTime::currentDateTime().addMonths(-3); - for(QDomNode n = caps.firstChild(); !n.isNull(); n = n.nextSibling()) { + for (QDomNode n = caps.firstChild(); !n.isNull(); n = n.nextSibling()) { QDomElement i = n.toElement(); - if(i.isNull()) { + if (i.isNull()) { qWarning("capsregistry.cpp: Null element"); continue; } - if(i.tagName() == "info") { + if (i.tagName() == "info") { QString node = i.attribute("node"); - int sep = node.indexOf('#'); + int sep = node.indexOf('#'); if (sep > 0 && sep + 1 < node.length()) { CapsInfo info = CapsInfo::fromXml(i); if (info.isValid() && info.lastSeen() > validTime) { capsInfo_[node] = CapsInfo::fromXml(i); } - //qDebug() << QString("Read %1 %2").arg(node).arg(ver); - } - else { + // qDebug() << QString("Read %1 %2").arg(node).arg(ver); + } else { qWarning() << "capsregistry.cpp: Node" << node << "invalid"; } - } - else { + } else { qWarning("capsregistry.cpp: Unknown element"); } } @@ -173,7 +159,7 @@ void CapsRegistry::load() /** * \brief Registers capabilities of a client. */ -void CapsRegistry::registerCaps(const CapsSpec& spec, const DiscoItem &item) +void CapsRegistry::registerCaps(const CapsSpec &spec, const DiscoItem &item) { QString dnode = spec.flatten(); if (!isRegistered(dnode)) { @@ -186,10 +172,7 @@ void CapsRegistry::registerCaps(const CapsSpec& spec, const DiscoItem &item) /** * \brief Checks if capabilities have been registered. */ -bool CapsRegistry::isRegistered(const QString& spec) const -{ - return capsInfo_.contains(spec); -} +bool CapsRegistry::isRegistered(const QString &spec) const { return capsInfo_.contains(spec); } DiscoItem CapsRegistry::disco(const QString &spec) const { @@ -197,7 +180,6 @@ DiscoItem CapsRegistry::disco(const QString &spec) const return ci.disco(); } - /*-------------------------------------------------------------- _____ __ __ / ____| | \/ | @@ -219,30 +201,19 @@ DiscoItem CapsRegistry::disco(const QString &spec) const /** * \brief Default constructor. */ -CapsManager::CapsManager(Client *client) : - client_(client), - isEnabled_(true) -{} - -CapsManager::~CapsManager() -{} +CapsManager::CapsManager(Client *client) : QObject(client), client_(client), isEnabled_(true) { } +CapsManager::~CapsManager() { } /** * \brief Checks whether the caps manager is enabled (and does lookups). */ -bool CapsManager::isEnabled() -{ - return isEnabled_; -} +bool CapsManager::isEnabled() { return isEnabled_; } /** * \brief Enables or disables the caps manager. */ -void CapsManager::setEnabled(bool b) -{ - isEnabled_ = b; -} +void CapsManager::setEnabled(bool b) { isEnabled_ = b; } /** * \brief Registers new incoming capabilities information of a JID. @@ -254,14 +225,15 @@ void CapsManager::setEnabled(bool b) * @param ver The entity's caps version * @param ext The entity's caps extensions */ -void CapsManager::updateCaps(const Jid& jid, const CapsSpec &c) +void CapsManager::updateCaps(const Jid &jid, const CapsSpec &c) { - if (jid.compare(client_->jid(),false)) + if (jid.compare(client_->jid(), false)) return; QString fullNode = c.flatten(); if (capsSpecs_[jid.full()] != c) { - //qDebug() << QString("caps.cpp: Updating caps for %1 (node=%2,ver=%3,ext=%4)").arg(QString(jid.full()).replace('%',"%%")).arg(node).arg(ver).arg(ext); + // qDebug() << QString("caps.cpp: Updating caps for %1 + // (node=%2,ver=%3,ext=%4)").arg(QString(jid.full()).replace('%',"%%")).arg(node).arg(ver).arg(ext); // Unregister from all old caps node capsJids_[capsSpecs_[jid.full()].flatten()].removeAll(jid.full()); @@ -278,22 +250,22 @@ void CapsManager::updateCaps(const Jid& jid, const CapsSpec &c) // Register new caps and check if we need to discover features if (isEnabled()) { if (!CapsRegistry::instance()->isRegistered(fullNode) && capsJids_[fullNode].count() == 1) { - //qDebug() << QString("caps.cpp: Sending disco request to %1, node=%2").arg(QString(jid.full()).replace('%',"%%")).arg(node + "#" + s.extensions()); - JT_DiscoInfo* disco = new JT_DiscoInfo(client_->rootTask()); + // qDebug() << QString("caps.cpp: Sending disco request to %1, + // node=%2").arg(QString(jid.full()).replace('%',"%%")).arg(node + "#" + s.extensions()); + JT_DiscoInfo *disco = new JT_DiscoInfo(client_->rootTask()); disco->setAllowCache(false); connect(disco, SIGNAL(finished()), SLOT(discoFinished())); disco->get(jid, fullNode); disco->go(true); } } - } - else { + } else { // Remove all caps specifications - qWarning() << QString("caps.cpp: Illegal caps info from %1: node=%2, ver=%3").arg(QString(jid.full()).replace('%',"%%")).arg(fullNode).arg(c.version()); + qWarning() << QString("caps.cpp: Illegal caps info from %1: node=%2, ver=%3") + .arg(QString(jid.full()).replace('%', "%%"), fullNode, c.version()); capsSpecs_.remove(jid.full()); } - } - else { + } else { // Add to the list of jids capsJids_[fullNode].push_back(jid.full()); } @@ -304,9 +276,9 @@ void CapsManager::updateCaps(const Jid& jid, const CapsSpec &c) * * @param jid The entity's JID */ -void CapsManager::disableCaps(const Jid& jid) +void CapsManager::disableCaps(const Jid &jid) { - //qDebug() << QString("caps.cpp: Disabling caps for %1.").arg(QString(jid.full()).replace('%',"%%")); + // qDebug() << QString("caps.cpp: Disabling caps for %1.").arg(QString(jid.full()).replace('%',"%%")); if (capsEnabled(jid)) { QString node = capsSpecs_[jid.full()].flatten(); if (!node.isEmpty()) { @@ -344,11 +316,11 @@ void CapsManager::updateDisco(const Jid &jid, const DiscoItem &item) * \brief This slot is called whenever capabilities of a client were discovered. * All jids with the corresponding client are updated. */ -void CapsManager::capsRegistered(const CapsSpec& cs) +void CapsManager::capsRegistered(const CapsSpec &cs) { // Notify affected jids. - foreach(const QString &s, capsJids_[cs.flatten()]) { - //qDebug() << QString("caps.cpp: Notifying %1.").arg(s.replace('%',"%%")); + for (const QString &s : std::as_const(capsJids_[cs.flatten()])) { + // qDebug() << QString("caps.cpp: Notifying %1.").arg(s.replace('%',"%%")); emit capsChanged(s); } } @@ -356,55 +328,48 @@ void CapsManager::capsRegistered(const CapsSpec& cs) /** * \brief Checks whether a given JID is broadcastingn its entity capabilities. */ -bool CapsManager::capsEnabled(const Jid& jid) const -{ - return capsSpecs_.contains(jid.full()); -} - +bool CapsManager::capsEnabled(const Jid &jid) const { return capsSpecs_.contains(jid.full()); } /** * \brief Requests the list of features of a given JID. */ -XMPP::DiscoItem CapsManager::disco(const Jid& jid) const +XMPP::DiscoItem CapsManager::disco(const Jid &jid) const { - //qDebug() << "caps.cpp: Retrieving features of " << jid.full(); + // qDebug() << "caps.cpp: Retrieving features of " << jid.full(); QStringList f; if (!capsEnabled(jid)) { return DiscoItem(); } QString node = capsSpecs_[jid.full()].flatten(); - //qDebug() << QString(" %1").arg(CapsRegistry::instance()->features(s).list().join("\n")); + // qDebug() << QString(" %1").arg(CapsRegistry::instance()->features(s).list().join("\n")); return CapsRegistry::instance()->disco(node); } /** * \brief Requests the list of features of a given JID. */ -XMPP::Features CapsManager::features(const Jid& jid) const -{ - return disco(jid).features(); -} +XMPP::Features CapsManager::features(const Jid &jid) const { return disco(jid).features(); } /** * \brief Returns the client name of a given jid. * \param jid the jid to retrieve the client name of */ -QString CapsManager::clientName(const Jid& jid) const +QString CapsManager::clientName(const Jid &jid) const { if (capsEnabled(jid)) { CapsSpec cs = capsSpecs_[jid.full()]; - QString name; + QString name; QString cs_str = cs.flatten(); if (CapsRegistry::instance()->isRegistered(cs_str)) { DiscoItem disco = CapsRegistry::instance()->disco(cs_str); - XData si = disco.registeredExtension(QLatin1String("urn:xmpp:dataforms:softwareinfo")); + XData si = disco.registeredExtension(QLatin1String("urn:xmpp:dataforms:softwareinfo")); if (si.isValid()) { name = si.getField("software").value().value(0); } if (name.isEmpty()) { - const DiscoItem::Identities& i = disco.identities(); + const DiscoItem::Identities &i = disco.identities(); if (i.count() > 0) { name = i.first().name; } @@ -413,8 +378,8 @@ QString CapsManager::clientName(const Jid& jid) const // Try to be intelligent about the name if (name.isEmpty()) { - const QString &node = cs.node(); - int startPos = 0, ds = 0; + const QString &node = cs.node(); + int startPos = 0, ds = 0; if (node.startsWith(QStringLiteral("http://"))) startPos = 7; else if (node.startsWith(QStringLiteral("https://"))) @@ -425,12 +390,11 @@ QString CapsManager::clientName(const Jid& jid) const ds = 5; } - name = QStringRef(&node, startPos, node.size() - startPos - ds).toString(); + name = QStringView { node }.mid(startPos, node.size() - startPos - ds).toString(); } return name; - } - else { + } else { return QString(); } } @@ -438,17 +402,17 @@ QString CapsManager::clientName(const Jid& jid) const /** * \brief Returns the client version of a given jid. */ -QString CapsManager::clientVersion(const Jid& jid) const +QString CapsManager::clientVersion(const Jid &jid) const { if (!capsEnabled(jid)) return QString(); - QString version; - const CapsSpec &cs = capsSpecs_[jid.full()]; - QString cs_str = cs.flatten(); + QString version; + const CapsSpec &cs = capsSpecs_[jid.full()]; + QString cs_str = cs.flatten(); if (CapsRegistry::instance()->isRegistered(cs_str)) { XData form = CapsRegistry::instance()->disco(cs_str).registeredExtension("urn:xmpp:dataforms:softwareinfo"); - version = form.getField("software_version").value().value(0); + version = form.getField("software_version").value().value(0); } return version; @@ -464,7 +428,7 @@ QString CapsManager::osVersion(const Jid &jid) const QString cs_str = capsSpecs_[jid.full()].flatten(); if (CapsRegistry::instance()->isRegistered(cs_str)) { XData form = CapsRegistry::instance()->disco(cs_str).registeredExtension("urn:xmpp:dataforms:softwareinfo"); - os_str = form.getField("os").value().value(0).trimmed(); + os_str = form.getField("os").value().value(0).trimmed(); if (!os_str.isEmpty()) { QString os_ver = form.getField("os_version").value().value(0).trimmed(); if (!os_ver.isEmpty()) @@ -475,9 +439,5 @@ QString CapsManager::osVersion(const Jid &jid) const return os_str; } -CapsSpec CapsManager::capsSpec(const Jid &jid) const -{ - return capsSpecs_.value(jid.full()); -} - +CapsSpec CapsManager::capsSpec(const Jid &jid) const { return capsSpecs_.value(jid.full()); } } // namespace XMPP diff --git a/src/xmpp/xmpp-im/xmpp_caps.h b/src/xmpp/xmpp-im/xmpp_caps.h index d11a3e7d..c528af9a 100644 --- a/src/xmpp/xmpp-im/xmpp_caps.h +++ b/src/xmpp/xmpp-im/xmpp_caps.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2016 Remko Troncon, Rion + * Copyright (C) 2016 Remko Troncon, Sergey Ilinykh * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -19,67 +19,61 @@ #ifndef XMPP_CAPS_H #define XMPP_CAPS_H -#include - -#include "xmpp_features.h" #include "xmpp_discoitem.h" +#include "xmpp_features.h" #include "xmpp_status.h" +#include namespace XMPP { - -class CapsInfo -{ +class CapsInfo { public: - inline CapsInfo() {} + inline CapsInfo() { } inline CapsInfo(const XMPP::DiscoItem &disco, const QDateTime &lastSeen = QDateTime()) : - _lastSeen(lastSeen.isNull()? QDateTime::currentDateTime() : lastSeen), - _disco(disco) {} - inline bool isValid() const { return _lastSeen.isValid(); } - inline const QDateTime &lastSeen() const { return _lastSeen; } + _lastSeen(lastSeen.isNull() ? QDateTime::currentDateTime() : lastSeen), _disco(disco) + { + } + inline bool isValid() const { return _lastSeen.isValid(); } + inline const QDateTime &lastSeen() const { return _lastSeen; } inline const XMPP::DiscoItem &disco() const { return _disco; } - QDomElement toXml(QDomDocument *doc) const; - static CapsInfo fromXml(const QDomElement &ci); + QDomElement toXml(QDomDocument *doc) const; + static CapsInfo fromXml(const QDomElement &ci); private: - QDateTime _lastSeen; + QDateTime _lastSeen; XMPP::DiscoItem _disco; }; - -class CapsRegistry : public QObject -{ +class CapsRegistry : public QObject { Q_OBJECT public: CapsRegistry(QObject *parent = nullptr); - static CapsRegistry* instance(); - static void setInstance(CapsRegistry*instance); + static CapsRegistry *instance(); + static void setInstance(CapsRegistry *instance); - void registerCaps(const CapsSpec&, const XMPP::DiscoItem &item); - bool isRegistered(const QString &) const; - DiscoItem disco(const QString&) const; + void registerCaps(const CapsSpec &, const XMPP::DiscoItem &item); + bool isRegistered(const QString &) const; + DiscoItem disco(const QString &) const; signals: - void registered(const XMPP::CapsSpec&); + void registered(const XMPP::CapsSpec &); public slots: void load(); void save(); protected: - virtual void saveData(const QByteArray &data); // reimplmenet these two functions - virtual QByteArray loadData(); // to have permanent cache + virtual void saveData(const QByteArray &data); // reimplmenet these two functions + virtual QByteArray loadData(); // to have permanent cache private: - static CapsRegistry *instance_; - QHash capsInfo_; + static CapsRegistry *instance_; + QHash capsInfo_; }; - -class CapsManager : public QObject -{ +class CapsManager : public QObject { Q_OBJECT public: @@ -89,36 +83,35 @@ class CapsManager : public QObject bool isEnabled(); void setEnabled(bool); - void updateCaps(const Jid& jid, const CapsSpec& caps); - void disableCaps(const Jid& jid); - bool capsEnabled(const Jid& jid) const; + void updateCaps(const Jid &jid, const CapsSpec &caps); + void disableCaps(const Jid &jid); + bool capsEnabled(const Jid &jid) const; XMPP::DiscoItem disco(const Jid &jid) const; - void updateDisco(const Jid &jid, const XMPP::DiscoItem &item); + void updateDisco(const Jid &jid, const XMPP::DiscoItem &item); - XMPP::Features features(const Jid& jid) const; - QString clientName(const Jid& jid) const; - QString clientVersion(const Jid& jid) const; - QString osVersion(const Jid& jid) const; - CapsSpec capsSpec(const Jid &jid) const; + XMPP::Features features(const Jid &jid) const; + QString clientName(const Jid &jid) const; + QString clientVersion(const Jid &jid) const; + QString osVersion(const Jid &jid) const; + CapsSpec capsSpec(const Jid &jid) const; signals: /** * This signal is emitted when the feature list of a given JID have changed. */ - void capsChanged(const Jid& jid); + void capsChanged(const Jid &jid); protected slots: void discoFinished(); - void capsRegistered(const CapsSpec&); + void capsRegistered(const CapsSpec &); private: - Client *client_; - bool isEnabled_; - QMap capsSpecs_; - QMap > capsJids_; + Client *client_; + bool isEnabled_; + QMap capsSpecs_; + QMap> capsJids_; }; - } // namespace XMPP -#endif // CAPS_H +#endif // XMPP_CAPS_H diff --git a/src/xmpp/xmpp-im/xmpp_captcha.h b/src/xmpp/xmpp-im/xmpp_captcha.h index d3d36930..9f94eb47 100644 --- a/src/xmpp/xmpp-im/xmpp_captcha.h +++ b/src/xmpp/xmpp-im/xmpp_captcha.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2016 Rion + * Copyright (C) 2016 Sergey Ilinykh * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -19,55 +19,45 @@ #ifndef XMPP_CAPTCHA_H #define XMPP_CAPTCHA_H -#include - #include "xmpp/jid/jid.h" #include "xmpp_url.h" -namespace XMPP -{ - class Message; - class XData; +#include + +namespace XMPP { +class CaptchaChallengePrivate; +class Message; +class XData; - class CaptchaChallengePrivate; - class CaptchaChallenge - { - public: - enum Result { - Passed, - Unavailable, - NotAcceptable - }; +class CaptchaChallenge { +public: + enum Result { Passed, Unavailable, NotAcceptable }; - enum State { - New, - Success, - Fail - }; + enum State { New, Success, Fail }; - static const int Timeout = 120; // secs + static const int Timeout = 120; // secs - CaptchaChallenge(); - CaptchaChallenge(const Message &); - CaptchaChallenge(const CaptchaChallenge &); - ~CaptchaChallenge(); + CaptchaChallenge(); + CaptchaChallenge(const Message &); + CaptchaChallenge(const CaptchaChallenge &); + ~CaptchaChallenge(); - CaptchaChallenge & operator=(const CaptchaChallenge &); + CaptchaChallenge &operator=(const CaptchaChallenge &); - bool isValid() const; - const Jid &offendedJid() const; - const Jid &arbiter() const; - const XData &form() const; - QString explanation() const; - const UrlList &urls() const; - State state() const; + bool isValid() const; + const Jid &offendedJid() const; + const Jid &arbiter() const; + const XData &form() const; + QString explanation() const; + const UrlList &urls() const; + State state() const; - Result validateResponse(const XData &); + Result validateResponse(const XData &); - private: - friend class CaptchaChallengePrivate; - QSharedDataPointer d; - }; -} +private: + friend class CaptchaChallengePrivate; + QSharedDataPointer d; +}; +} // namespace XMPP -#endif +#endif // XMPP_CAPTCHA_H diff --git a/src/xmpp/xmpp-im/xmpp_chatstate.h b/src/xmpp/xmpp-im/xmpp_chatstate.h index ef4faf94..a7a4e339 100644 --- a/src/xmpp/xmpp-im/xmpp_chatstate.h +++ b/src/xmpp/xmpp-im/xmpp_chatstate.h @@ -20,14 +20,7 @@ #define XMPP_CHATSTATE namespace XMPP { - typedef enum { - StateNone, - StateActive, - StateComposing, - StatePaused, - StateInactive, - StateGone - } ChatState; -} +typedef enum { StateNone, StateActive, StateComposing, StatePaused, StateInactive, StateGone } ChatState; +} // namespace XMPP -#endif +#endif // XMPP_CHATSTATE diff --git a/src/xmpp/xmpp-im/xmpp_client.h b/src/xmpp/xmpp-im/xmpp_client.h index 60c852f3..5a1431b2 100644 --- a/src/xmpp/xmpp-im/xmpp_client.h +++ b/src/xmpp/xmpp-im/xmpp_client.h @@ -19,72 +19,77 @@ #ifndef XMPP_CLIENT_H #define XMPP_CLIENT_H -#include -#include -#include - #include "xmpp/jid/jid.h" -#include "xmpp_status.h" #include "xmpp_discoitem.h" +#include "xmpp_status.h" + +#include +#include +#include -class QString; -class QDomElement; -class QDomDocument; class ByteStream; +class QDomDocument; +class QDomElement; class QNetworkAccessManager; + namespace XMPP { - class ClientStream; - class Features; - class FileTransferManager; - class IBBManager; - class JidLinkManager; - class LiveRoster; - class LiveRosterItem; - class Message; - class Resource; - class ResourceList; - class Roster; - class RosterItem; - class S5BManager; - class BSConnection; - class Stream; - class Task; - class CapsManager; - class CarbonsManager; - class EncryptionHandler; - class ServerInfoManager; - class HttpFileUploadManager; - class JT_PushMessage; - - namespace Jingle { +class BSConnection; +class CapsManager; +class CarbonsManager class ClientStream; +class EncryptionHandler; +class Features; +class FileTransferManager; +class HttpFileUploadManager; +class IBBManager; +class JT_PushMessage; +class JidLinkManager; +class LiveRoster; +class LiveRosterItem; +class Message; +class Resource; +class ResourceList; +class Roster; +class RosterItem; +class S5BManager; +class ServerInfoManager; +class Stream; +class Task; +class TcpPortReserver; +class ExternalServiceDiscovery; +class StunDiscoManager; + +namespace Jingle { + class Manager; + namespace S5B { + class Manager; + } + namespace IBB { + class Manager; + } + namespace ICE { class Manager; - namespace S5B { - class Manager; - } } } -namespace XMPP -{ - class Client : public QObject - { +namespace XMPP { + class Client : public QObject { Q_OBJECT public: - Client(QObject *parent=0); + Client(QObject *parent = nullptr); ~Client(); bool isActive() const; - void connectToServer(ClientStream *s, const Jid &j, bool auth=true); + void connectToServer(ClientStream *s, const Jid &j, bool auth = true); void start(const QString &host, const QString &user, const QString &pass, const QString &resource); - void close(bool fast=false); + void close(bool fast = false); - bool hasStream() const; - Stream & stream(); - QString streamBaseNS() const; - const LiveRoster & roster() const; - const ResourceList & resourceList() const; - bool isSessionRequired() const; + bool hasStream() const; + Stream &stream(); + QString streamBaseNS() const; + const LiveRoster &roster() const; + const ResourceList &resourceList() const; + bool isSessionRequired() const; void send(const QDomElement &); void send(const QString &); @@ -94,74 +99,85 @@ namespace XMPP QString user() const; QString pass() const; QString resource() const; - Jid jid() const; + Jid jid() const; - void setNetworkAccessManager(QNetworkAccessManager *qnam); + void setNetworkAccessManager(QNetworkAccessManager *qnam); QNetworkAccessManager *networkAccessManager() const; - void rosterRequest(); + void rosterRequest(bool withGroupsDelimiter = true); void sendMessage(Message &); - void sendSubscription(const Jid &, const QString &, const QString& nick = QString()); + void sendSubscription(const Jid &, const QString &, const QString &nick = QString()); void setPresence(const Status &); - void debug(const QString &); - QString genUniqueId(); - Task *rootTask(); + void debug(const QString &); + QString genUniqueId(); + Task *rootTask(); QDomDocument *doc() const; - QString OSName() const; - QString OSVersion() const; - QString timeZone() const; - int timeZoneOffset() const; - bool manualTimeZoneOffset() const; - QString clientName() const; - QString clientVersion() const; + QString OSName() const; + QString OSVersion() const; + QString timeZone() const; + int timeZoneOffset() const; + bool manualTimeZoneOffset() const; + QString clientName() const; + QString clientVersion() const; CapsSpec caps() const; CapsSpec serverCaps() const; - void setOSName(const QString &); - void setOSVersion(const QString &); - void setTimeZone(const QString &, int); - void setClientName(const QString &); - void setClientVersion(const QString &); - void setCaps(const CapsSpec &); - void setEncryptionHandler(EncryptionHandler *); + void setOSName(const QString &); + void setOSVersion(const QString &); + void setTimeZone(const QString &, int); + void setClientName(const QString &); + void setClientVersion(const QString &); + void setCaps(const CapsSpec &); + void setEncryptionHandler(EncryptionHandler *); EncryptionHandler *encryptionHandler() const; - void setIdentity(const DiscoItem::Identity &); + void setIdentity(const DiscoItem::Identity &); DiscoItem::Identity identity() const; - void setFeatures(const Features& f); - const Features& features() const; - DiscoItem makeDiscoResult(const QString &node = QString::null) const; - - S5BManager *s5bManager() const; - IBBManager *ibbManager() const; - BoBManager *bobManager() const; - JidLinkManager *jidLinkManager() const; - CapsManager *capsManager() const; - CarbonsManager *carbonsManager() const; - JT_PushMessage *pushMessage() const; - ServerInfoManager *serverInfoManager() const; - HttpFileUploadManager *httpFileUploadManager() const; - Jingle::Manager* jingleManager() const; - Jingle::S5B::Manager *jingleS5BManager() const; - - void setFileTransferEnabled(bool b); + void setFeatures(const Features &f); + const Features &features() const; + DiscoItem makeDiscoResult(const QString &node = QString()) const; + void setCapsOptimizationAllowed(bool allowed); + bool capsOptimizationAllowed() const; + + void setTcpPortReserver(TcpPortReserver *portReserver); + TcpPortReserver *tcpPortReserver() const; + S5BManager *s5bManager() const; + IBBManager *ibbManager() const; + BoBManager *bobManager() const; + JidLinkManager *jidLinkManager() const; + CapsManager *capsManager() const; + CarbonsManager *carbonsManager() const; + JT_PushMessage *pushMessage() const; + ServerInfoManager *serverInfoManager() const; + ExternalServiceDiscovery *externalServiceDiscovery() const; + StunDiscoManager *stunDiscoManager() const; + HttpFileUploadManager *httpFileUploadManager() const; + Jingle::Manager *jingleManager() const; + Jingle::S5B::Manager *jingleS5BManager() const; + Jingle::IBB::Manager *jingleIBBManager() const; + Jingle::ICE::Manager *jingleICEManager() const; + + void setFileTransferEnabled(bool b); FileTransferManager *fileTransferManager() const; - QString groupChatPassword(const QString& host, const QString& room) const; - bool groupChatJoin(const QString &host, const QString &room, const QString &nick, const QString& password = QString(), int maxchars = -1, int maxstanzas = -1, int seconds = -1, const QDateTime &since = QDateTime(), const Status& = Status()); - void groupChatSetStatus(const QString &host, const QString &room, const Status &); - void groupChatChangeNick(const QString &host, const QString &room, const QString &nick, const Status &); - void groupChatLeave(const QString &host, const QString &room, const QString &statusStr = QString()); - void groupChatLeaveAll(const QString &statusStr = QString()); + QString groupChatPassword(const QString &host, const QString &room) const; + bool groupChatJoin(const QString &host, const QString &room, const QString &nick, + const QString &password = QString(), int maxchars = -1, int maxstanzas = -1, + int seconds = -1, const QDateTime &since = QDateTime(), const Status & = Status()); + void groupChatSetStatus(const QString &host, const QString &room, const Status &); + void groupChatChangeNick(const QString &host, const QString &room, const QString &nick, const Status &); + void groupChatLeave(const QString &host, const QString &room, const QString &statusStr = QString()); + void groupChatLeaveAll(const QString &statusStr = QString()); QString groupChatNick(const QString &host, const QString &room) const; signals: void activated(); void disconnected(); - //void authFinished(bool, int, const QString &); + // void authFinished(bool, int, const QString &); + void rosterGroupsDelimiterRequestFinished(const QString &); void rosterRequestFinished(bool, int, const QString &); void rosterItemAdded(const RosterItem &); void rosterItemUpdated(const RosterItem &); @@ -186,11 +202,11 @@ namespace XMPP void endImportRoster(); private slots: - //void streamConnected(); - //void streamHandshaken(); - //void streamError(const StreamError &); - //void streamSSLCertificateReady(const QSSLCert &); - //void streamCloseFinished(); + // void streamConnected(); + // void streamHandshaken(); + // void streamError(const StreamError &); + // void streamSSLCertificateReady(const QSSLCert &); + // void streamCloseFinished(); void streamError(int); void streamReadyRead(); void streamIncomingXml(const QString &); @@ -199,7 +215,7 @@ namespace XMPP void slotRosterRequestFinished(); // basic daemons - void ppSubscription(const Jid &, const QString &, const QString&); + void ppSubscription(const Jid &, const QString &, const QString &); void ppPresence(const Jid &, const Status &); void pmMessage(const Message &); void prRoster(const Roster &); @@ -207,7 +223,7 @@ namespace XMPP void s5b_incomingReady(); void ibb_incomingReady(); - void handleSMAckResponse(int); + void handleSMAckResponse(int); void parseUnhandledStreamFeatures(); public: @@ -227,6 +243,6 @@ namespace XMPP class ClientPrivate; ClientPrivate *d; }; -} +} // namespace XMPP -#endif +#endif // XMPP_CLIENT_H diff --git a/src/xmpp/xmpp-im/xmpp_discoinfotask.cpp b/src/xmpp/xmpp-im/xmpp_discoinfotask.cpp index 1bf0252c..31f8b103 100644 --- a/src/xmpp/xmpp-im/xmpp_discoinfotask.cpp +++ b/src/xmpp/xmpp-im/xmpp_discoinfotask.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2001, 2002 Justin Karneges + * Copyright (C) 2001-2002 Justin Karneges * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -16,90 +16,70 @@ * */ -#include -#include -#include +#include "xmpp_discoinfotask.h" -#include "xmpp_task.h" #include "xmpp/jid/jid.h" +#include "xmpp_caps.h" +#include "xmpp_client.h" #include "xmpp_discoitem.h" -#include "xmpp_discoinfotask.h" +#include "xmpp_task.h" #include "xmpp_xmlcommon.h" -#include "xmpp_client.h" -#include "xmpp_caps.h" + +#include +#include +#include using namespace XMPP; -class DiscoInfoTask::Private -{ +class DiscoInfoTask::Private { public: - bool allowCache = true; - Jid jid; - QString node; + bool allowCache = true; + Jid jid; + QString node; DiscoItem::Identity ident; - DiscoItem item; + DiscoItem item; }; -DiscoInfoTask::DiscoInfoTask(Task *parent) -: Task(parent) -{ - d = new Private; -} +DiscoInfoTask::DiscoInfoTask(Task *parent) : Task(parent) { d = new Private; } -DiscoInfoTask::~DiscoInfoTask() -{ - delete d; -} +DiscoInfoTask::~DiscoInfoTask() { delete d; } -void DiscoInfoTask::setAllowCache(bool allow) -{ - d->allowCache = allow; -} +void DiscoInfoTask::setAllowCache(bool allow) { d->allowCache = allow; } void DiscoInfoTask::get(const DiscoItem &item) { DiscoItem::Identity id; - if ( item.identities().count() == 1 ) + if (item.identities().count() == 1) id = item.identities().first(); get(item.jid(), item.node(), id); } -void DiscoInfoTask::get (const Jid &j, const QString &node, DiscoItem::Identity ident) +void DiscoInfoTask::get(const Jid &j, const QString &node, DiscoItem::Identity ident) { d->item = DiscoItem(); // clear item - d->jid = j; - d->node = node; + d->jid = j; + d->node = node; d->ident = ident; } - /** * Original requested jid. * Is here because sometimes the responder does not include this information * in the reply. */ -const Jid& DiscoInfoTask::jid() const -{ - return d->jid; -} +const Jid &DiscoInfoTask::jid() const { return d->jid; } /** * Original requested node. * Is here because sometimes the responder does not include this information * in the reply. */ -const QString& DiscoInfoTask::node() const -{ - return d->node; -} +const QString &DiscoInfoTask::node() const { return d->node; } -const DiscoItem &DiscoInfoTask::item() const -{ - return d->item; -} +const DiscoItem &DiscoInfoTask::item() const { return d->item; } -void DiscoInfoTask::onGo () +void DiscoInfoTask::onGo() { if (d->allowCache && client()->capsManager()->isEnabled()) { d->item = client()->capsManager()->disco(d->jid); @@ -109,10 +89,9 @@ void DiscoInfoTask::onGo () } } - QDomElement iq = createIQ(doc(), "get", d->jid.full(), id()); - QDomElement query = doc()->createElement("query"); - query.setAttribute("xmlns", "http://jabber.org/protocol/disco#info"); - if ( !d->node.isEmpty() ) + QDomElement iq = createIQ(doc(), "get", d->jid.full(), id()); + QDomElement query = doc()->createElementNS("http://jabber.org/protocol/disco#info", "query"); + if (!d->node.isEmpty()) query.setAttribute("node", d->node); #if 0 // seems like disco#info get request was misinterpreted. xep-0030 says it has to be an EMPTY query. @@ -134,29 +113,26 @@ void DiscoInfoTask::onGo () void DiscoInfoTask::cachedReady() { - d->item.setJid( d->jid ); + d->item.setJid(d->jid); setSuccess(); } bool DiscoInfoTask::take(const QDomElement &x) { - if(!iqVerify(x, d->jid, id())) + if (!iqVerify(x, d->jid, id())) return false; - if(x.attribute("type") == "result") { + if (x.attribute("type") == "result") { d->item = DiscoItem::fromDiscoInfoResult(queryTag(x)); - d->item.setJid( d->jid ); + d->item.setJid(d->jid); if (d->allowCache && client()->capsManager()->isEnabled()) { client()->capsManager()->updateDisco(d->jid, d->item); } setSuccess(); - } - else { + } else { setError(x); } return true; } - - diff --git a/src/xmpp/xmpp-im/xmpp_discoinfotask.h b/src/xmpp/xmpp-im/xmpp_discoinfotask.h index 47c561bc..81023223 100644 --- a/src/xmpp/xmpp-im/xmpp_discoinfotask.h +++ b/src/xmpp/xmpp-im/xmpp_discoinfotask.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2001, 2002 Justin Karneges + * Copyright (C) 2001-2002 Justin Karneges * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -19,46 +19,43 @@ #ifndef XMPP_DISCOINFOTASK_H #define XMPP_DISCOINFOTASK_H -#include "xmpp_task.h" #include "xmpp_discoitem.h" +#include "xmpp_task.h" -namespace XMPP { - class Jid; -} -class QString; class QDomElement; +class QString; + +namespace XMPP { +class Jid; -namespace XMPP -{ - class DiscoInfoTask : public Task - { - Q_OBJECT - public: - DiscoInfoTask(Task *); - ~DiscoInfoTask(); +class DiscoInfoTask : public Task { + Q_OBJECT +public: + DiscoInfoTask(Task *); + ~DiscoInfoTask(); - // Allow retreive result from cache and update cache on finish with new data - void setAllowCache(bool allow = true); + // Allow retreive result from cache and update cache on finish with new data + void setAllowCache(bool allow = true); - void get(const Jid &, const QString &node = QString::null, const DiscoItem::Identity = DiscoItem::Identity()); - void get(const DiscoItem &); + void get(const Jid &, const QString &node = QString(), const DiscoItem::Identity = DiscoItem::Identity()); + void get(const DiscoItem &); - const DiscoItem &item() const; - const Jid& jid() const; - const QString& node() const; + const DiscoItem &item() const; + const Jid &jid() const; + const QString &node() const; - void onGo(); - bool take(const QDomElement &); + void onGo(); + bool take(const QDomElement &); - private slots: - void cachedReady(); +private slots: + void cachedReady(); - private: - class Private; - Private *d; - }; +private: + class Private; + Private *d; +}; +// Deprecated name +typedef DiscoInfoTask JT_DiscoInfo; +} // namespace XMPP - // Deprecated name - typedef DiscoInfoTask JT_DiscoInfo; -} -#endif +#endif // XMPP_DISCOINFOTASK_H diff --git a/src/xmpp/xmpp-im/xmpp_discoitem.cpp b/src/xmpp/xmpp-im/xmpp_discoitem.cpp index 6a1f3243..f959365e 100644 --- a/src/xmpp/xmpp-im/xmpp_discoitem.cpp +++ b/src/xmpp/xmpp-im/xmpp_discoitem.cpp @@ -17,112 +17,98 @@ * */ -#include - #include "xmpp_discoitem.h" +#include + using namespace XMPP; -class XMPP::DiscoItemPrivate : public QSharedData -{ +class XMPP::DiscoItemPrivate : public QSharedData { public: - DiscoItemPrivate() - { - action = DiscoItem::None; - } + DiscoItemPrivate() { action = DiscoItem::None; } - Jid jid; - QString name; - QString node; + Jid jid; + QString name; + QString node; DiscoItem::Action action; - Features features; + Features features; DiscoItem::Identities identities; - QList exts; + QList exts; }; -DiscoItem::DiscoItem() - : d(new DiscoItemPrivate) -{ -} +DiscoItem::DiscoItem() : d(new DiscoItemPrivate) { } -DiscoItem::DiscoItem(const DiscoItem &from) - : d(new DiscoItemPrivate) -{ - *this = from; -} +DiscoItem::DiscoItem(const DiscoItem &from) : d(new DiscoItemPrivate) { *this = from; } -DiscoItem & DiscoItem::operator= (const DiscoItem &from) +DiscoItem &DiscoItem::operator=(const DiscoItem &from) { - d->jid = from.d->jid; - d->name = from.d->name; - d->node = from.d->node; - d->action = from.d->action; - d->features = from.d->features; + d->jid = from.d->jid; + d->name = from.d->name; + d->node = from.d->node; + d->action = from.d->action; + d->features = from.d->features; d->identities = from.d->identities; - d->exts = from.d->exts; + d->exts = from.d->exts; return *this; } -DiscoItem::~DiscoItem() -{ - -} +DiscoItem::~DiscoItem() { } AgentItem DiscoItem::toAgentItem() const { AgentItem ai; - ai.setJid( jid() ); - ai.setName( name() ); + ai.setJid(jid()); + ai.setName(name()); Identity id; - if ( !identities().isEmpty() ) + if (!identities().isEmpty()) id = identities().first(); - ai.setCategory( id.category ); - ai.setType( id.type ); + ai.setCategory(id.category); + ai.setType(id.type); - ai.setFeatures( d->features ); + ai.setFeatures(d->features); return ai; } void DiscoItem::fromAgentItem(const AgentItem &ai) { - setJid( ai.jid() ); - setName( ai.name() ); + setJid(ai.jid()); + setName(ai.name()); Identity id; id.category = ai.category(); - id.type = ai.type(); - id.name = ai.name(); + id.type = ai.type(); + id.name = ai.name(); Identities idList; idList << id; - setIdentities( idList ); + setIdentities(idList); - setFeatures( ai.features() ); + setFeatures(ai.features()); } QString DiscoItem::capsHash(QCryptographicHash::Algorithm algo) const { - QStringList prep; + QStringList prep; DiscoItem::Identities idents = d->identities; - qSort(idents); + std::sort(idents.begin(), idents.end()); - foreach (const DiscoItem::Identity &id, idents) { + for (const DiscoItem::Identity &id : idents) { prep << QString("%1/%2/%3/%4").arg(id.category, id.type, id.lang, id.name); } QStringList fl = d->features.list(); - qSort(fl); + std::sort(fl.begin(), fl.end()); prep += fl; - QMap forms; - foreach (const XData &xd, d->exts) { + QMap forms; + for (const XData &xd : d->exts) { if (xd.registrarType().isEmpty()) { continue; } @@ -131,10 +117,11 @@ QString DiscoItem::capsHash(QCryptographicHash::Algorithm algo) const } forms.insert(xd.registrarType(), xd); } - foreach (const XData &xd, forms.values()) { + const auto &xds = forms.values(); + for (const XData &xd : xds) { prep << xd.registrarType(); - QMap values; - foreach (const XData::Field &f, xd.fields()) { + QMap values; + for (const XData::Field &f : xd.fields()) { if (f.var() == QLatin1String("FORM_TYPE")) { continue; } @@ -145,10 +132,10 @@ QString DiscoItem::capsHash(QCryptographicHash::Algorithm algo) const if (v.isEmpty()) { continue; // maybe it's media-element but xep-115 (1.5) and xep-232 (0.3) are not clear about that. } - qSort(v); + std::sort(v.begin(), v.end()); values[f.var()] = v; } - QMap ::ConstIterator it = values.constBegin(); + QMap::ConstIterator it = values.constBegin(); for (; it != values.constEnd(); ++it) { prep += it.key(); prep += it.value(); @@ -156,7 +143,7 @@ QString DiscoItem::capsHash(QCryptographicHash::Algorithm algo) const } QByteArray ba = QString(prep.join(QLatin1String("<")) + QLatin1Char('<')).toUtf8(); - //qDebug() << "Server caps ver: " << (prep.join(QLatin1String("<")) + QLatin1Char('<')) + // qDebug() << "Server caps ver: " << (prep.join(QLatin1String("<")) + QLatin1Char('<')) // << "Hash:" << QString::fromLatin1(QCryptographicHash::hash(ba, algo).toBase64()); return QString::fromLatin1(QCryptographicHash::hash(ba, algo).toBase64()); } @@ -165,21 +152,20 @@ DiscoItem DiscoItem::fromDiscoInfoResult(const QDomElement &q) { DiscoItem item; - item.setNode( q.attribute("node") ); + item.setNode(q.attribute("node")); - QStringList features; + QStringList features; DiscoItem::Identities identities; - QList extList; + QList extList; - for(QDomNode n = q.firstChild(); !n.isNull(); n = n.nextSibling()) { + for (QDomNode n = q.firstChild(); !n.isNull(); n = n.nextSibling()) { QDomElement e = n.toElement(); - if( e.isNull() ) + if (e.isNull()) continue; - if ( e.tagName() == "feature" ) { + if (e.tagName() == "feature") { features << e.attribute("var"); - } - else if ( e.tagName() == "identity" ) { + } else if (e.tagName() == "identity") { DiscoItem::Identity id; id.category = e.attribute("category"); @@ -187,28 +173,28 @@ DiscoItem DiscoItem::fromDiscoInfoResult(const QDomElement &q) id.lang = e.attribute("lang"); id.name = e.attribute("name"); - identities.append( id ); - } - else if (e.tagName() == QLatin1String("x") && e.attribute("xmlns") == QLatin1String("jabber:x:data")) { + identities.append(id); + } else if (e.tagName() == QLatin1String("x") && e.namespaceURI() == QLatin1String("jabber:x:data")) { XData form; form.fromXml(e); extList.append(form); } } - item.setFeatures( features ); - item.setIdentities( identities ); - item.setExtensions( extList ); + item.setFeatures(features); + item.setIdentities(identities); + item.setExtensions(extList); return item; } QDomElement DiscoItem::toDiscoInfoResult(QDomDocument *doc) const { - QDomElement q = doc->createElementNS(QLatin1String("http://jabber.org/protocol/disco#info"), QLatin1String("query")); + QDomElement q + = doc->createElementNS(QLatin1String("http://jabber.org/protocol/disco#info"), QLatin1String("query")); q.setAttribute("node", d->node); - foreach (const Identity &id, d->identities) { + for (const Identity &id : d->identities) { QDomElement idel = q.appendChild(doc->createElement(QLatin1String("identity"))).toElement(); idel.setAttribute("category", id.category); idel.setAttribute("type", id.type); @@ -220,94 +206,66 @@ QDomElement DiscoItem::toDiscoInfoResult(QDomDocument *doc) const } } - foreach (const QString &f, d->features.list()) { + const QStringList &features = d->features.list(); + for (const QString &f : features) { QDomElement fel = q.appendChild(doc->createElement(QLatin1String("feature"))).toElement(); fel.setAttribute("var", f); } - foreach (const XData &f, d->exts) { + for (const XData &f : d->exts) { q.appendChild(f.toXml(doc)); } return q; } -const Jid &DiscoItem::jid() const -{ - return d->jid; -} +const Jid &DiscoItem::jid() const { return d->jid; } -void DiscoItem::setJid(const Jid &j) -{ - d->jid = j; -} +void DiscoItem::setJid(const Jid &j) { d->jid = j; } -const QString &DiscoItem::name() const -{ - return d->name; -} +const QString &DiscoItem::name() const { return d->name; } -void DiscoItem::setName(const QString &n) -{ - d->name = n; -} +void DiscoItem::setName(const QString &n) { d->name = n; } -const QString &DiscoItem::node() const -{ - return d->node; -} +const QString &DiscoItem::node() const { return d->node; } -void DiscoItem::setNode(const QString &n) -{ - d->node = n; -} +void DiscoItem::setNode(const QString &n) { d->node = n; } -DiscoItem::Action DiscoItem::action() const -{ - return d->action; -} +DiscoItem::Action DiscoItem::action() const { return d->action; } -void DiscoItem::setAction(Action a) -{ - d->action = a; -} +void DiscoItem::setAction(Action a) { d->action = a; } -const Features &DiscoItem::features() const -{ - return d->features; -} +const Features &DiscoItem::features() const { return d->features; } -void DiscoItem::setFeatures(const Features &f) -{ - d->features = f; -} +void DiscoItem::setFeatures(const Features &f) { d->features = f; } -const DiscoItem::Identities &DiscoItem::identities() const -{ - return d->identities; -} +const DiscoItem::Identities &DiscoItem::identities() const { return d->identities; } void DiscoItem::setIdentities(const Identities &i) { d->identities = i; - if ( name().isEmpty() && i.count() ) - setName( i.first().name ); + if (name().isEmpty() && i.count()) + setName(i.first().name); } -const QList &DiscoItem::extensions() const -{ - return d->exts; -} +const QList &DiscoItem::extensions() const { return d->exts; } -void DiscoItem::setExtensions(const QList &extlist) +XData DiscoItem::findExtension(XData::Type xdataType, const QString &formType) const { - d->exts = extlist; + for (const auto &x : d->exts) { + if (x.type() == xdataType && (formType.isEmpty() || x.registrarType() == formType)) { + return x; + } + } + return XData(XData::Data_Invalid); } +void DiscoItem::setExtensions(const QList &extlist) { d->exts = extlist; } + XData DiscoItem::registeredExtension(const QString &ns) const { - foreach (const XData &xd, d->exts) { + for (const XData &xd : d->exts) { if (xd.registrarType() == ns) { return xd; } @@ -319,9 +277,9 @@ DiscoItem::Action DiscoItem::string2action(const QString &s) { Action a; - if ( s == "update" ) + if (s == "update") a = Update; - else if ( s == "remove" ) + else if (s == "remove") a = Remove; else a = None; @@ -333,18 +291,16 @@ QString DiscoItem::action2string(const Action a) { QString s; - if ( a == Update ) + if (a == Update) s = "update"; - else if ( a == Remove ) + else if (a == Remove) s = "remove"; else - s = QString::null; + s = QString(); return s; } - - bool XMPP::operator<(const DiscoItem::Identity &a, const DiscoItem::Identity &b) { int r = a.category.compare(b.category); @@ -363,6 +319,5 @@ bool XMPP::operator<(const DiscoItem::Identity &a, const DiscoItem::Identity &b) bool DiscoItem::Identity::operator==(const DiscoItem::Identity &other) const { - return category == other.category && type == other.type && - lang == other.lang && name == other.name; + return category == other.category && type == other.type && lang == other.lang && name == other.name; } diff --git a/src/xmpp/xmpp-im/xmpp_discoitem.h b/src/xmpp/xmpp-im/xmpp_discoitem.h index a6b06145..f1022617 100644 --- a/src/xmpp/xmpp-im/xmpp_discoitem.h +++ b/src/xmpp/xmpp-im/xmpp_discoitem.h @@ -20,88 +20,84 @@ #ifndef XMPP_DISCOITEM #define XMPP_DISCOITEM -#include -#include - #include "xmpp/jid/jid.h" +#include "xmpp_agentitem.h" #include "xmpp_features.h" #include "xmpp_xdata.h" -#include "xmpp_agentitem.h" + +#include +#include namespace XMPP { - class DiscoItemPrivate; +class DiscoItemPrivate; - class DiscoItem - { - public: - DiscoItem(); - ~DiscoItem(); +class DiscoItem { +public: + DiscoItem(); + ~DiscoItem(); - const Jid &jid() const; - const QString &node() const; - const QString &name() const; + const Jid &jid() const; + const QString &node() const; + const QString &name() const; - void setJid(const Jid &); - void setName(const QString &); - void setNode(const QString &); + void setJid(const Jid &); + void setName(const QString &); + void setNode(const QString &); - enum Action { - None = 0, - Remove, - Update - }; + enum Action { None = 0, Remove, Update }; - Action action() const; - void setAction(Action); + Action action() const; + void setAction(Action); - const Features &features() const; - void setFeatures(const Features &); + const Features &features() const; + void setFeatures(const Features &); - struct Identity - { - QString category; - QString type; - QString lang; - QString name; + struct Identity { + QString category; + QString type; + QString lang; + QString name; - inline Identity() {} - inline Identity(const QString &categoty, const QString &type, - const QString &lang = QString::null, const QString &name = QString::null) : - category(categoty), type(type), lang(lang), name(name) {} - bool operator==(const Identity &other) const; - }; + inline Identity() { } + inline Identity(const QString &categoty, const QString &type, const QString &lang = QString(), + const QString &name = QString()) : category(categoty), type(type), lang(lang), name(name) + { + } + bool operator==(const Identity &other) const; + }; - typedef QList Identities; + typedef QList Identities; - const Identities &identities() const; - void setIdentities(const Identities &); - inline void setIdentities(const Identity &id) { setIdentities(Identities() << id); } + const Identities &identities() const; + void setIdentities(const Identities &); + inline void setIdentities(const Identity &id) { setIdentities(Identities() << id); } - const QList &extensions() const; - void setExtensions(const QList &extlist); - XData registeredExtension(const QString &ns) const; + const QList &extensions() const; + XData findExtension(XData::Type xdataType, const QString &formType = QString()) const; + void setExtensions(const QList &extlist); + XData registeredExtension(const QString &ns) const; - // some useful helper functions - static Action string2action(const QString &s); - static QString action2string(const Action a); + // some useful helper functions + static Action string2action(const QString &s); + static QString action2string(const Action a); - DiscoItem & operator= (const DiscoItem &); - DiscoItem(const DiscoItem &); + DiscoItem &operator=(const DiscoItem &); + DiscoItem(const DiscoItem &); - operator AgentItem() const { return toAgentItem(); } - AgentItem toAgentItem() const; - void fromAgentItem(const AgentItem &); + operator AgentItem() const { return toAgentItem(); } + AgentItem toAgentItem() const; + void fromAgentItem(const AgentItem &); - QString capsHash(QCryptographicHash::Algorithm algo) const; + QString capsHash(QCryptographicHash::Algorithm algo) const; - static DiscoItem fromDiscoInfoResult(const QDomElement &x); - QDomElement toDiscoInfoResult(QDomDocument *doc) const; + static DiscoItem fromDiscoInfoResult(const QDomElement &x); + QDomElement toDiscoInfoResult(QDomDocument *doc) const; - private: - QSharedDataPointer d; - }; +private: + QSharedDataPointer d; +}; - bool operator<(const DiscoItem::Identity &a, const DiscoItem::Identity &b); -} +bool operator<(const DiscoItem::Identity &a, const DiscoItem::Identity &b); +} // namespace XMPP -#endif +#endif // XMPP_DISCOITEM diff --git a/src/xmpp/xmpp-im/xmpp_encryption.cpp b/src/xmpp/xmpp-im/xmpp_encryption.cpp new file mode 100644 index 00000000..25c21e2d --- /dev/null +++ b/src/xmpp/xmpp-im/xmpp_encryption.cpp @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2021 Sergey Ilinykh + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library. If not, see . + * + */ + +#include "xmpp_encryption.h" + +namespace XMPP { + +class EncryptionManager::Private { +public: + QList methods; +}; + +EncryptionManager::EncryptionManager() : d(new Private) { } + +EncryptionManager::~EncryptionManager() { } + +void EncryptionManager::registerMethod(EncryptionMethod *algo) { d->methods.append(algo); } + +void EncryptionManager::unregisterMethod(EncryptionMethod *algo) { d->methods.removeOne(algo); } + +EncryptionManager::MethodsMap EncryptionManager::methods(EncryptionMethod::Capabilities caps) const +{ + MethodsMap ret; + for (auto const &m : std::as_const(d->methods)) { + if (caps & m->capabilities()) + ret[m->id()] = m->name(); + } + return ret; +} + +} diff --git a/src/xmpp/xmpp-im/xmpp_encryption.h b/src/xmpp/xmpp-im/xmpp_encryption.h new file mode 100644 index 00000000..489d4fe3 --- /dev/null +++ b/src/xmpp/xmpp-im/xmpp_encryption.h @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2021 Sergey Ilinykh + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library. If not, see . + * + */ + +#include "xmpp_features.h" + +#include + +#include +#include + +class QDomElement; + +namespace XMPP { + +class EncryptedSession : public QObject { + Q_OBJECT +public: + void write(const QDomElement &xml); + void writeIncoming(const QDomElement &xml); + void write(const QByteArray &xml); + void writeIncoming(const QByteArray &xml); + + QByteArray read(); + QByteArray readOutgoing(); +}; + +class EncryptionMethod : public QObject { + Q_OBJECT +public: + enum Capability { + XmppStanza = 0x1, /* XML */ + DataMessage = 0x2, /* all at once */ + DataStream = 0x4 /* incremental */ + }; + Q_DECLARE_FLAGS(Capabilities, Capability) + + virtual QString id() const = 0; + virtual QString name() const = 0; + virtual Capabilities capabilities() const = 0; + virtual EncryptedSession *startSession(const Capabilities &caps) = 0; + virtual Features features() = 0; +}; + +class EncryptionManager : public QObject { + Q_OBJECT +public: + using MethodId = QString; + using MethodName = QString; + + EncryptionManager(); + ~EncryptionManager(); + using MethodsMap = std::map; + + void registerMethod(EncryptionMethod *algo); + void unregisterMethod(EncryptionMethod *algo); + MethodsMap methods(EncryptionMethod::Capabilities caps = EncryptionMethod::Capabilities(0xff)) const; + +private: + class Private; + std::unique_ptr d; +}; + +} diff --git a/src/xmpp/xmpp-im/xmpp_encryptionhandler.h b/src/xmpp/xmpp-im/xmpp_encryptionhandler.h index 9e106287..21e86c8f 100644 --- a/src/xmpp/xmpp-im/xmpp_encryptionhandler.h +++ b/src/xmpp/xmpp-im/xmpp_encryptionhandler.h @@ -22,14 +22,12 @@ class QDomElement; -namespace XMPP -{ - class EncryptionHandler - { - public: - virtual bool decryptMessageElement(QDomElement &) = 0; - virtual bool encryptMessageElement(QDomElement &) = 0; - }; -} +namespace XMPP { +class EncryptionHandler { +public: + virtual bool decryptMessageElement(QDomElement &) = 0; + virtual bool encryptMessageElement(QDomElement &) = 0; +}; +} // namespace XMPP -#endif //PSI_XMPP_ENCRYPTIONHANDLER_H +#endif // PSI_XMPP_ENCRYPTIONHANDLER_H diff --git a/src/xmpp/xmpp-im/xmpp_externalservicediscovery.cpp b/src/xmpp/xmpp-im/xmpp_externalservicediscovery.cpp new file mode 100644 index 00000000..c598b9e6 --- /dev/null +++ b/src/xmpp/xmpp-im/xmpp_externalservicediscovery.cpp @@ -0,0 +1,230 @@ +/* + * xmpp_externalservicedisco.cpp - Implementation of XEP-0215 + * Copyright (C) 2021 Sergey Ilinykh + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library. If not, see . + * + */ + +#include "xmpp_externalservicediscovery.h" + +#include "xmpp_client.h" +#include "xmpp_jid.h" +#include "xmpp_serverinfomanager.h" +#include "xmpp_xmlcommon.h" + +namespace XMPP { + +JT_ExternalServiceDiscovery::JT_ExternalServiceDiscovery(Task *parent) : Task(parent) { } + +void JT_ExternalServiceDiscovery::getServices(const QString &type) +{ + type_ = type; + creds_.clear(); // to indicate it's services request, not creds +} + +void JT_ExternalServiceDiscovery::getCredentials(const QSet &ids) { creds_ = ids; } + +void JT_ExternalServiceDiscovery::onGo() +{ + QDomElement iq = createIQ(doc(), "get", client()->jid().domain(), id()); + QDomElement query = doc()->createElementNS(QLatin1String("urn:xmpp:extdisco:2"), + QLatin1String(creds_.isEmpty() ? "services" : "credentials")); + if (creds_.isEmpty()) { + if (!type_.isEmpty()) { + query.setAttribute(QLatin1String("type"), type_); + } + } else { + for (auto const &c : std::as_const(creds_)) { + QDomElement service = doc()->createElement(QLatin1String("service")); + service.setAttribute(QLatin1String("host"), c.host); + service.setAttribute(QLatin1String("type"), c.type); + if (c.port) + service.setAttribute(QLatin1String("port"), c.port); + query.appendChild(service); + } + } + iq.appendChild(query); + send(iq); +} + +bool JT_ExternalServiceDiscovery::take(const QDomElement &x) +{ + if (!iqVerify(x, Jid(QString(), client()->jid().domain()), id())) + return false; + + if (x.attribute("type") == "result") { + auto query = x.firstChildElement(QLatin1String(creds_.isEmpty() ? "services" : "credentials")); + if (query.namespaceURI() != QLatin1String("urn:xmpp:extdisco:2")) { + setError(0, QLatin1String("invalid namespace")); + return true; + } + QString serviceTag(QLatin1String("service")); + for (auto el = query.firstChildElement(serviceTag); !el.isNull(); el = el.nextSiblingElement(serviceTag)) { + // services_.append(ExternalService {}); + auto s = std::make_shared(); + if (s->parse(el)) { + services_.append(s); + } + } + setSuccess(); + } else { + setError(x); + } + + return true; +} + +bool ExternalService::parse(QDomElement &el) +{ + QString actionOpt = el.attribute(QLatin1String("action")); + QString expiresOpt = el.attribute(QLatin1String("expires")); + name = el.attribute(QLatin1String("name")); + password = el.attribute(QLatin1String("password")); + QString restrictedOpt = el.attribute(QLatin1String("restricted")); + transport = el.attribute(QLatin1String("transport")); + username = el.attribute(QLatin1String("username")); + host = el.attribute(QLatin1String("host")); + QString portReq = el.attribute(QLatin1String("port")); + type = el.attribute(QLatin1String("type")); + + bool ok; + if (host.isEmpty() || portReq.isEmpty() || type.isEmpty()) + return false; + + port = portReq.toUShort(&ok); + if (!ok) + return false; + + if (!expiresOpt.isEmpty()) { + auto date = QDateTime::fromString(expiresOpt.left(19), Qt::ISODate); + auto curUtc = QDateTime::currentDateTimeUtc(); + if (!date.isValid() || date < curUtc) + return false; + expires = QDeadlineTimer(curUtc.msecsTo(date)); + if (expires.hasExpired()) + qInfo("Server returned already expired service %s expired at %s UTC", qPrintable(*this), + qPrintable(expiresOpt)); + } else { + expires = QDeadlineTimer(QDeadlineTimer::Forever); + } + + if (actionOpt.isEmpty() || actionOpt == QLatin1String("add")) + action = Action::Add; + else if (actionOpt == QLatin1String("modify")) + action = Action::Modify; + else if (actionOpt == QLatin1String("delete")) + action = Action::Delete; + else + return false; + + if (!restrictedOpt.isEmpty()) { + if (restrictedOpt == QLatin1String("true") || restrictedOpt == QLatin1String("1")) + restricted = true; + else if (restrictedOpt != QLatin1String("false") && restrictedOpt != QLatin1String("0")) + return false; + } + + return true; +} + +ExternalService::operator QString() const +{ + return QString(QLatin1String("ExternalService")) + .arg(name, host, QString::number(port), type, transport); +} + +ExternalServiceDiscovery::ExternalServiceDiscovery(Client *client) : client_(client) { } + +bool ExternalServiceDiscovery::isSupported() const +{ + return client_->serverInfoManager()->features().test("urn:xmpp:extdisco:2"); +} + +void ExternalServiceDiscovery::services(QObject *ctx, ServicesCallback &&callback, std::chrono::minutes minTtl, + const QStringList &types) +{ + if (!isSupported()) { + callback({}); + return; + } + + // check if cache is valid (no expired or ready to expire items) + ExternalServiceList ret; + bool cacheValid = true; + for (auto const &s : std::as_const(services_)) { + if (!(types.isEmpty() || types.contains(s->type))) + continue; // not interesting for us + if (!(s->expires.isForever() || s->expires.remainingTimeAsDuration() > minTtl)) { + cacheValid = false; + break; + } + ret += s; + } + + if (cacheValid && !ret.isEmpty()) { + callback(ret); + return; + } + + if (currentTask) { + connect(currentTask, &Task::finished, ctx, + [this, types, cb = std::move(callback)]() { cb(cachedServices(types)); }); + } else { + if (types.isEmpty() || types.size() > 1) { + currentTask = new JT_ExternalServiceDiscovery(client_->rootTask()); + connect(currentTask, &Task::finished, ctx, [this, types, cb = std::move(callback)]() { + services_ = currentTask->services(); + currentTask = nullptr; // it will self-delete anyway + cb(cachedServices(types)); + }); + currentTask->getServices(); + currentTask->go(true); + } else { + auto task = new JT_ExternalServiceDiscovery(client_->rootTask()); + auto type = types[0]; + connect(task, &Task::finished, ctx, + [this, task, type, cb = std::move(callback)]() { cb(task->services()); }); + task->getServices(type); + task->go(true); + // in fact we can improve caching even more if start remembering specific repviously requested types, + // even if the result was negative. + } + } +} + +ExternalServiceList ExternalServiceDiscovery::cachedServices(const QStringList &types) +{ + if (types.isEmpty()) + return services_; + + ExternalServiceList ret; + for (auto const &s : std::as_const(services_)) { + if (types.contains(s->type)) + ret += s; + } + return ret; +} + +void ExternalServiceDiscovery::credentials(QObject *ctx, ServicesCallback &&callback, + const QSet &ids) +{ + auto task = new JT_ExternalServiceDiscovery(client_->rootTask()); + connect(task, &Task::finished, ctx ? ctx : this, + [this, task, cb = std::move(callback)]() { cb(task->services()); }); + task->getCredentials(ids); + task->go(true); +} + +} // namespace XMPP diff --git a/src/xmpp/xmpp-im/xmpp_externalservicediscovery.h b/src/xmpp/xmpp-im/xmpp_externalservicediscovery.h new file mode 100644 index 00000000..563603c2 --- /dev/null +++ b/src/xmpp/xmpp-im/xmpp_externalservicediscovery.h @@ -0,0 +1,147 @@ +/* + * xmpp_externalservicedisco.h - Implementation of XEP-0215 + * Copyright (C) 2021 Sergey Ilinykh + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library. If not, see . + * + */ + +#ifndef XMPP_EXTERNALSERVICEDISCOVERY_H +#define XMPP_EXTERNALSERVICEDISCOVERY_H + +#include "xmpp_task.h" + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +namespace XMPP { + +struct ExternalService { + using Ptr = std::shared_ptr; + enum Action { Add, Delete, Modify }; + + Action action = Action::Add; + QDeadlineTimer expires; // optional + QString host; // required + QString name; // optional + QString password; // optional + std::uint16_t port; // required + bool restricted = false; // optional + QString transport; // optional + QString type; // required + QString username; // optional + + bool parse(QDomElement &el); + operator QString() const; +}; + +struct ExternalServiceId { + QString host; + QString type; + std::uint16_t port; + + bool operator==(const ExternalServiceId &other) const + { + return host == other.host && type == other.type && port == other.port; + } +}; + +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) +inline uint qHash(const ExternalServiceId &id, uint seed = 0) +#else +inline size_t qHash(const ExternalServiceId &id, size_t seed = 0) +#endif +{ + return ::qHash(id.host, seed) ^ ::qHash(id.type, seed) ^ ::qHash(id.port, seed); +} + +using ExternalServiceList = QVector; + +// XEP-0215 0.7 +class JT_ExternalServiceDiscovery : public Task { + Q_OBJECT +public: + explicit JT_ExternalServiceDiscovery(Task *parent = nullptr); + + void getServices(const QString &type = QString()); + void getCredentials(const QSet &ids); + inline const ExternalServiceList &services() const { return services_; } + + void onGo(); + bool take(const QDomElement &); + +private: + QSet creds_; + QString type_; + + ExternalServiceList services_; // result +}; + +class ExternalServiceDiscovery : public QObject { + Q_OBJECT +public: + using ServicesCallback = std::function; + + ExternalServiceDiscovery(Client *client); + + bool isSupported() const; + + /** + * @brief Resuest services from server or use cached ones + * @param ctx - if ctx dies, the request will be aborted + * @param callback - callback to call when ready + * @param minTtl - if service expires in less than minTtl it will be re-requested + * @param serviceTypes - types of services to request. e.g "stun", "turn" + */ + void services(QObject *ctx, ServicesCallback &&callback, std::chrono::minutes minTtl = std::chrono::minutes(1), + const QStringList &types = QStringList()); + ExternalServiceList cachedServices(const QStringList &type = QStringList()); + + /** + * @brief credentials resolves credentials for specific services + * @param ctx - if ctx dies, the request will be aborted + * @param callback - callback to call when ready + * @param ids - identifier of services + * + * The credentials won't be cached since it's assumed if crdentials are returned with services request then + * the creds are constant values until the service is expired. + * Otherwise `restricted` flag has to be set and the credentials are requested when they are really needed. + * Most likely with `restricted` flag those are going to be temporal credentials. Even so the caller may cache them + * on its own risk. + */ + void credentials(QObject *ctx, ServicesCallback &&callback, const QSet &ids); +signals: + // server push signals only + void serviceAdded(const ExternalServiceList &); + void serviceDeleted(ExternalServiceList &); + void serviceModified(ExternalServiceList &); + +private: + Client *client_; + QPointer currentTask = nullptr; // for all services (no type) + ExternalServiceList services_; +}; + +} // namespace XMPP + +#endif // XMPP_EXTERNALSERVICEDISCOVERY_H diff --git a/src/xmpp/xmpp-im/xmpp_features.cpp b/src/xmpp/xmpp-im/xmpp_features.cpp index 0190e67b..8aa1f5be 100644 --- a/src/xmpp/xmpp-im/xmpp_features.cpp +++ b/src/xmpp/xmpp-im/xmpp_features.cpp @@ -17,31 +17,23 @@ * */ -#include -#include -#include -#include - #include "xmpp_features.h" -#include "jingle.h" #include "jingle-ft.h" +#include "jingle.h" + +#include +#include +#include +#include using namespace XMPP; -Features::Features() -{ -} +Features::Features() { } -Features::Features(const QStringList &l) -{ - setList(l); -} +Features::Features(const QStringList &l) { setList(l); } -Features::Features(const QSet &s) -{ - setList(s); -} +Features::Features(const QSet &s) { setList(s); } Features::Features(const QString &str) { @@ -51,39 +43,43 @@ Features::Features(const QString &str) setList(l); } -Features::~Features() -{ -} +Features::~Features() { } QStringList Features::list() const { +#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0) + return QStringList(_list.begin(), _list.end()); +#else return _list.toList(); +#endif } void Features::setList(const QStringList &l) { +#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0) + _list = QSet(l.begin(), l.end()); +#else _list = QSet::fromList(l); +#endif } -void Features::setList(const QSet &l) -{ - _list = l; -} +void Features::setList(const QSet &l) { _list = l; } -void Features::addFeature(const QString& s) -{ - _list += s; -} +void Features::addFeature(const QString &s) { _list += s; } bool Features::test(const QStringList &ns) const { - return _list.contains(QSet::fromList(ns)); +#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0) + auto ss = QSet(ns.begin(), ns.end()); +#else + auto ss = QSet::fromList(ns); +#endif + return _list.contains(ss); } -bool Features::test(const QSet &ns) const -{ - return _list.contains(ns); -} +bool Features::test(const QSet &ns) const { return _list.contains(ns); } + +bool Features::test(const QString &ns) const { return _list.contains(ns); } #define FID_MULTICAST "http://jabber.org/protocol/address" bool Features::hasMulticast() const @@ -203,79 +199,92 @@ bool Features::hasJingleFT() const return test(ns); } +#define FID_JINGLEICEUDP "urn:xmpp:jingle:transports:ice-udp:1" +bool Features::hasJingleIceUdp() const { return test(QStringList() << QLatin1String(FID_JINGLEICEUDP)); } + +#define FID_JINGLEICE "urn:xmpp:jingle:transports:ice:0" +bool Features::hasJingleIce() const { return test(QStringList() << QLatin1String(FID_JINGLEICE)); } + +#define NS_CAPS "http://jabber.org/protocol/caps" +bool Features::hasCaps() const { return test(QStringList() << QLatin1String(NS_CAPS)); } + +#define NS_CAPS_OPTIMIZE "http://jabber.org/protocol/caps#optimize" +bool Features::hasCapsOptimize() const { return test(QStringList() << QLatin1String(NS_CAPS_OPTIMIZE)); } + +#define NS_DIRECT_MUC_INVITE "jabber:x:conference" +bool Features::hasDirectMucInvite() const { return test(QStringList() << QLatin1String(NS_DIRECT_MUC_INVITE)); } + // custom Psi actions #define FID_ADD "psi:add" -class Features::FeatureName : public QObject -{ +class Features::FeatureName : public QObject { Q_OBJECT public: - FeatureName() - : QObject(QCoreApplication::instance()) + FeatureName() : QObject(QCoreApplication::instance()) { id2s[FID_Invalid] = tr("ERROR: Incorrect usage of Features class"); - id2s[FID_None] = tr("None"); - id2s[FID_Register] = tr("Register"); - id2s[FID_Search] = tr("Search"); - id2s[FID_Groupchat] = tr("Groupchat"); + id2s[FID_None] = tr("None"); + id2s[FID_Register] = tr("Register"); + id2s[FID_Search] = tr("Search"); + id2s[FID_Groupchat] = tr("Groupchat"); id2s[FID_Gateway] = tr("Gateway"); - id2s[FID_Disco] = tr("Service Discovery"); - id2s[FID_VCard] = tr("VCard"); - id2s[FID_AHCommand] = tr("Execute command"); - id2s[FID_QueryVersion] = tr("Query version"); - id2s[FID_MessageCarbons]= tr("Message Carbons"); + id2s[FID_Disco] = tr("Service Discovery"); + id2s[FID_VCard] = tr("vCard"); + id2s[FID_AHCommand] = tr("Execute command"); + id2s[FID_QueryVersion] = tr("Query version"); + id2s[FID_MessageCarbons] = tr("Message Carbons"); // custom Psi actions - id2s[FID_Add] = tr("Add to roster"); + id2s[FID_Add] = tr("Add to roster"); // compute reverse map - //QMap::Iterator it = id2s.begin(); - //for ( ; it != id2s.end(); ++it) + // QMap::Iterator it = id2s.begin(); + // for ( ; it != id2s.end(); ++it) // s2id[it.data()] = it.key(); - id2f[FID_Register] = FID_REGISTER; - id2f[FID_Search] = FID_SEARCH; - id2f[FID_Groupchat] = FID_GROUPCHAT; + id2f[FID_Register] = FID_REGISTER; + id2f[FID_Search] = FID_SEARCH; + id2f[FID_Groupchat] = FID_GROUPCHAT; id2f[FID_Gateway] = FID_GATEWAY; - id2f[FID_Disco] = FID_DISCO; - id2f[FID_VCard] = FID_VCARD; - id2f[FID_AHCommand] = FID_AHCOMMAND; - id2f[FID_QueryVersion] = FID_QUERYVERSION; - id2f[FID_MessageCarbons]= FID_MESSAGECARBONS; + id2f[FID_Disco] = FID_DISCO; + id2f[FID_VCard] = FID_VCARD; + id2f[FID_AHCommand] = FID_AHCOMMAND; + id2f[FID_QueryVersion] = FID_QUERYVERSION; + id2f[FID_MessageCarbons] = FID_MESSAGECARBONS; // custom Psi actions - id2f[FID_Add] = FID_ADD; + id2f[FID_Add] = FID_ADD; } - //QMap s2id; + // QMap s2id; QMap id2s; QMap id2f; }; -static Features::FeatureName *featureName = 0; +static Features::FeatureName *featureName = nullptr; long Features::id() const { - if ( _list.count() > 1 ) + if (_list.count() > 1) return FID_Invalid; - else if ( hasRegister() ) + else if (hasRegister()) return FID_Register; - else if ( hasSearch() ) + else if (hasSearch()) return FID_Search; - else if ( hasGroupchat() ) + else if (hasGroupchat()) return FID_Groupchat; - else if ( hasGateway() ) + else if (hasGateway()) return FID_Gateway; - else if ( hasDisco() ) + else if (hasDisco()) return FID_Disco; - else if ( hasVCard() ) + else if (hasVCard()) return FID_VCard; - else if ( hasCommand() ) + else if (hasCommand()) return FID_AHCommand; - else if ( test(QStringList(FID_ADD)) ) + else if (test(QStringList(FID_ADD))) return FID_Add; - else if ( hasVersion() ) + else if (hasVersion()) return FID_QueryVersion; return FID_None; @@ -289,7 +298,7 @@ long Features::id(const QString &feature) QString Features::feature(long id) { - if ( !featureName ) + if (!featureName) featureName = new FeatureName(); return featureName->id2f[id]; @@ -303,16 +312,13 @@ Features &Features::operator<<(const QString &feature) QString Features::name(long id) { - if ( !featureName ) + if (!featureName) featureName = new FeatureName(); return featureName->id2s[id]; } -QString Features::name() const -{ - return name(id()); -} +QString Features::name() const { return name(id()); } QString Features::name(const QString &feature) { diff --git a/src/xmpp/xmpp-im/xmpp_features.h b/src/xmpp/xmpp-im/xmpp_features.h index d3117675..6b7f3885 100644 --- a/src/xmpp/xmpp-im/xmpp_features.h +++ b/src/xmpp/xmpp-im/xmpp_features.h @@ -20,94 +20,102 @@ #ifndef XMPP_FEATURES_H #define XMPP_FEATURES_H -#include #include +#include class QString; -namespace XMPP -{ - class Features - { - public: - Features(); - Features(const QStringList &); - Features(const QSet &); - Features(const QString &); - ~Features(); - - QStringList list() const; // actual featurelist - void setList(const QStringList &); - void setList(const QSet &); - void addFeature(const QString&); - - // features - inline bool isEmpty() const { return _list.isEmpty(); } - bool hasRegister() const; - bool hasSearch() const; - bool hasMulticast() const; - bool hasGroupchat() const; - bool hasVoice() const; - bool hasDisco() const; - bool hasChatState() const; - bool hasCommand() const; - bool hasGateway() const; - bool hasVersion() const; - bool hasVCard() const; - bool hasMessageCarbons() const; - bool hasJingleFT() const; - - [[deprecated]] inline bool canRegister() const { return hasRegister(); } - [[deprecated]] inline bool canSearch() const { return hasSearch(); } - [[deprecated]] inline bool canMulticast() const { return hasMulticast(); } - [[deprecated]] inline bool canGroupchat() const { return hasGroupchat(); } - [[deprecated]] inline bool canVoice() const { return hasVoice(); } - [[deprecated]] inline bool canDisco() const { return hasDisco(); } - [[deprecated]] inline bool canChatState() const { return hasChatState(); } - [[deprecated]] inline bool canCommand() const { return hasCommand(); } - [[deprecated]] inline bool isGateway() const { return hasGateway(); } - [[deprecated]] inline bool haveVCard() const { return hasVCard(); } - [[deprecated]] inline bool canMessageCarbons() const { return hasMessageCarbons(); } - - enum FeatureID { - FID_Invalid = -1, - FID_None, - FID_Register, - FID_Search, - FID_Groupchat, - FID_Disco, - FID_Gateway, - FID_VCard, - FID_AHCommand, - FID_QueryVersion, - FID_MessageCarbons, - - // private Psi actions - FID_Add - }; - - // useful functions - inline bool test(const char *ns) const - { return test(QSet() << QLatin1String(ns)); } - bool test(const QStringList &) const; - bool test(const QSet &) const; - - QString name() const; - static QString name(long id); - static QString name(const QString &feature); - - long id() const; - static long id(const QString &feature); - static QString feature(long id); - - Features &operator<<(const QString &feature); - inline bool operator==(const Features &other) - { return _list == other._list; } - - class FeatureName; - private: - QSet _list; +namespace XMPP { +class Features { +public: + Features(); + Features(const QStringList &); + Features(const QSet &); + Features(const QString &); + ~Features(); + + QStringList list() const; // actual featurelist + void setList(const QStringList &); + void setList(const QSet &); + void addFeature(const QString &); + + // features + inline bool isEmpty() const { return _list.isEmpty(); } + bool hasRegister() const; + bool hasSearch() const; + bool hasMulticast() const; + bool hasGroupchat() const; + bool hasVoice() const; + bool hasDisco() const; + bool hasChatState() const; + bool hasCommand() const; + bool hasGateway() const; + bool hasVersion() const; + bool hasVCard() const; + bool hasMessageCarbons() const; + bool hasJingleFT() const; + bool hasJingleIceUdp() const; + bool hasJingleIce() const; + bool hasCaps() const; + bool hasCapsOptimize() const; + bool hasDirectMucInvite() const; + + [[deprecated]] inline bool canRegister() const { return hasRegister(); } + [[deprecated]] inline bool canSearch() const { return hasSearch(); } + [[deprecated]] inline bool canMulticast() const { return hasMulticast(); } + [[deprecated]] inline bool canGroupchat() const { return hasGroupchat(); } + [[deprecated]] inline bool canVoice() const { return hasVoice(); } + [[deprecated]] inline bool canDisco() const { return hasDisco(); } + [[deprecated]] inline bool canChatState() const { return hasChatState(); } + [[deprecated]] inline bool canCommand() const { return hasCommand(); } + [[deprecated]] inline bool isGateway() const { return hasGateway(); } + [[deprecated]] inline bool haveVCard() const { return hasVCard(); } + [[deprecated]] inline bool canMessageCarbons() const { return hasMessageCarbons(); } + + enum FeatureID { + FID_Invalid = -1, + FID_None, + FID_Register, + FID_Search, + FID_Groupchat, + FID_Disco, + FID_Gateway, + FID_VCard, + FID_AHCommand, + FID_QueryVersion, + FID_MessageCarbons, + + // private Psi actions + FID_Add }; -} -#endif + // useful functions + inline bool test(const char *ns) const { return test(QLatin1String(ns)); } + bool test(const QString &) const; + bool test(const QStringList &) const; + bool test(const QSet &) const; + + QString name() const; + static QString name(long id); + static QString name(const QString &feature); + + long id() const; + static long id(const QString &feature); + static QString feature(long id); + + Features &operator<<(const QString &feature); + inline bool operator==(const Features &other) const { return _list == other._list; } + Features &operator+=(const Features &other) + { + _list += other._list; + return *this; + } + + class FeatureName; + +private: + QSet _list; +}; +} // namespace XMPP + +#endif // XMPP_FEATURES_H diff --git a/src/xmpp/xmpp-im/xmpp_form.h b/src/xmpp/xmpp-im/xmpp_form.h new file mode 100644 index 00000000..d45624bd --- /dev/null +++ b/src/xmpp/xmpp-im/xmpp_form.h @@ -0,0 +1,96 @@ +/* + * xmpp_form.h - XMPP form + * Copyright (C) 2003 Justin Karneges + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library. If not, see . + * + */ + +#pragma once + +#include +#include + +namespace XMPP { +typedef QList AgentList; +typedef QList DiscoList; + +class FormField { +public: + enum { username, nick, password, name, first, last, email, address, city, state, zip, phone, url, date, misc }; + FormField(const QString &type = "", const QString &value = ""); + ~FormField(); + + int type() const; + QString fieldName() const; + QString realName() const; + bool isSecret() const; + const QString &value() const; + void setType(int); + bool setType(const QString &); + void setValue(const QString &); + +private: + int tagNameToType(const QString &) const; + QString typeToTagName(int) const; + + int v_type; + QString v_value; + + class Private; + Private *d = nullptr; +}; + +class Form : public QList { +public: + Form(const Jid &j = ""); + ~Form(); + + Jid jid() const; + QString instructions() const; + QString key() const; + void setJid(const Jid &); + void setInstructions(const QString &); + void setKey(const QString &); + +private: + Jid v_jid; + QString v_instructions, v_key; + + class Private; + Private *d = nullptr; +}; + +class SearchResult { +public: + SearchResult(const Jid &jid = ""); + ~SearchResult(); + + const Jid &jid() const; + const QString &nick() const; + const QString &first() const; + const QString &last() const; + const QString &email() const; + + void setJid(const Jid &); + void setNick(const QString &); + void setFirst(const QString &); + void setLast(const QString &); + void setEmail(const QString &); + +private: + Jid v_jid; + QString v_nick, v_first, v_last, v_email; +}; +} // namespace XMPP diff --git a/src/xmpp/xmpp-im/xmpp_hash.cpp b/src/xmpp/xmpp-im/xmpp_hash.cpp new file mode 100644 index 00000000..decfb0e4 --- /dev/null +++ b/src/xmpp/xmpp-im/xmpp_hash.cpp @@ -0,0 +1,348 @@ +/* + * Copyright (C) 2019-2021 Sergey Ilinykh + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library. If not, see . + * + */ + +#include "xmpp_hash.h" + +#include "xmpp/blake2/blake2qt.h" +#include "xmpp_features.h" +#include "xmpp_xmlcommon.h" + +#include +#include +#include + +#include +#include + +namespace XMPP { + +//---------------------------------------------------------------------------- +// Hash +//---------------------------------------------------------------------------- +static const char *const sha1_synonims[] = { "sha1", nullptr }; +// NOTE: keep this in sync with enum. same order! +struct HashDesc { + const char *text; + Hash::Type hashType; + const char *const *synonims = nullptr; +}; +static const std::array hashTypes { + HashDesc { "unknown", Hash::Type::Unknown }, HashDesc { "sha-1", Hash::Type::Sha1, sha1_synonims }, + HashDesc { "sha-256", Hash::Type::Sha256 }, HashDesc { "sha-512", Hash::Type::Sha512 }, + HashDesc { "sha3-256", Hash::Type::Sha3_256 }, HashDesc { "sha3-512", Hash::Type::Sha3_512 }, + HashDesc { "blake2b-256", Hash::Type::Blake2b256 }, HashDesc { "blake2b-512", Hash::Type::Blake2b512 } +}; + +using HashVariant = std::variant; +HashVariant findHasher(Hash::Type hashType) +{ + QString qcaType; + QCryptographicHash::Algorithm qtType = QCryptographicHash::Algorithm(-1); + Blake2Hash::DigestSize blakeDS = Blake2Hash::DigestSize(-1); + + switch (hashType) { + case Hash::Type::Sha1: + qtType = QCryptographicHash::Sha1; + qcaType = "sha1"; + break; + case Hash::Type::Sha256: + qtType = QCryptographicHash::Sha256; + qcaType = "sha256"; + break; + case Hash::Type::Sha512: + qtType = QCryptographicHash::Sha512; + qcaType = "sha512"; + break; + case Hash::Type::Sha3_256: + qtType = QCryptographicHash::Sha3_256; + qcaType = "sha3_256"; + break; + case Hash::Type::Sha3_512: + qtType = QCryptographicHash::Sha3_512; + qcaType = "sha3_512"; + break; + case Hash::Type::Blake2b256: + qcaType = "blake2b_256"; + blakeDS = Blake2Hash::Digest256; + break; + case Hash::Type::Blake2b512: + qcaType = "blake2b_512"; + blakeDS = Blake2Hash::Digest512; + break; + case Hash::Type::Unknown: + default: + qDebug("invalid hash type"); + return nullptr; + } + + if (!qcaType.isEmpty()) { + QCA::Hash hashObj(qcaType); + if (hashObj.context()) { + return hashObj; + } + } + + if (qtType != QCryptographicHash::Algorithm(-1)) { + return HashVariant { std::in_place_type, qtType }; + } else if (blakeDS != Blake2Hash::DigestSize(-1)) { + Blake2Hash bh(blakeDS); + if (bh.isValid()) { + return HashVariant { std::in_place_type, std::move(bh) }; + } + } + return nullptr; +} + +Hash::Hash(const QDomElement &el) +{ + QString algo = el.attribute(QLatin1String("algo")); + v_type = parseType(QStringView { algo }); + if (v_type != Unknown && el.tagName() == QLatin1String("hash")) { + v_data = QByteArray::fromBase64(el.text().toLatin1()); + if (v_data.isEmpty()) { + v_type = Type::Unknown; + } + } +} + +QString Hash::stringType() const +{ + if (!v_type || int(v_type) > LastType) + return QString(); // must be empty. other code relies on it + static_assert(LastType + 1 == hashTypes.size(), "hashType and enum are not in sync"); + return QLatin1String(hashTypes[int(v_type)].text); +} + +Hash::Type Hash::parseType(const QStringView &algo) +{ + if (!algo.isEmpty()) { + for (auto const &hash : hashTypes) { + if (algo == QLatin1String(hash.text)) { + return hash.hashType; + } + if (hash.synonims) { + auto cur = hash.synonims; + while (*cur) { + if (algo == QLatin1String(*cur)) { + return hash.hashType; + } + cur++; + } + } + } + } + return Unknown; +} + +bool Hash::compute(const QByteArray &ba) +{ + v_data.clear(); + auto hasher = findHasher(v_type); + std::visit( + [&ba, this](auto &&arg) { + using T = std::decay_t; + if constexpr (std::is_same_v) { + arg.update(ba); + v_data = arg.final().toByteArray(); + } else if constexpr (std::is_same_v) { + arg.addData(ba); + v_data = arg.result(); + } else if constexpr (std::is_same_v) { + if (arg.addData(ba)) + v_data = arg.final(); + } + }, + hasher); + + if (!v_data.isEmpty()) + return true; + + qDebug("failed to compute %s hash for %lld bytes", qPrintable(stringType()), qsizetype(ba.size())); + return false; +} + +bool Hash::compute(QIODevice *dev) +{ + v_data.clear(); + auto hasher = findHasher(v_type); + std::visit( + [dev, this](auto &&arg) { + using T = std::decay_t; + if constexpr (std::is_same_v) { + arg.update(dev); + v_data = arg.final().toByteArray(); + } else if constexpr (std::is_same_v) { + arg.addData(dev); + v_data = arg.result(); + } else if constexpr (std::is_same_v) { + if (arg.addData(dev)) + v_data = arg.final(); + } + }, + hasher); + + if (!v_data.isEmpty()) + return true; + + qDebug("failed to compute %s hash on device 0x%p", qPrintable(stringType()), dev); + return false; +} + +QDomElement Hash::toXml(QDomDocument *doc) const +{ + auto stype = stringType(); + if (!stype.isEmpty()) { + auto el = doc->createElementNS(HASH_NS, QLatin1String(v_data.isEmpty() ? "hash-used" : "hash")); + el.setAttribute(QLatin1String("algo"), stype); + if (!v_data.isEmpty()) { + XMLHelper::setTagText(el, v_data.toBase64()); + } + return el; + } + return QDomElement(); +} + +void Hash::populateFeatures(Features &features) +{ + features.addFeature("urn:xmpp:hashes:2"); + for (auto const &hash : hashTypes) { + features.addFeature(QLatin1String("urn:xmpp:hash-function-text-names:") + QLatin1String(hash.text)); + } +} + +Hash Hash::from(XMPP::Hash::Type t, const QByteArray &fileData) +{ + Hash h(t); + if (!h.compute(fileData)) + h.setType(Unknown); + return h; +} + +Hash Hash::from(XMPP::Hash::Type t, QIODevice *dev) +{ + Hash h(t); + if (!h.compute(dev)) + h.setType(Unknown); + return h; +} + +Hash Hash::from(Hash::Type t, const QFileInfo &file) +{ + if (file.isReadable()) { + QFile f(file.filePath()); + f.open(QIODevice::ReadOnly); + return from(t, &f); + } + return Hash(); +} + +Hash Hash::from(const QStringView &str) +{ + auto ind = str.indexOf('+'); + if (ind <= 0) + return Hash(); + Hash hash(str.left(ind)); + if (hash.isValid()) { + auto data = QByteArray::fromHex(str.mid(ind + 1).toLatin1()); + if (data.size()) + hash.setData(data); + else + hash = Hash(); + } + return hash; +} + +Hash Hash::fastestHash(const Features &features) +{ + std::array qcaAlgos = { "blake2b_512", "blake2b_256", "sha1", "sha512", "sha256", "sha3_256", "sha3_512" }; + std::array qcaMap = { Blake2b512, Blake2b256, Sha1, Sha512, Sha256, Sha3_256, Sha3_512 }; + QStringList priorityFeatures; + priorityFeatures.reserve(int(qcaAlgos.size())); + for (auto t : qcaMap) { + priorityFeatures.append(QString(QLatin1String("urn:xmpp:hash-function-text-names:")) + + QLatin1String(hashTypes[int(t)].text)); + // REVIEW modify hashTypes with priority info instead? + } + for (int i = 0; i < qcaAlgos.size(); i++) { + if (QCA::isSupported(qcaAlgos[i]) && features.test(priorityFeatures[i])) { + return Hash(qcaMap[i]); + } + } + return Hash(); // qca is the fastest and it defintiely has sha1. so no reason to use qt or custom blake +} + +class StreamHashPrivate { +public: + Hash::Type type; + HashVariant hasher; + StreamHashPrivate(Hash::Type type) : type(type), hasher(findHasher(type)) { } +}; + +StreamHash::StreamHash(Hash::Type type) : d(new StreamHashPrivate(type)) { } + +StreamHash::~StreamHash() { } + +bool StreamHash::addData(const QByteArray &data) +{ + if (data.isEmpty()) + return true; + + bool ret = true; + std::visit( + [&data, &ret](auto &&arg) { + using T = std::decay_t; + if constexpr (std::is_same_v) { + arg.update(data); + } else if constexpr (std::is_same_v) { + arg.addData(data); + } else if constexpr (std::is_same_v) { + ret = arg.addData(data); + } else + ret = false; + }, + d->hasher); + return ret; +} + +Hash StreamHash::final() +{ + auto data = std::visit( + [](auto &&arg) { + using T = std::decay_t; + if constexpr (std::is_same_v) { + return arg.final().toByteArray(); + } else if constexpr (std::is_same_v) { + return arg.result(); + } else if constexpr (std::is_same_v) { + return arg.final(); + } + return QByteArray(); + }, + d->hasher); + + Hash h(d->type, data); + if (data.isEmpty()) { + qDebug("failed to compute %s hash on a stream", qPrintable(h.stringType())); + return {}; + } + return h; +} + +void StreamHash::restart() { d.reset(new StreamHashPrivate(d->type)); } + +} // namespace XMPP diff --git a/src/xmpp/xmpp-im/xmpp_hash.h b/src/xmpp/xmpp-im/xmpp_hash.h index df6ddfb4..67400493 100644 --- a/src/xmpp/xmpp-im/xmpp_hash.h +++ b/src/xmpp/xmpp-im/xmpp_hash.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019 Sergey Ilinykh + * Copyright (C) 2019-2021 Sergey Ilinykh * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -19,53 +19,95 @@ #ifndef XMPP_HASH_H #define XMPP_HASH_H -#include "xmpp_stanza.h" - +#include #include -#include -#define XMPP_HASH_NS "urn:xmpp:hashes:2" // TODO make nsdb.cpp/h with static declarations of all ns + +#include class QDomElement; +class QFileInfo; +class QIODevice; +class QDomDocument; -namespace XMPP -{ - class Features; - - class Hash - { - public: - enum Type { // XEP-0300 Version 0.5.3 (2018-02-14) - Unknown, // not standard, just a default - Sha1, // SHOULD NOT - Sha256, // MUST - Sha512, // SHOULD - Sha3_256, // MUST - Sha3_512, // SHOULD - Blake2b256, // MUST - Blake2b512, // SHOULD - LastType = Blake2b512 - }; - - inline Hash(Type type = Type::Unknown) : v_type(type) {} - Hash(const QDomElement&); - - inline bool isValid() const { return v_type > Unknown && v_type <= LastType; } - - inline Type type() const { return v_type; } - inline void setType(Type t) { v_type = t; } - - inline QByteArray data() const { return v_data; } - inline void setData(const QByteArray &d) { v_data = d; } // sets already computed hash - bool computeFromData(const QByteArray &); // computes hash from passed data - bool computeFromDevice(QIODevice *dev); - - QDomElement toXml(QDomDocument *doc) const; - static void populateFeatures(XMPP::Features &); - - private: - Type v_type = Type::Unknown; - QByteArray v_data; +namespace XMPP { + +extern QString HASH_NS; +class Features; + +class Hash { +public: + // NB: keep this in sync with Hash::fastestHash() and with hashTypes in cpp the file! + enum Type { // XEP-0300 Version 0.5.3 (2018-02-14) + Unknown, // not standard, just a default + Sha1, // SHOULD NOT + Sha256, // MUST + Sha512, // SHOULD + Sha3_256, // MUST + Sha3_512, // SHOULD + Blake2b256, // MUST + Blake2b512, // SHOULD + LastType = Blake2b512 }; -} + inline Hash(Type type = Type::Unknown) : v_type(type) { } + inline Hash(Type type, const QByteArray &data) : v_type(type), v_data(data) { } + inline Hash(const QStringView &algo) : v_type(parseType(algo)) { } + Hash(const QDomElement &); + + inline bool operator==(const Hash &other) const { return v_type == other.v_type && v_data == other.v_data; } + + inline bool isValid() const { return v_type > Unknown && v_type <= LastType; } + inline operator bool() const { return isValid(); } + + inline Type type() const { return v_type; } + inline void setType(Type t) { v_type = t; } + QString stringType() const; + static Type parseType(const QStringView &algo); + + inline QByteArray data() const { return v_data; } + inline void setData(const QByteArray &d) { v_data = d; } // sets already computed hash + inline QByteArray toHex() const { return v_data.toHex(); } + inline QByteArray toBase64() const { return v_data.toBase64(); } + inline QString toString() const { return QString("%1+%2").arg(stringType(), QString::fromLatin1(v_data.toHex())); } + bool compute(const QByteArray &); // computes hash from passed data + bool compute(QIODevice *dev); // reads all the device and computes hash from the data + + QDomElement toXml(QDomDocument *doc) const; + static void populateFeatures(XMPP::Features &); + static Hash from(Type t, const QByteArray &fileData); + static Hash from(XMPP::Hash::Type t, QIODevice *dev); + static Hash from(XMPP::Hash::Type t, const QFileInfo &file); + static Hash from(const QStringView &str); // e.g. sha1+aabccddeeffaabbcc232387539465923645 + static Hash fastestHash(const Features &features); + +private: + Type v_type = Type::Unknown; + QByteArray v_data; +}; + +class StreamHashPrivate; +class StreamHash { +public: + StreamHash(Hash::Type type); + ~StreamHash(); + bool addData(const QByteArray &data); + Hash final(); + void restart(); + +private: + friend class StreamHashPrivate; + std::unique_ptr d; +}; + +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) +Q_DECL_PURE_FUNCTION inline uint qHash(const Hash &hash, uint seed = 0) Q_DECL_NOTHROW +#else +Q_DECL_PURE_FUNCTION inline size_t qHash(const Hash &hash, size_t seed = 0) Q_DECL_NOTHROW #endif +{ + return qHash(hash.data(), seed); +} + +} // namespace XMPP + +#endif // XMPP_HASH_H diff --git a/src/xmpp/xmpp-im/xmpp_htmlelement.h b/src/xmpp/xmpp-im/xmpp_htmlelement.h index 57ca1d66..b7dbc1c5 100644 --- a/src/xmpp/xmpp-im/xmpp_htmlelement.h +++ b/src/xmpp/xmpp-im/xmpp_htmlelement.h @@ -23,26 +23,24 @@ class QString; -namespace XMPP -{ - class HTMLElement - { - public: - HTMLElement(); - HTMLElement(const QDomElement &body); - - void setBody(const QDomElement &body); - const QDomElement& body() const; - QString toString(const QString &rootTagName = "body") const; - QString text() const; - void filterOutUnwanted(bool strict = false); - - private: - void filterOutUnwantedRecursive(QDomElement &el, bool strict); - - QDomDocument doc_; - QDomElement body_; - }; -} - -#endif +namespace XMPP { +class HTMLElement { +public: + HTMLElement(); + HTMLElement(const QDomElement &body); + + void setBody(const QDomElement &body); + const QDomElement &body() const; + QString toString(const QString &rootTagName = "body") const; + QString text() const; + void filterOutUnwanted(bool strict = false); + +private: + void filterOutUnwantedRecursive(QDomElement &el, bool strict); + + QDomDocument doc_; + QDomElement body_; +}; +} // namespace XMPP + +#endif // XMPP_HTMLELEMENT_H diff --git a/src/xmpp/xmpp-im/xmpp_httpauthrequest.h b/src/xmpp/xmpp-im/xmpp_httpauthrequest.h index 4702dee6..882f7809 100644 --- a/src/xmpp/xmpp-im/xmpp_httpauthrequest.h +++ b/src/xmpp/xmpp-im/xmpp_httpauthrequest.h @@ -21,36 +21,37 @@ #include -class QDomElement; +#include "iris/xmpp_stanza.h" + class QDomDocument; +class QDomElement; + +namespace XMPP { +class HttpAuthRequest { +public: + HttpAuthRequest(const QString &m, const QString &u, const QString &i); + HttpAuthRequest(const QString &m = QString(), const QString &u = QString()); + HttpAuthRequest(const QDomElement &); + + bool isEmpty() const; + + void setMethod(const QString &); + void setUrl(const QString &); + void setId(const QString &); + QString method() const; + QString url() const; + QString id() const; + bool hasId() const; + + QDomElement toXml(QDomDocument &) const; + bool fromXml(const QDomElement &); + + static Stanza::Error denyError; + +private: + QString method_, url_, id_; + bool hasId_; +}; +} // namespace XMPP -namespace XMPP -{ - class HttpAuthRequest - { - public: - HttpAuthRequest(const QString &m, const QString &u, const QString &i); - HttpAuthRequest(const QString &m = QString(), const QString &u = QString()); - HttpAuthRequest(const QDomElement &); - - bool isEmpty() const; - - void setMethod(const QString&); - void setUrl(const QString&); - void setId(const QString&); - QString method() const; - QString url() const; - QString id() const; - bool hasId() const; - - QDomElement toXml(QDomDocument &) const; - bool fromXml(const QDomElement &); - - static Stanza::Error denyError; - private: - QString method_, url_, id_; - bool hasId_; - }; -} - -#endif +#endif // XMPP_AUTHREQUEST_H diff --git a/src/xmpp/xmpp-im/xmpp_ibb.cpp b/src/xmpp/xmpp-im/xmpp_ibb.cpp index cf37f7f2..645c7b95 100644 --- a/src/xmpp/xmpp-im/xmpp_ibb.cpp +++ b/src/xmpp/xmpp-im/xmpp_ibb.cpp @@ -1,6 +1,6 @@ /* * ibb.cpp - Inband bytestream - * Copyright (C) 2001, 2002 Justin Karneges + * Copyright (C) 2001-2002 Justin Karneges * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -19,52 +19,50 @@ #include "xmpp_ibb.h" -#include +#include "xmpp_client.h" +#include "xmpp_stream.h" #include "xmpp_xmlcommon.h" -#include -#include +#include +#include -#define IBB_PACKET_SIZE 4096 -#define IBB_PACKET_DELAY 0 +#define IBB_PACKET_DELAY 0 using namespace XMPP; -static int num_conn = 0; -static int id_conn = 0; -static const char *IBB_NS = "http://jabber.org/protocol/ibb"; +static int num_conn = 0; +static int id_conn = 0; +static const char *IBB_NS = "http://jabber.org/protocol/ibb"; //---------------------------------------------------------------------------- // IBBConnection //---------------------------------------------------------------------------- -class IBBConnection::Private -{ +class IBBConnection::Private { public: Private() = default; - int state = 0; - quint16 seq = 0; - Jid peer; - QString sid; + int state = 0; + quint16 seq = 0; + Jid peer; + QString sid; IBBManager *m = nullptr; - JT_IBB *j = nullptr; - QString iq_id; - QString stanza; + JT_IBB *j = nullptr; + QString iq_id; + QString stanza; - int blockSize = 0; - //QByteArray recvBuf, sendBuf; + int blockSize = IBBConnection::PacketSize; + // QByteArray recvBuf, sendBuf; bool closePending, closing; int id; // connection id }; -IBBConnection::IBBConnection(IBBManager *m) - : BSConnection(m) +IBBConnection::IBBConnection(IBBManager *m) : BSConnection(m) { - d = new Private; - d->m = m; - d->j = 0; - d->blockSize = IBB_PACKET_SIZE; + d = new Private; + d->m = m; + d->j = nullptr; + d->blockSize = PacketSize; resetConnection(); ++num_conn; @@ -77,18 +75,18 @@ IBBConnection::IBBConnection(IBBManager *m) void IBBConnection::resetConnection(bool clear) { d->m->unlink(this); - d->state = Idle; + d->state = Idle; d->closePending = false; - d->closing = false; - d->seq = 0; + d->closing = false; + d->seq = 0; delete d->j; - d->j = 0; + d->j = nullptr; clearWriteBuffer(); - if(clear) + if (clear) clearReadBuffer(); - setOpenMode(clear || !bytesAvailable()? QIODevice::NotOpen : QIODevice::ReadOnly); + setOpenMode(clear || !bytesAvailable() ? QIODevice::NotOpen : QIODevice::ReadOnly); } IBBConnection::~IBBConnection() @@ -104,14 +102,16 @@ IBBConnection::~IBBConnection() delete d; } +void IBBConnection::setPacketSize(int blockSize) { d->blockSize = blockSize; } + void IBBConnection::connectToJid(const Jid &peer, const QString &sid) { close(); resetConnection(true); d->state = Requesting; - d->peer = peer; - d->sid = sid; + d->peer = peer; + d->sid = sid; #ifdef IBB_DEBUG qDebug("IBBConnection[%d]: initiating request to %s", d->id, qPrintable(peer.full())); @@ -119,18 +119,17 @@ void IBBConnection::connectToJid(const Jid &peer, const QString &sid) d->j = new JT_IBB(d->m->client()->rootTask()); connect(d->j, SIGNAL(finished()), SLOT(ibb_finished())); - d->j->request(d->peer, d->sid); + d->j->request(d->peer, d->sid, d->blockSize); d->j->go(true); } void IBBConnection::accept() { - if(d->state != WaitingForAccept) + if (d->state != WaitingForAccept) return; #ifdef IBB_DEBUG - qDebug("IBBConnection[%d]: accepting %s [%s]", d->id, - qPrintable(d->peer.full()), qPrintable(d->sid)); + qDebug("IBBConnection[%d]: accepting %s [%s]", d->id, qPrintable(d->peer.full()), qPrintable(d->sid)); #endif d->m->doAccept(this, d->iq_id); @@ -143,10 +142,10 @@ void IBBConnection::accept() void IBBConnection::close() { - if(d->state == Idle) + if (d->state == Idle) return; - if(d->state == WaitingForAccept) { + if (d->state == WaitingForAccept) { d->m->doReject(this, d->iq_id, Stanza::Error::Forbidden, "Rejected"); resetConnection(); return; @@ -156,12 +155,12 @@ void IBBConnection::close() qDebug("IBBConnection[%d]: closing", d->id); #endif - if(d->state == Active) { + if (d->state == Active) { d->closePending = true; trySend(); // if there is data pending to be written, then pend the closing - if(bytesToWrite() > 0) { + if (bytesToWrite() > 0 || d->closing) { return; } } @@ -169,60 +168,40 @@ void IBBConnection::close() resetConnection(); } -int IBBConnection::state() const -{ - return d->state; -} +int IBBConnection::state() const { return d->state; } -Jid IBBConnection::peer() const -{ - return d->peer; -} +Jid IBBConnection::peer() const { return d->peer; } -QString IBBConnection::sid() const -{ - return d->sid; -} +QString IBBConnection::sid() const { return d->sid; } -BytestreamManager* IBBConnection::manager() const -{ - return d->m; -} +BytestreamManager *IBBConnection::manager() const { return d->m; } -bool IBBConnection::isOpen() const -{ - if(d->state == Active) - return true; - else - return false; -} +bool IBBConnection::isOpen() const { return d->state == Active; } qint64 IBBConnection::writeData(const char *data, qint64 maxSize) { - if(d->state != Active || d->closePending || d->closing) { + if (d->state != Active || d->closePending || d->closing) { setErrorString("read only"); return 0; } - ByteStream::appendWrite(QByteArray::fromRawData(data, maxSize)); + ByteStream::appendWrite(QByteArray::fromRawData(data, int(maxSize))); trySend(); return maxSize; } -void IBBConnection::waitForAccept(const Jid &peer, const QString &iq_id, - const QString &sid, int blockSize, +void IBBConnection::waitForAccept(const Jid &peer, const QString &iq_id, const QString &sid, int blockSize, const QString &stanza) { close(); resetConnection(true); - d->state = WaitingForAccept; - d->peer = peer; - d->iq_id = iq_id; - d->sid = sid; + d->state = WaitingForAccept; + d->peer = peer; + d->iq_id = iq_id; + d->sid = sid; d->blockSize = blockSize; - d->stanza = stanza; - + d->stanza = stanza; } void IBBConnection::takeIncomingData(const IBBData &ibbData) @@ -250,41 +229,37 @@ void IBBConnection::setRemoteClosed() void IBBConnection::ibb_finished() { JT_IBB *j = d->j; - d->j = 0; + d->j = nullptr; - if(j->success()) { - if(j->mode() == JT_IBB::ModeRequest) { + if (j->success()) { + if (j->mode() == JT_IBB::ModeRequest) { #ifdef IBB_DEBUG - qDebug("IBBConnection[%d]: %s [%s] accepted.", d->id, - qPrintable(d->peer.full()), qPrintable(d->sid)); + qDebug("IBBConnection[%d]: %s [%s] accepted.", d->id, qPrintable(d->peer.full()), qPrintable(d->sid)); #endif d->state = Active; setOpenMode(QIODevice::ReadWrite); d->m->link(this); emit connected(); - } - else { - if(d->closing) { + } else { + if (d->closing) { resetConnection(); emit delayedCloseFinished(); } - if(bytesToWrite() || d->closePending) + if (bytesToWrite() || d->closePending) QTimer::singleShot(IBB_PACKET_DELAY, this, SLOT(trySend())); emit bytesWritten(j->bytesWritten()); // will delete this connection if no bytes left. } - } - else { - if(j->mode() == JT_IBB::ModeRequest) { + } else { + if (j->mode() == JT_IBB::ModeRequest) { #ifdef IBB_DEBUG qDebug("IBBConnection[%d]: %s refused.", d->id, qPrintable(d->peer.full())); #endif resetConnection(true); setError(ErrRequest); - } - else { + } else { resetConnection(true); setError(ErrData); } @@ -294,24 +269,22 @@ void IBBConnection::ibb_finished() void IBBConnection::trySend() { // if we already have an active task, then don't do anything - if(d->j) + if (d->j) return; QByteArray a = takeWrite(d->blockSize); - if(a.isEmpty()) { + if (a.isEmpty()) { if (!d->closePending) return; // null operation? d->closePending = false; - d->closing = true; + d->closing = true; #ifdef IBB_DEBUG qDebug("IBBConnection[%d]: closing", d->id); #endif - } - else { + } else { #ifdef IBB_DEBUG - qDebug("IBBConnection[%d]: sending [%d] bytes (%d bytes left)", - d->id, a.size(), bytesToWrite()); + qDebug("IBBConnection[%d]: sending [%d] bytes (%d bytes left)", d->id, a.size(), bytesToWrite()); #endif } @@ -319,30 +292,26 @@ void IBBConnection::trySend() connect(d->j, SIGNAL(finished()), SLOT(ibb_finished())); if (d->closing) { d->j->close(d->peer, d->sid); - } - else { + } else { d->j->sendData(d->peer, IBBData(d->sid, d->seq++, a)); } d->j->go(true); } - - //---------------------------------------------------------------------------- // IBBData //---------------------------------------------------------------------------- -IBBData& IBBData::fromXml(const QDomElement &e) +IBBData &IBBData::fromXml(const QDomElement &e) { - sid = e.attribute("sid"); - seq = e.attribute("seq").toInt(); + sid = e.attribute("sid"); + seq = quint16(e.attribute("seq").toInt()); data = QByteArray::fromBase64(e.text().toUtf8()); return *this; } QDomElement IBBData::toXml(QDomDocument *doc) const { - QDomElement query = textTag(doc, "data", QString::fromLatin1(data.toBase64())).toElement(); - query.setAttribute("xmlns", IBB_NS); + QDomElement query = textTagNS(doc, IBB_NS, "data", QString::fromLatin1(data.toBase64())).toElement(); query.setAttribute("seq", QString::number(seq)); query.setAttribute("sid", sid); return query; @@ -351,33 +320,27 @@ QDomElement IBBData::toXml(QDomDocument *doc) const //---------------------------------------------------------------------------- // IBBManager //---------------------------------------------------------------------------- -class IBBManager::Private -{ +class IBBManager::Private { public: Private() = default; - Client *client = nullptr; + Client *client = nullptr; IBBConnectionList activeConns; IBBConnectionList incomingConns; - JT_IBB *ibb = nullptr; + JT_IBB *ibb = nullptr; }; -IBBManager::IBBManager(Client *parent) - : BytestreamManager(parent) +IBBManager::IBBManager(Client *parent) : BytestreamManager(parent) { - d = new Private; + d = new Private; d->client = parent; d->ibb = new JT_IBB(d->client->rootTask(), true); - connect(d->ibb, - SIGNAL(incomingRequest(Jid,QString,QString,int,QString)), - SLOT(ibb_incomingRequest(Jid,QString,QString,int,QString))); - connect(d->ibb, - SIGNAL(incomingData(Jid,QString,IBBData,Stanza::Kind)), - SLOT(takeIncomingData(Jid,QString,IBBData,Stanza::Kind))); - connect(d->ibb, - SIGNAL(closeRequest(Jid,QString,QString)), - SLOT(ibb_closeRequest(Jid,QString,QString))); + connect(d->ibb, SIGNAL(incomingRequest(Jid, QString, QString, int, QString)), + SLOT(ibb_incomingRequest(Jid, QString, QString, int, QString))); + connect(d->ibb, SIGNAL(incomingData(Jid, QString, IBBData, Stanza::Kind)), + SLOT(takeIncomingData(Jid, QString, IBBData, Stanza::Kind))); + connect(d->ibb, SIGNAL(closeRequest(Jid, QString, QString)), SLOT(ibb_closeRequest(Jid, QString, QString))); } IBBManager::~IBBManager() @@ -388,28 +351,18 @@ IBBManager::~IBBManager() delete d; } -const char* IBBManager::ns() -{ - return IBB_NS; -} +const char *IBBManager::ns() { return IBB_NS; } -Client *IBBManager::client() const -{ - return d->client; -} +Client *IBBManager::client() const { return d->client; } -BSConnection *IBBManager::createConnection() -{ - return new IBBConnection(this); -} +BSConnection *IBBManager::createConnection() { return new IBBConnection(this); } IBBConnection *IBBManager::takeIncoming() { - return d->incomingConns.isEmpty()? 0 : d->incomingConns.takeFirst(); + return d->incomingConns.isEmpty() ? nullptr : d->incomingConns.takeFirst(); } -void IBBManager::ibb_incomingRequest(const Jid &from, const QString &id, - const QString &sid, int blockSize, +void IBBManager::ibb_incomingRequest(const Jid &from, const QString &id, const QString &sid, int blockSize, const QString &stanza) { // create a "waiting" connection @@ -419,17 +372,15 @@ void IBBManager::ibb_incomingRequest(const Jid &from, const QString &id, emit incomingReady(); } -void IBBManager::takeIncomingData(const Jid &from, const QString &id, - const IBBData &data, Stanza::Kind sKind) +void IBBManager::takeIncomingData(const Jid &from, const QString &id, const IBBData &data, Stanza::Kind sKind) { IBBConnection *c = findConnection(data.sid, from); - if(!c) { + if (!c) { if (sKind == Stanza::IQ) { d->ibb->respondError(from, id, Stanza::Error::ItemNotFound, "No such stream"); } // TODO imeplement xep-0079 error processing in case of Stanza::Message - } - else { + } else { if (sKind == Stanza::IQ) { d->ibb->respondAck(from, id); } @@ -437,99 +388,77 @@ void IBBManager::takeIncomingData(const Jid &from, const QString &id, } } -void IBBManager::ibb_closeRequest(const Jid &from, const QString &id, - const QString &sid) +void IBBManager::ibb_closeRequest(const Jid &from, const QString &id, const QString &sid) { IBBConnection *c = findConnection(sid, from); - if(!c) { + if (!c) { d->ibb->respondError(from, id, Stanza::Error::ItemNotFound, "No such stream"); - } - else { + } else { d->ibb->respondAck(from, id); c->setRemoteClosed(); } } -bool IBBManager::isAcceptableSID(const XMPP::Jid &jid, const QString&sid) const +bool IBBManager::isAcceptableSID(const XMPP::Jid &jid, const QString &sid) const { - return findConnection(sid, jid) == NULL; + return findConnection(sid, jid) == nullptr; } -const char* IBBManager::sidPrefix() const -{ - return "ibb_"; -} +const char *IBBManager::sidPrefix() const { return "ibb_"; } -void IBBManager::link(IBBConnection *c) -{ - d->activeConns.append(c); -} +void IBBManager::link(IBBConnection *c) { d->activeConns.append(c); } -void IBBManager::unlink(IBBConnection *c) -{ - d->activeConns.removeAll(c); -} +void IBBManager::unlink(IBBConnection *c) { d->activeConns.removeAll(c); } IBBConnection *IBBManager::findConnection(const QString &sid, const Jid &peer) const { - foreach(IBBConnection* c, d->activeConns) { - if(c->sid() == sid && (peer.isEmpty() || c->peer().compare(peer)) ) + for (IBBConnection *c : std::as_const(d->activeConns)) { + if (c->sid() == sid && (peer.isEmpty() || c->peer().compare(peer))) return c; } - return 0; + return nullptr; } -void IBBManager::doAccept(IBBConnection *c, const QString &id) -{ - d->ibb->respondAck(c->peer(), id); -} +void IBBManager::doAccept(IBBConnection *c, const QString &id) { d->ibb->respondAck(c->peer(), id); } -void IBBManager::doReject(IBBConnection *c, const QString &id, - Stanza::Error::ErrorCond cond, const QString &str) +void IBBManager::doReject(IBBConnection *c, const QString &id, Stanza::Error::ErrorCond cond, const QString &str) { d->ibb->respondError(c->peer(), id, cond, str); } - //---------------------------------------------------------------------------- // JT_IBB //---------------------------------------------------------------------------- -class JT_IBB::Private -{ +class JT_IBB::Private { public: Private() = default; QDomElement iq; - int mode = 0; - bool serve = false; - Jid to; - QString sid; - int bytesWritten = 0; + int mode = 0; + bool serve = false; + Jid to; + QString sid; + int bytesWritten = 0; }; -JT_IBB::JT_IBB(Task *parent, bool serve) -:Task(parent) +JT_IBB::JT_IBB(Task *parent, bool serve) : Task(parent) { - d = new Private; + d = new Private; d->serve = serve; } -JT_IBB::~JT_IBB() -{ - delete d; -} +JT_IBB::~JT_IBB() { delete d; } -void JT_IBB::request(const Jid &to, const QString &sid) +void JT_IBB::request(const Jid &to, const QString &sid, int blockSize) { d->mode = ModeRequest; QDomElement iq; - d->to = to; - iq = createIQ(doc(), "set", to.full(), id()); - QDomElement query = doc()->createElement("open"); - //genUniqueKey - query.setAttribute("xmlns", IBB_NS); + d->to = to; + iq = createIQ(doc(), "set", to.full(), id()); + QDomElement query = doc()->createElementNS(IBB_NS, "open"); + // genUniqueKey query.setAttribute("sid", sid); - query.setAttribute("block-size", IBB_PACKET_SIZE); + query.setAttribute("block-size", blockSize); query.setAttribute("stanza", "iq"); iq.appendChild(query); d->iq = iq; @@ -539,9 +468,9 @@ void JT_IBB::sendData(const Jid &to, const IBBData &ibbData) { d->mode = ModeSendData; QDomElement iq; - d->to = to; + d->to = to; d->bytesWritten = ibbData.data.size(); - iq = createIQ(doc(), "set", to.full(), id()); + iq = createIQ(doc(), "set", to.full(), id()); iq.appendChild(ibbData.toXml(doc())); d->iq = iq; } @@ -550,73 +479,61 @@ void JT_IBB::close(const Jid &to, const QString &sid) { d->mode = ModeSendData; QDomElement iq; - d->to = to; - iq = createIQ(doc(), "set", to.full(), id()); - QDomElement query = iq.appendChild(doc()->createElement("close")).toElement(); - query.setAttribute("xmlns", IBB_NS); + d->to = to; + iq = createIQ(doc(), "set", to.full(), id()); + QDomElement query = iq.appendChild(doc()->createElementNS(IBB_NS, "close")).toElement(); query.setAttribute("sid", sid); d->iq = iq; } -void JT_IBB::respondError(const Jid &to, const QString &id, - Stanza::Error::ErrorCond cond, const QString &text) +void JT_IBB::respondError(const Jid &to, const QString &id, Stanza::Error::ErrorCond cond, const QString &text) { - QDomElement iq = createIQ(doc(), "error", to.full(), id); + QDomElement iq = createIQ(doc(), "error", to.full(), id); Stanza::Error error(Stanza::Error::Cancel, cond, text); iq.appendChild(error.toXml(*client()->doc(), client()->stream().baseNS())); send(iq); } -void JT_IBB::respondAck(const Jid &to, const QString &id) -{ - send( createIQ(doc(), "result", to.full(), id) ); -} +void JT_IBB::respondAck(const Jid &to, const QString &id) { send(createIQ(doc(), "result", to.full(), id)); } -void JT_IBB::onGo() -{ - send(d->iq); -} +void JT_IBB::onGo() { send(d->iq); } bool JT_IBB::take(const QDomElement &e) { - if(d->serve) { + if (d->serve) { // must be an iq-set tag - if(e.tagName() != "iq" || e.attribute("type") != "set") + if (e.tagName() != "iq" || e.attribute("type") != "set") return false; - QString id = e.attribute("id"); - QString from = e.attribute("from"); + QString id = e.attribute("id"); + QString from = e.attribute("from"); QDomElement openEl = e.firstChildElement("open"); - if (!openEl.isNull() && openEl.attribute("xmlns") == IBB_NS) { - emit incomingRequest(Jid(from), id, - openEl.attribute("sid"), - openEl.attribute("block-size").toInt(), - openEl.attribute("stanza")); + if (!openEl.isNull() && openEl.namespaceURI() == IBB_NS) { + emit incomingRequest(Jid(from), id, openEl.attribute("sid"), openEl.attribute("block-size").toInt(), + openEl.attribute("stanza")); return true; } QDomElement dataEl = e.firstChildElement("data"); - if (!dataEl.isNull() && dataEl.attribute("xmlns") == IBB_NS) { + if (!dataEl.isNull() && dataEl.namespaceURI() == IBB_NS) { IBBData data; - emit incomingData(Jid(from), id, data.fromXml(dataEl), Stanza::IQ); + emit incomingData(Jid(from), id, data.fromXml(dataEl), Stanza::IQ); return true; } QDomElement closeEl = e.firstChildElement("close"); - if (!closeEl.isNull() && closeEl.attribute("xmlns") == IBB_NS) { + if (!closeEl.isNull() && closeEl.namespaceURI() == IBB_NS) { emit closeRequest(Jid(from), id, closeEl.attribute("sid")); return true; } return false; - } - else { + } else { Jid from(e.attribute("from")); - if(e.attribute("id") != id() || !d->to.compare(from)) + if (e.attribute("id") != id() || !d->to.compare(from)) return false; - if(e.attribute("type") == "result") { + if (e.attribute("type") == "result") { setSuccess(); - } - else { + } else { setError(e); } @@ -624,18 +541,8 @@ bool JT_IBB::take(const QDomElement &e) } } -Jid JT_IBB::jid() const -{ - return d->to; -} +Jid JT_IBB::jid() const { return d->to; } -int JT_IBB::mode() const -{ - return d->mode; -} - -int JT_IBB::bytesWritten() const -{ - return d->bytesWritten; -} +int JT_IBB::mode() const { return d->mode; } +int JT_IBB::bytesWritten() const { return d->bytesWritten; } diff --git a/src/xmpp/xmpp-im/xmpp_ibb.h b/src/xmpp/xmpp-im/xmpp_ibb.h index 0b70723d..6b475881 100644 --- a/src/xmpp/xmpp-im/xmpp_ibb.h +++ b/src/xmpp/xmpp-im/xmpp_ibb.h @@ -1,6 +1,6 @@ /* * ibb.h - Inband bytestream - * Copyright (C) 2001, 2002 Justin Karneges + * Copyright (C) 2001-2002 Justin Karneges * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -17,162 +17,145 @@ * */ -#ifndef JABBER_IBB_H -#define JABBER_IBB_H - -#include -#include -#include +#ifndef XMPP_IBB_H +#define XMPP_IBB_H #include "bytestream.h" #include "xmpp_bytestream.h" -#include "im.h" #include "xmpp_task.h" -namespace XMPP -{ - class Client; - class IBBManager; - - class IBBData - { - public: - IBBData() : seq(0) {} - IBBData(const QString &sid, quint16 seq, const QByteArray &data) - : sid(sid) - , seq(seq) - , data(data) - {} - - IBBData& fromXml(const QDomElement &e); - QDomElement toXml(QDomDocument *) const; - - QString sid; - quint16 seq; - QByteArray data; - }; - - // this is an IBB connection. use it much like a qsocket - class IBBConnection : public BSConnection - { - Q_OBJECT - public: - enum { ErrRequest, ErrData }; - enum { Idle, Requesting, WaitingForAccept, Active }; - IBBConnection(IBBManager *); - ~IBBConnection(); - - void connectToJid(const Jid &peer, const QString &sid); - void accept(); - void close(); - - int state() const; - Jid peer() const; - QString sid() const; - BytestreamManager* manager() const; - - bool isOpen() const; - - protected: - qint64 writeData(const char *data, qint64 maxSize); - - signals: - void connected(); - - private slots: - void ibb_finished(); - void trySend(); - - private: - class Private; - Private *d; - - void resetConnection(bool clear=false); - - friend class IBBManager; - void waitForAccept(const Jid &peer, const QString &iq_id, - const QString &sid, int blockSize, - const QString &stanza); - void takeIncomingData(const IBBData &ibbData); - void setRemoteClosed(); - }; - - typedef QList IBBConnectionList; - class IBBManager : public BytestreamManager - { - Q_OBJECT - public: - IBBManager(Client *); - ~IBBManager(); - - static const char* ns(); - Client *client() const; - - bool isAcceptableSID(const Jid &peer, const QString &sid) const; - BSConnection *createConnection(); - IBBConnection *takeIncoming(); - - public slots: - void takeIncomingData(const Jid &from, const QString &id, - const IBBData &data, Stanza::Kind); - - protected: - const char* sidPrefix() const; - - private slots: - void ibb_incomingRequest(const Jid &from, const QString &id, - const QString &sid, int blockSize, - const QString &stanza); - void ibb_closeRequest(const Jid &from, const QString &id, - const QString &sid); - - private: - class Private; - Private *d; - - friend class IBBConnection; - IBBConnection *findConnection(const QString &sid, const Jid &peer="") const; - void link(IBBConnection *); - void unlink(IBBConnection *); - void doAccept(IBBConnection *c, const QString &id); - void doReject(IBBConnection *c, const QString &id, - Stanza::Error::ErrorCond cond, const QString &); - }; - - class JT_IBB : public Task - { - Q_OBJECT - public: - enum { ModeRequest, ModeSendData }; - JT_IBB(Task *, bool serve=false); - ~JT_IBB(); - - void request(const Jid &, const QString &sid); - void sendData(const Jid &, const IBBData &ibbData); - void close(const Jid &, const QString &sid); - void respondError(const Jid &, const QString &id, - Stanza::Error::ErrorCond cond, const QString &text = ""); - void respondAck(const Jid &to, const QString &id); - - void onGo(); - bool take(const QDomElement &); - - Jid jid() const; - int mode() const; - int bytesWritten() const; - - signals: - void incomingRequest(const Jid &from, const QString &id, - const QString &sid, int blockSize, - const QString &stanza); - void incomingData(const Jid &from, const QString &id, - const IBBData &data, Stanza::Kind); - void closeRequest(const Jid &from, const QString &id, const QString &sid); +#include +#include +#include + +namespace XMPP { +class Client; +class IBBManager; + +class IBBData { +public: + IBBData() : seq(0) { } + IBBData(const QString &sid, quint16 seq, const QByteArray &data) : sid(sid), seq(seq), data(data) { } + + IBBData &fromXml(const QDomElement &e); + QDomElement toXml(QDomDocument *) const; + + QString sid; + quint16 seq; + QByteArray data; +}; + +// this is an IBB connection. use it much like a qsocket +class IBBConnection final : public BSConnection { + Q_OBJECT +public: + static const int PacketSize = 4096; + + enum { ErrRequest, ErrData }; + enum { Idle, Requesting, WaitingForAccept, Active }; + IBBConnection(IBBManager *); + ~IBBConnection(); - private: - class Private; - Private *d; - }; -} + void setPacketSize(int blockSize = IBBConnection::PacketSize); + void connectToJid(const Jid &peer, const QString &sid); + void accept(); + void close(); -#endif + int state() const; + Jid peer() const; + QString sid() const; + BytestreamManager *manager() const; + + bool isOpen() const; + +protected: + qint64 writeData(const char *data, qint64 maxSize); + +signals: + void connected(); + +private slots: + void ibb_finished(); + void trySend(); + +private: + class Private; + Private *d; + + void resetConnection(bool clear = false); + + friend class IBBManager; + void waitForAccept(const Jid &peer, const QString &iq_id, const QString &sid, int blockSize, const QString &stanza); + void takeIncomingData(const IBBData &ibbData); + void setRemoteClosed(); +}; + +typedef QList IBBConnectionList; +class IBBManager : public BytestreamManager { + Q_OBJECT +public: + IBBManager(Client *); + ~IBBManager(); + + static const char *ns(); + Client *client() const; + + bool isAcceptableSID(const Jid &peer, const QString &sid) const; + BSConnection *createConnection(); + IBBConnection *takeIncoming(); + +public slots: + void takeIncomingData(const Jid &from, const QString &id, const IBBData &data, Stanza::Kind); + +protected: + const char *sidPrefix() const; + +private slots: + void ibb_incomingRequest(const Jid &from, const QString &id, const QString &sid, int blockSize, + const QString &stanza); + void ibb_closeRequest(const Jid &from, const QString &id, const QString &sid); + +private: + class Private; + Private *d; + + friend class IBBConnection; + IBBConnection *findConnection(const QString &sid, const Jid &peer = "") const; + void link(IBBConnection *); + void unlink(IBBConnection *); + void doAccept(IBBConnection *c, const QString &id); + void doReject(IBBConnection *c, const QString &id, Stanza::Error::ErrorCond cond, const QString &); +}; + +class JT_IBB : public Task { + Q_OBJECT +public: + enum { ModeRequest, ModeSendData }; + JT_IBB(Task *, bool serve = false); + ~JT_IBB(); + + void request(const Jid &, const QString &sid, int blockSize = IBBConnection::PacketSize); + void sendData(const Jid &, const IBBData &ibbData); + void close(const Jid &, const QString &sid); + void respondError(const Jid &, const QString &id, Stanza::Error::ErrorCond cond, const QString &text = ""); + void respondAck(const Jid &to, const QString &id); + + void onGo(); + bool take(const QDomElement &); + + Jid jid() const; + int mode() const; + int bytesWritten() const; + +signals: + void incomingRequest(const Jid &from, const QString &id, const QString &sid, int blockSize, const QString &stanza); + void incomingData(const Jid &from, const QString &id, const IBBData &data, Stanza::Kind); + void closeRequest(const Jid &from, const QString &id, const QString &sid); + +private: + class Private; + Private *d; +}; +} // namespace XMPP + +#endif // XMPP_IBB_H diff --git a/src/xmpp/xmpp-im/xmpp_liveroster.h b/src/xmpp/xmpp-im/xmpp_liveroster.h index a4339b63..4c0fbb2a 100644 --- a/src/xmpp/xmpp-im/xmpp_liveroster.h +++ b/src/xmpp/xmpp-im/xmpp_liveroster.h @@ -19,24 +19,32 @@ #ifndef XMPP_LIVEROSTER_H #define XMPP_LIVEROSTER_H +#include "xmpp_liverosteritem.h" + #include -#include "xmpp_liverosteritem.h" +namespace XMPP { +class Jid; + +class LiveRoster : public QList { +public: + LiveRoster(); + LiveRoster(const LiveRoster &other); + ~LiveRoster(); + + LiveRoster &operator=(const LiveRoster &other); -namespace XMPP -{ - class Jid; + void flagAllForDelete(); + LiveRoster::Iterator find(const Jid &, bool compareRes = true); + LiveRoster::ConstIterator find(const Jid &, bool compareRes = true) const; - class LiveRoster : public QList - { - public: - LiveRoster(); - ~LiveRoster(); + void setGroupsDelimiter(const QString &groupsDelimiter); + QString groupsDelimiter() const; - void flagAllForDelete(); - LiveRoster::Iterator find(const Jid &, bool compareRes=true); - LiveRoster::ConstIterator find(const Jid &, bool compareRes=true) const; - }; -} +private: + class Private; + Private *d; +}; +} // namespace XMPP -#endif +#endif // XMPP_LIVEROSTER_H diff --git a/src/xmpp/xmpp-im/xmpp_liverosteritem.h b/src/xmpp/xmpp-im/xmpp_liverosteritem.h index b46d0664..b4c18397 100644 --- a/src/xmpp/xmpp-im/xmpp_liverosteritem.h +++ b/src/xmpp/xmpp-im/xmpp_liverosteritem.h @@ -19,39 +19,38 @@ #ifndef XMPP_LIVEROSTERITEM_H #define XMPP_LIVEROSTERITEM_H -#include "xmpp_status.h" #include "xmpp_resourcelist.h" #include "xmpp_rosteritem.h" +#include "xmpp_status.h" -namespace XMPP -{ - class LiveRosterItem : public RosterItem - { - public: - LiveRosterItem(const Jid &j=""); - LiveRosterItem(const RosterItem &); - ~LiveRosterItem(); +namespace XMPP { +class LiveRosterItem : public RosterItem { +public: + LiveRosterItem(const Jid &j = ""); + LiveRosterItem(const RosterItem &); + ~LiveRosterItem(); + LiveRosterItem &operator=(const LiveRosterItem &other) = default; - void setRosterItem(const RosterItem &); + void setRosterItem(const RosterItem &); - ResourceList & resourceList(); - ResourceList::Iterator priority(); + ResourceList &resourceList(); + ResourceList::Iterator priority(); - const ResourceList & resourceList() const; - ResourceList::ConstIterator priority() const; + const ResourceList &resourceList() const; + ResourceList::ConstIterator priority() const; - bool isAvailable() const; - const Status & lastUnavailableStatus() const; - bool flagForDelete() const; + bool isAvailable() const; + const Status &lastUnavailableStatus() const; + bool flagForDelete() const; - void setLastUnavailableStatus(const Status &); - void setFlagForDelete(bool); + void setLastUnavailableStatus(const Status &); + void setFlagForDelete(bool); - private: - ResourceList v_resourceList; - Status v_lastUnavailableStatus; - bool v_flagForDelete; - }; -} +private: + ResourceList v_resourceList; + Status v_lastUnavailableStatus; + bool v_flagForDelete; +}; +} // namespace XMPP -#endif +#endif // XMPP_LIVEROSTERITEM_H diff --git a/src/xmpp/xmpp-im/xmpp_message.h b/src/xmpp/xmpp-im/xmpp_message.h index 46023696..e062f834 100644 --- a/src/xmpp/xmpp-im/xmpp_message.h +++ b/src/xmpp/xmpp-im/xmpp_message.h @@ -19,219 +19,221 @@ #ifndef XMPP_MESSAGE_H #define XMPP_MESSAGE_H -#include "xmpp_stanza.h" -#include "xmpp_url.h" +#include "xmpp_address.h" #include "xmpp_chatstate.h" +#include "xmpp_muc.h" #include "xmpp_receipts.h" -#include "xmpp_address.h" +#include "xmpp_reference.h" #include "xmpp_rosterx.h" -#include "xmpp_muc.h" +#include "xmpp_stanza.h" +#include "xmpp_url.h" #include -class QString; class QDateTime; +class QString; namespace XMPP { - class Jid; - class PubSubItem; - class PubSubRetraction; - class HTMLElement; - class HttpAuthRequest; - class XData; - class BoBData; - class IBBData; - class Forwarding; - - typedef QMap StringMap; - - typedef enum { OfflineEvent, DeliveredEvent, DisplayedEvent, - ComposingEvent, CancelEvent } MsgEvent; - - class Message - { - public: - // XEP-0334 - enum ProcessingHint { - NoPermanentStore = 1, - NoStore = 2, - NoCopy = 4, - Store = 8 - }; - Q_DECLARE_FLAGS(ProcessingHints, ProcessingHint) - - struct StanzaId { - Jid by; - QString id; - }; - - Message(); - Message(const Jid &to); - Message(const Message &from); - Message & operator=(const Message &from); - ~Message(); - bool operator ==(const Message &from) const; - inline bool isNull() const { return d == nullptr; } - inline operator bool() const { return d != nullptr; } - - Jid to() const; - Jid from() const; - QString id() const; - QString type() const; - QString lang() const; - QString subject(const QString &lang=QString::null) const; - QString subject(const QLocale &lang) const; - StringMap subjectMap() const; - QString body(const QString &lang="") const; - QString body(const QLocale &lang) const; - QString thread() const; - Stanza::Error error() const; - - void setTo(const Jid &j); - void setFrom(const Jid &j); - void setId(const QString &s); - void setType(const QString &s); - void setLang(const QString &s); - void setSubject(const QString &s, const QString &lang=""); - void setBody(const QString &s, const QString &lang=""); - void setThread(const QString &s, bool send = false); - void setError(const Stanza::Error &err); - - // XEP-0060 - QString pubsubNode() const; - QList pubsubItems() const; - QList pubsubRetractions() const; - - // XEP-0091 - QDateTime timeStamp() const; - void setTimeStamp(const QDateTime &ts, bool send = false); - - // XEP-0071 - HTMLElement html(const QString &lang="") const; - void setHTML(const HTMLElement &s, const QString &lang=""); - bool containsHTML() const; - - // XEP-0066 - UrlList urlList() const; - void urlAdd(const Url &u); - void urlsClear(); - void setUrlList(const UrlList &list); - - // XEP-0022 - QString eventId() const; - void setEventId(const QString& id); - bool containsEvents() const; - bool containsEvent(MsgEvent e) const; - void addEvent(MsgEvent e); - - // XEP-0085 - ChatState chatState() const; - void setChatState(ChatState); - - // XEP-0184 - MessageReceipt messageReceipt() const; - void setMessageReceipt(MessageReceipt); - QString messageReceiptId() const; - void setMessageReceiptId(const QString &s); - - // XEP-0027 - QString xsigned() const; - void setXSigned(const QString &s); - QString xencrypted() const; - void setXEncrypted(const QString &s); - - // XEP-0033 - AddressList addresses() const; - AddressList findAddresses(Address::Type t) const; - void addAddress(const Address &a); - void clearAddresses(); - void setAddresses(const AddressList &list); - - // XEP-144 - RosterExchangeItems rosterExchangeItems() const; - void setRosterExchangeItems(const RosterExchangeItems&); - - // XEP-172 - void setNick(const QString&); - QString nick() const; - - // XEP-0070 - void setHttpAuthRequest(const HttpAuthRequest&); - HttpAuthRequest httpAuthRequest() const; - - // XEP-0004 - void setForm(const XData&); - XData getForm() const; - - // XEP-xxxx SXE - void setSxe(const QDomElement&); - QDomElement sxe() const; - - // XEP-0231 bits of binary - void addBoBData(const BoBData &); - QList bobDataList() const; - - // XEP-0047 ibb - IBBData ibbData() const; - - // XEP-0280 Message Carbons - Jid displayJid() const; - Message displayMessage() const; - void setCarbonsPrivate(bool enable); - bool carbonsPrivate() const; - - // XEP-0297 - void setForwarded(const Forwarding &frw); - // note, the next method has to be called only on not-null message - const Forwarding &forwarded() const; - - // XEP-0308 - QString replaceId() const; - void setReplaceId(const QString& id); - - // XEP-0334 - void setProcessingHints(const ProcessingHints &hints); - ProcessingHints processingHints() const; - - // MUC - void addMUCStatus(int); - QList getMUCStatuses() const; - void addMUCInvite(const MUCInvite&); - QList mucInvites() const; - void setMUCDecline(const MUCDecline&); - MUCDecline mucDecline() const; - QString mucPassword() const; - void setMUCPassword(const QString&); - bool hasMUCUser() const; - - // XEP-0359 - StanzaId stanzaId() const; - void setStanzaId(const StanzaId &id); - QString originId() const; - void setOriginId(const QString &id); - - // Obsolete invitation - QString invite() const; - void setInvite(const QString &s); - - // for compatibility. delete me later - bool spooled() const; - void setSpooled(bool); - bool wasEncrypted() const; - void setWasEncrypted(bool); - - Stanza toStanza(Stream *stream) const; - bool fromStanza(const Stanza &s); - bool fromStanza(const Stanza &s, int tzoffset); - bool fromStanza(const Stanza &s, bool useTimeZoneOffset, int timeZoneOffset); - - private: - class Private; - QExplicitlySharedDataPointer d; +class BoBData; +class Forwarding; +class HTMLElement; +class HttpAuthRequest; +class IBBData; +class Jid; +class PubSubItem; +class PubSubRetraction; +class XData; + +typedef QMap StringMap; + +typedef enum { OfflineEvent, DeliveredEvent, DisplayedEvent, ComposingEvent, CancelEvent } MsgEvent; + +class Message { +public: + // XEP-0334 + enum ProcessingHint { NoPermanentStore = 1, NoStore = 2, NoCopy = 4, Store = 8 }; + Q_DECLARE_FLAGS(ProcessingHints, ProcessingHint) + + struct StanzaId { + Jid by; + QString id; }; -} -Q_DECLARE_OPERATORS_FOR_FLAGS(XMPP::Message::ProcessingHints) + Message(); + Message(const Jid &to); + Message(const Message &from); + Message &operator=(const Message &from); + ~Message(); + bool operator==(const Message &from) const; + inline bool isNull() const { return d == nullptr; } + bool isPureSubject() const; + + Jid to() const; + Jid from() const; + QString id() const; + QString type() const; + QString lang() const; + QString subject(const QString &lang = {}) const; + QString subject(const QLocale &lang) const; + StringMap subjectMap() const; + QString body(const QString &lang = "") const; + QString body(const QLocale &lang) const; + QString thread() const; + Stanza::Error error() const; + + void setTo(const Jid &j); + void setFrom(const Jid &j); + void setId(const QString &s); + void setType(const QString &s); + void setLang(const QString &s); + void setSubject(const QString &s, const QString &lang = {}); + void setBody(const QString &s, const QString &lang = {}); + void setThread(const QString &s, bool send = false); + void setError(const Stanza::Error &err); + + // XEP-0060 + QString pubsubNode() const; + QList pubsubItems() const; + QList pubsubRetractions() const; + + // XEP-0091 + QDateTime timeStamp() const; + void setTimeStamp(const QDateTime &ts, bool send = false); + + // XEP-0071 + HTMLElement html(const QString &lang = {}) const; + void setHTML(const HTMLElement &s, const QString &lang = {}); + bool containsHTML() const; + + // XEP-0066 + UrlList urlList() const; + void urlAdd(const Url &u); + void urlsClear(); + void setUrlList(const UrlList &list); + + // XEP-0022 + QString eventId() const; + void setEventId(const QString &id); + bool containsEvents() const; + bool containsEvent(MsgEvent e) const; + void addEvent(MsgEvent e); + + // XEP-0085 + ChatState chatState() const; + void setChatState(ChatState); + + // XEP-0184 + MessageReceipt messageReceipt() const; + void setMessageReceipt(MessageReceipt); + QString messageReceiptId() const; + void setMessageReceiptId(const QString &s); + + // XEP-0027 + QString xsigned() const; + void setXSigned(const QString &s); + QString xencrypted() const; + void setXEncrypted(const QString &s); + + // XEP-0033 + AddressList addresses() const; + AddressList findAddresses(Address::Type t) const; + void addAddress(const Address &a); + void clearAddresses(); + void setAddresses(const AddressList &list); + + // XEP-144 + RosterExchangeItems rosterExchangeItems() const; + void setRosterExchangeItems(const RosterExchangeItems &); + + // XEP-172 + void setNick(const QString &); + QString nick() const; + + // XEP-0070 + void setHttpAuthRequest(const HttpAuthRequest &); + HttpAuthRequest httpAuthRequest() const; + + // XEP-0004 + void setForm(const XData &); + XData getForm() const; + + // XEP-xxxx SXE + void setSxe(const QDomElement &); + QDomElement sxe() const; + + // XEP-0231 bits of binary + void addBoBData(const BoBData &); + QList bobDataList() const; + + // XEP-0047 ibb + IBBData ibbData() const; + + // XEP-0280 Message Carbons + Jid displayJid() const; + Message displayMessage() const; + void setCarbonsPrivate(bool enable); + bool carbonsPrivate() const; + + // XEP-0297 + void setForwarded(const Forwarding &frw); + // note, the next method has to be called only on not-null message + const Forwarding &forwarded() const; + + // XEP-0308 + QString replaceId() const; + void setReplaceId(const QString &id); + + // XEP-0334 + void setProcessingHints(const ProcessingHints &hints); + ProcessingHints processingHints() const; + + // MUC + void addMUCStatus(int); + QList getMUCStatuses() const; + void addMUCInvite(const MUCInvite &); + QList mucInvites() const; + void setMUCDecline(const MUCDecline &); + MUCDecline mucDecline() const; + QString mucPassword() const; + void setMUCPassword(const QString &); + bool hasMUCUser() const; + + // XEP-0359 + StanzaId stanzaId() const; + void setStanzaId(const StanzaId &id); + QString originId() const; + void setOriginId(const QString &id); + + // XEP-0380 + QString encryptionProtocol() const; + void setEncryptionProtocol(const QString &protocol); + + // XEP-0385 and XEP-0372 + QList references() const; + void addReference(const Reference &r); + void setReferences(const QList &r); + + // Obsolete invitation + QString invite() const; + void setInvite(const QString &s); + + // for compatibility. delete me later + bool spooled() const; + void setSpooled(bool); + bool wasEncrypted() const; + void setWasEncrypted(bool); + + Stanza toStanza(Stream *stream) const; + bool fromStanza(const Stanza &s); + bool fromStanza(const Stanza &s, int tzoffset); + bool fromStanza(const Stanza &s, bool useTimeZoneOffset, int timeZoneOffset); + +private: + class Private; + QExplicitlySharedDataPointer d; +}; +} // namespace XMPP +Q_DECLARE_OPERATORS_FOR_FLAGS(XMPP::Message::ProcessingHints) -#endif +#endif // XMPP_MESSAGE_H diff --git a/src/xmpp/xmpp-im/xmpp_muc.h b/src/xmpp/xmpp-im/xmpp_muc.h index 17b3657e..ffd69e0d 100644 --- a/src/xmpp/xmpp-im/xmpp_muc.h +++ b/src/xmpp/xmpp-im/xmpp_muc.h @@ -20,117 +20,119 @@ #ifndef XMPP_MUC_H #define XMPP_MUC_H -#include -#include - #include "xmpp/jid/jid.h" -namespace XMPP -{ - class MUCItem - { - public: - enum Affiliation { UnknownAffiliation, Outcast, NoAffiliation, Member, Admin, Owner }; - enum Role { UnknownRole, NoRole, Visitor, Participant, Moderator }; - - MUCItem(Role = UnknownRole, Affiliation = UnknownAffiliation); - MUCItem(const QDomElement&); - - void setNick(const QString&); - void setJid(const Jid&); - void setAffiliation(Affiliation); - void setRole(Role); - void setActor(const Jid&); - void setReason(const QString&); - - const QString& nick() const; - const Jid& jid() const; // real jid of muc participant - Affiliation affiliation() const; - Role role() const; - const Jid& actor() const; - const QString& reason() const; - - void fromXml(const QDomElement&); - QDomElement toXml(QDomDocument&); - - bool operator==(const MUCItem& o); - - private: - QString nick_; - Jid jid_, actor_; - Affiliation affiliation_; - Role role_; - QString reason_; - }; - - class MUCInvite - { - public: - MUCInvite(); - MUCInvite(const QDomElement&); - MUCInvite(const Jid& to, const QString& reason = QString()); - - const Jid& to() const; - void setTo(const Jid&); - const Jid& from() const; - void setFrom(const Jid&); - const QString& reason() const; - void setReason(const QString&); - bool cont() const; - void setCont(bool); - - - void fromXml(const QDomElement&); - QDomElement toXml(QDomDocument&) const; - bool isNull() const; - - private: - Jid to_, from_; - QString reason_, password_; - bool cont_; - }; - - class MUCDecline - { - public: - MUCDecline(); - MUCDecline(const Jid& to, const QString& reason); - MUCDecline(const QDomElement&); - - const Jid& to() const; - void setTo(const Jid&); - const Jid& from() const; - void setFrom(const Jid&); - const QString& reason() const; - void setReason(const QString&); - - void fromXml(const QDomElement&); - QDomElement toXml(QDomDocument&) const; - bool isNull() const; - - private: - Jid to_, from_; - QString reason_; - }; - - class MUCDestroy - { - public: - MUCDestroy(); - MUCDestroy(const QDomElement&); +#include +#include - const Jid& jid() const; - void setJid(const Jid&); - const QString& reason() const; - void setReason(const QString&); +namespace XMPP { +class MUCItem { +public: + enum Affiliation { UnknownAffiliation, Outcast, NoAffiliation, Member, Admin, Owner }; + enum Role { UnknownRole, NoRole, Visitor, Participant, Moderator }; - void fromXml(const QDomElement&); - QDomElement toXml(QDomDocument&) const; + struct Actor { + Jid jid; + QString nick; - private: - Jid jid_; - QString reason_; + bool operator==(const Actor &o) const; }; -} -#endif + MUCItem(Role = UnknownRole, Affiliation = UnknownAffiliation); + MUCItem(const QDomElement &); + + void setNick(const QString &); + void setJid(const Jid &); + void setAffiliation(Affiliation); + void setRole(Role); + void setActor(const Actor &); + void setReason(const QString &); + + const QString &nick() const; + const Jid &jid() const; // real jid of muc participant + Affiliation affiliation() const; + Role role() const; + const Actor &actor() const; + const QString &reason() const; + + void fromXml(const QDomElement &); + QDomElement toXml(QDomDocument &); + + bool operator==(const MUCItem &o) const; + +private: + QString nick_; + Jid jid_; + Actor actor_; + Affiliation affiliation_; + Role role_; + QString reason_; +}; + +class MUCInvite { +public: + MUCInvite(); + MUCInvite(const QDomElement &); + MUCInvite(const Jid &to, const QString &reason = QString()); + + const Jid &to() const; + void setTo(const Jid &); + const Jid &from() const; + void setFrom(const Jid &); + const QString &reason() const; + void setReason(const QString &); + bool cont() const; + void setCont(bool); + + void fromXml(const QDomElement &); + QDomElement toXml(QDomDocument &) const; + bool isNull() const; + +private: + Jid to_, from_; + QString reason_, password_; + bool cont_; +}; + +class MUCDecline { +public: + MUCDecline(); + MUCDecline(const Jid &to, const QString &reason); + MUCDecline(const QDomElement &); + + const Jid &to() const; + void setTo(const Jid &); + const Jid &from() const; + void setFrom(const Jid &); + const QString &reason() const; + void setReason(const QString &); + + void fromXml(const QDomElement &); + QDomElement toXml(QDomDocument &) const; + bool isNull() const; + +private: + Jid to_, from_; + QString reason_; +}; + +class MUCDestroy { +public: + MUCDestroy(); + MUCDestroy(const QDomElement &); + + const Jid &jid() const; + void setJid(const Jid &); + const QString &reason() const; + void setReason(const QString &); + + void fromXml(const QDomElement &); + QDomElement toXml(QDomDocument &) const; + +private: + Jid jid_; + QString reason_; +}; +} // namespace XMPP + +#endif // XMPP_MUC_H diff --git a/src/xmpp/xmpp-im/xmpp_pubsubitem.h b/src/xmpp/xmpp-im/xmpp_pubsubitem.h index 2820c36e..0c0b283e 100644 --- a/src/xmpp/xmpp-im/xmpp_pubsubitem.h +++ b/src/xmpp/xmpp-im/xmpp_pubsubitem.h @@ -19,23 +19,21 @@ #ifndef XMPP_PUBSUBITEM_H #define XMPP_PUBSUBITEM_H -#include #include +#include -namespace XMPP -{ - class PubSubItem - { - public: - PubSubItem(); - PubSubItem(const QString& id, const QDomElement& payload); - const QString& id() const; - const QDomElement& payload() const; +namespace XMPP { +class PubSubItem { +public: + PubSubItem(); + PubSubItem(const QString &id, const QDomElement &payload); + const QString &id() const; + const QDomElement &payload() const; - private: - QString id_; - QDomElement payload_; - }; -} +private: + QString id_; + QDomElement payload_; +}; +} // namespace XMPP -#endif +#endif // XMPP_PUBSUBITEM_H diff --git a/src/xmpp/xmpp-im/xmpp_pubsubretraction.h b/src/xmpp/xmpp-im/xmpp_pubsubretraction.h index 2ece653e..1a2023a5 100644 --- a/src/xmpp/xmpp-im/xmpp_pubsubretraction.h +++ b/src/xmpp/xmpp-im/xmpp_pubsubretraction.h @@ -19,22 +19,19 @@ #ifndef XMPP_PUBSUBRETRACTION_H #define XMPP_PUBSUBRETRACTION_H -#include #include +#include -namespace XMPP -{ - - class PubSubRetraction - { - public: - PubSubRetraction(); - PubSubRetraction(const QString& id); - const QString& id() const; +namespace XMPP { +class PubSubRetraction { +public: + PubSubRetraction(); + PubSubRetraction(const QString &id); + const QString &id() const; - private: - QString id_; - }; -} +private: + QString id_; +}; +} // namespace XMPP -#endif +#endif // XMPP_PUBSUBRETRACTION_H diff --git a/src/xmpp/xmpp-im/xmpp_receipts.h b/src/xmpp/xmpp-im/xmpp_receipts.h index 8468837b..3220e292 100644 --- a/src/xmpp/xmpp-im/xmpp_receipts.h +++ b/src/xmpp/xmpp-im/xmpp_receipts.h @@ -21,11 +21,7 @@ #define XMPP_RECEIPTS_H namespace XMPP { - typedef enum { - ReceiptNone, - ReceiptRequest, - ReceiptReceived - } MessageReceipt; -} +typedef enum { ReceiptNone, ReceiptRequest, ReceiptReceived } MessageReceipt; +} // namespace XMPP -#endif +#endif // XMPP_RECEIPTS_H diff --git a/src/xmpp/xmpp-im/xmpp_reference.cpp b/src/xmpp/xmpp-im/xmpp_reference.cpp new file mode 100644 index 00000000..935ccc5b --- /dev/null +++ b/src/xmpp/xmpp-im/xmpp_reference.cpp @@ -0,0 +1,181 @@ +/* + * xmpp_reference.h - XMPP References / XEP-0372 + * Copyright (C) 2019 Sergey Ilinykh + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library. If not, see . + * + */ + +#include "xmpp_reference.h" + +using namespace XMPP; + +#define D() (d ? d : (d = new Private)) + +const QString XMPP::MEDIASHARING_NS(QStringLiteral("urn:xmpp:sims:1")); +const QString XMPP::REFERENCE_NS(QStringLiteral("urn:xmpp:reference:0")); + +class Reference::Private : public QSharedData { +public: + Reference::Type type; + QString uri; + QString anchor; + int begin = -1; + int end = -1; + MediaSharing mediaSharing; +}; + +Reference::Reference() { } + +Reference::Reference(Type type, const QString &uri) : d(new Private) +{ + d->type = type; + d->uri = uri; +} + +Reference::~Reference() { } + +Reference::Reference(const Reference &other) : d(other.d) { } + +Reference &Reference::operator=(const Reference &other) +{ + d = other.d; + return *this; +} + +Reference::Type Reference::type() const { return d->type; } + +const QString &Reference::uri() const { return d->uri; } + +void Reference::setRange(int begin, int end) +{ + D()->begin = begin; + d->end = end; +} + +int Reference::begin() const { return d->begin; } + +int Reference::end() const { return d->end; } + +const QString &Reference::anchor() const { return d->anchor; } + +void Reference::setAnchor(const QString &anchor) { D()->anchor = anchor; } + +void Reference::setMediaSharing(const MediaSharing &ms) { D()->mediaSharing = ms; } + +const MediaSharing &Reference::mediaSharing() const { return d->mediaSharing; } + +bool Reference::fromXml(const QDomElement &e) +{ + QString type = e.attribute(QString::fromLatin1("type")); + QString uri = e.attribute(QString::fromLatin1("uri")); + QString begin = e.attribute(QString::fromLatin1("begin")); + QString end = e.attribute(QString::fromLatin1("end")); + QString anchor = e.attribute(QString::fromLatin1("anchor")); + + if (type.isEmpty() || uri.isEmpty()) { + return false; + } + + Type t; + if (type == QString::fromLatin1("data")) + t = Data; + else if (type == QString::fromLatin1("mention")) + t = Mention; + else + return false; + + int beginN = -1, endN = -1; + bool ok; + if (!begin.isEmpty() && !(beginN = begin.toInt(&ok), ok)) + return false; + + if (!end.isEmpty() && !(endN = end.toInt(&ok), ok)) + return false; + + if (beginN >= 0 && endN >= 0 && endN < beginN) + return false; + + if ((endN >= 0 && beginN == -1) || (endN == -1 && beginN >= 0)) + return false; + + auto msEl = e.firstChildElement("media-sharing"); + MediaSharing ms; + if (msEl.namespaceURI() == MEDIASHARING_NS) { + auto fileEl = msEl.firstChildElement("file"); + auto sourcesEl = msEl.firstChildElement("sources"); + if (sourcesEl.isNull() || fileEl.isNull() || fileEl.namespaceURI() != XMPP::Jingle::FileTransfer::NS) + return false; + + ms.file = XMPP::Jingle::FileTransfer::File(fileEl); + if (!ms.file.isValid() || !ms.file.hasComputedHashes()) + return false; + + auto srcName = QString::fromLatin1("reference"); + for (auto el = sourcesEl.firstChildElement(srcName); !el.isNull(); el = el.nextSiblingElement(srcName)) { + if (el.namespaceURI() == REFERENCE_NS) { + Reference ref; + if (!ref.fromXml(el)) { + return false; + } + ms.sources.append(ref.uri()); + } + } + if (ms.sources.isEmpty()) + return false; + } + + D()->type = t; + d->uri = uri; + d->begin = beginN; + d->end = endN; + d->anchor = anchor; + d->mediaSharing = ms; + + return true; +} + +QDomElement Reference::toXml(QDomDocument *doc) const +{ + if (!d) { + return QDomElement(); + } + auto root = doc->createElementNS(REFERENCE_NS, QString::fromLatin1("reference")); + root.setAttribute(QString::fromLatin1("uri"), d->uri); + root.setAttribute(QString::fromLatin1("type"), QString(d->type == Reference::Mention ? "mention" : "data")); + + if (d->mediaSharing.file.isValid() && d->mediaSharing.sources.count()) { + auto msEl = doc->createElementNS(MEDIASHARING_NS, QString::fromLatin1("media-sharing")); + root.appendChild(msEl); + msEl.appendChild(d->mediaSharing.file.toXml(doc)); + auto sourcesEl = msEl.appendChild(doc->createElement(QString::fromLatin1("sources"))).toElement(); + for (auto const &s : d->mediaSharing.sources) { + auto sEl = sourcesEl.appendChild(doc->createElementNS(REFERENCE_NS, QString::fromLatin1("reference"))) + .toElement(); + sEl.setAttribute(QString::fromLatin1("uri"), s); + sEl.setAttribute(QString::fromLatin1("type"), QString::fromLatin1("data")); + } + } + + if (d->begin != -1) + root.setAttribute(QString::fromLatin1("begin"), d->begin); + + if (d->end != -1) + root.setAttribute(QString::fromLatin1("end"), d->end); + + if (!d->anchor.isEmpty()) + root.setAttribute(QString::fromLatin1("anchor"), d->anchor); + + return root; +} diff --git a/src/xmpp/xmpp-im/xmpp_reference.h b/src/xmpp/xmpp-im/xmpp_reference.h new file mode 100644 index 00000000..8a3aaace --- /dev/null +++ b/src/xmpp/xmpp-im/xmpp_reference.h @@ -0,0 +1,77 @@ +/* + * xmpp_reference.h - XMPP References / XEP-0372 + * Copyright (C) 2019 Sergey Ilinykh + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library. If not, see . + * + */ + +#ifndef XMPPREFERENCE_H +#define XMPPREFERENCE_H + +#include "jingle-ft.h" + +#include +#include + +namespace XMPP { +extern const QString MEDIASHARING_NS; +extern const QString REFERENCE_NS; + +class MediaSharing { +public: + Jingle::FileTransfer::File file; + QStringList sources; + + inline bool isValid() const { return file.isValid(); } +}; + +class Reference { +public: + enum Type : char { + + Mention, + Data + }; + + Reference(); + Reference(Type type, const QString &uri); + ~Reference(); + Reference(const Reference &other); + Reference &operator=(const Reference &other); + + bool isValid() const { return d != nullptr; } + Type type() const; + const QString &uri() const; + + void setRange(int begin, int end); + int begin() const; + int end() const; + + const QString &anchor() const; + void setAnchor(const QString &anchor); + + void setMediaSharing(const MediaSharing &); + const MediaSharing &mediaSharing() const; + + bool fromXml(const QDomElement &e); + QDomElement toXml(QDomDocument *) const; + +private: + class Private; + QSharedDataPointer d; +}; +} // namespace XMPP + +#endif // XMPPREFERENCE_H diff --git a/src/xmpp/xmpp-im/xmpp_resource.h b/src/xmpp/xmpp-im/xmpp_resource.h index 1c9f0b6e..cdbe105f 100644 --- a/src/xmpp/xmpp-im/xmpp_resource.h +++ b/src/xmpp/xmpp-im/xmpp_resource.h @@ -19,28 +19,26 @@ #ifndef XMPP_RESOURCE_H #define XMPP_RESOURCE_H -#include - #include "xmpp_status.h" -namespace XMPP -{ - class Resource - { - public: - Resource(const QString &name="", const Status &s=Status()); +#include + +namespace XMPP { +class Resource { +public: + Resource(const QString &name = "", const Status &s = Status()); - const QString & name() const; - int priority() const; - const Status & status() const; + const QString &name() const; + int priority() const; + const Status &status() const; - void setName(const QString &); - void setStatus(const Status &); + void setName(const QString &); + void setStatus(const Status &); - private: - QString v_name; - Status v_status; - }; -} +private: + QString v_name; + Status v_status; +}; +} // namespace XMPP -#endif +#endif // XMPP_RESOURCE_H diff --git a/src/xmpp/xmpp-im/xmpp_resourcelist.h b/src/xmpp/xmpp-im/xmpp_resourcelist.h index 75c4c4a3..93003581 100644 --- a/src/xmpp/xmpp-im/xmpp_resourcelist.h +++ b/src/xmpp/xmpp-im/xmpp_resourcelist.h @@ -19,26 +19,24 @@ #ifndef XMPP_RESOURCELIST_H #define XMPP_RESOURCELIST_H -#include - #include "xmpp_resource.h" +#include + class QString; -namespace XMPP -{ - class ResourceList : public QList - { - public: - ResourceList(); - ~ResourceList(); +namespace XMPP { +class ResourceList : public QList { +public: + ResourceList(); + ~ResourceList(); - ResourceList::Iterator find(const QString &); - ResourceList::Iterator priority(); + ResourceList::Iterator find(const QString &); + ResourceList::Iterator priority(); - ResourceList::ConstIterator find(const QString &) const; - ResourceList::ConstIterator priority() const; - }; -} + ResourceList::ConstIterator find(const QString &) const; + ResourceList::ConstIterator priority() const; +}; +} // namespace XMPP -#endif +#endif // XMPP_RESOURCELIST_H diff --git a/src/xmpp/xmpp-im/xmpp_roster.h b/src/xmpp/xmpp-im/xmpp_roster.h index 2e134686..4b0bb93a 100644 --- a/src/xmpp/xmpp-im/xmpp_roster.h +++ b/src/xmpp/xmpp-im/xmpp_roster.h @@ -19,29 +19,34 @@ #ifndef XMPP_ROSTER_H #define XMPP_ROSTER_H -#include - #include "xmpp_rosteritem.h" +#include + class QDomDocument; class QDomElement; -namespace XMPP -{ - class Jid; - class Roster : public QList - { - public: - Roster(); - ~Roster(); - - Roster::Iterator find(const Jid &); - Roster::ConstIterator find(const Jid &) const; - - private: - class RosterPrivate; - RosterPrivate *d = nullptr; - }; -} - -#endif +namespace XMPP { +class Jid; + +class Roster : public QList { +public: + Roster(); + Roster(const Roster &other); + ~Roster(); + + Roster &operator=(const Roster &other); + + Roster::Iterator find(const Jid &); + Roster::ConstIterator find(const Jid &) const; + + void setGroupsDelimiter(const QString &groupsDelimiter); + QString groupsDelimiter() const; + +private: + class Private; + Private *d = nullptr; +}; +} // namespace XMPP + +#endif // XMPP_ROSTER_H diff --git a/src/xmpp/xmpp-im/xmpp_rosteritem.h b/src/xmpp/xmpp-im/xmpp_rosteritem.h index 5ed23f6f..13c7f11f 100644 --- a/src/xmpp/xmpp-im/xmpp_rosteritem.h +++ b/src/xmpp/xmpp-im/xmpp_rosteritem.h @@ -19,64 +19,62 @@ #ifndef XMPP_ROSTERITEM_H #define XMPP_ROSTERITEM_H +#include "xmpp/jid/jid.h" + #include #include -#include "xmpp/jid/jid.h" - -namespace XMPP -{ - class Subscription - { - public: - enum SubType { None, To, From, Both, Remove }; +namespace XMPP { +class Subscription { +public: + enum SubType { None, To, From, Both, Remove }; - Subscription(SubType type=None); + Subscription(SubType type = None); - int type() const; + int type() const; - QString toString() const; - bool fromString(const QString &); + QString toString() const; + bool fromString(const QString &); - private: - SubType value; - }; +private: + SubType value; +}; - class RosterItem - { - public: - RosterItem(const Jid &jid=""); - RosterItem(const RosterItem &item); - virtual ~RosterItem(); +class RosterItem { +public: + RosterItem(const Jid &jid = ""); + RosterItem(const RosterItem &item); + virtual ~RosterItem(); + RosterItem &operator=(const RosterItem &other) = default; - const Jid & jid() const; - const QString & name() const; - const QStringList & groups() const; - const Subscription & subscription() const; - const QString & ask() const; - bool isPush() const; - bool inGroup(const QString &) const; + const Jid &jid() const; + const QString &name() const; + const QStringList &groups() const; + const Subscription &subscription() const; + const QString &ask() const; + bool isPush() const; + bool inGroup(const QString &) const; - virtual void setJid(const Jid &); - void setName(const QString &); - void setGroups(const QStringList &); - void setSubscription(const Subscription &); - void setAsk(const QString &); - void setIsPush(bool); - bool addGroup(const QString &); - bool removeGroup(const QString &); + virtual void setJid(const Jid &); + void setName(const QString &); + void setGroups(const QStringList &); + void setSubscription(const Subscription &); + void setAsk(const QString &); + void setIsPush(bool); + bool addGroup(const QString &); + bool removeGroup(const QString &); - QDomElement toXml(QDomDocument *) const; - bool fromXml(const QDomElement &); + QDomElement toXml(QDomDocument *) const; + bool fromXml(const QDomElement &); - private: - Jid v_jid; - QString v_name; - QStringList v_groups; - Subscription v_subscription; - QString v_ask; - bool v_push; - }; -} +private: + Jid v_jid; + QString v_name; + QStringList v_groups; + Subscription v_subscription; + QString v_ask; + bool v_push; +}; +} // namespace XMPP -#endif +#endif // XMPP_ROSTERITEM_H diff --git a/src/xmpp/xmpp-im/xmpp_rosterx.h b/src/xmpp/xmpp-im/xmpp_rosterx.h index b4d52d15..41bbfc9f 100644 --- a/src/xmpp/xmpp-im/xmpp_rosterx.h +++ b/src/xmpp/xmpp-im/xmpp_rosterx.h @@ -20,46 +20,45 @@ #ifndef XMPP_ROSTERX_H #define XMPP_ROSTERX_H +#include "xmpp/jid/jid.h" + #include #include -#include "xmpp/jid/jid.h" - class QDomElement; -namespace XMPP -{ - class Stanza; +namespace XMPP { +class Stanza; - class RosterExchangeItem - { - public: - enum Action { Add, Delete, Modify }; +class RosterExchangeItem { +public: + enum Action { Add, Delete, Modify }; - RosterExchangeItem(const Jid& jid, const QString& name = "", const QStringList& groups = QStringList(), Action = Add); - RosterExchangeItem(const QDomElement&); + RosterExchangeItem(const Jid &jid, const QString &name = "", const QStringList &groups = QStringList(), + Action = Add); + RosterExchangeItem(const QDomElement &); - const Jid& jid() const; - Action action() const; - const QString& name() const; - const QStringList& groups() const; - bool isNull() const; + const Jid &jid() const; + Action action() const; + const QString &name() const; + const QStringList &groups() const; + bool isNull() const; - void setJid(const Jid&); - void setAction(Action); - void setName(const QString&); - void setGroups(const QStringList&); + void setJid(const Jid &); + void setAction(Action); + void setName(const QString &); + void setGroups(const QStringList &); - QDomElement toXml(Stanza&) const; - void fromXml(const QDomElement&); + QDomElement toXml(Stanza &) const; + void fromXml(const QDomElement &); - private: - Jid jid_; - QString name_; - QStringList groups_; - Action action_; - }; - typedef QList RosterExchangeItems; -} +private: + Jid jid_; + QString name_; + QStringList groups_; + Action action_; +}; +typedef QList RosterExchangeItems; +} // namespace XMPP -#endif +#endif // XMPP_ROSTERX_H diff --git a/src/xmpp/xmpp-im/xmpp_serverinfomanager.cpp b/src/xmpp/xmpp-im/xmpp_serverinfomanager.cpp index f942beda..5db2e3af 100644 --- a/src/xmpp/xmpp-im/xmpp_serverinfomanager.cpp +++ b/src/xmpp/xmpp-im/xmpp_serverinfomanager.cpp @@ -1,6 +1,6 @@ /* * xmpp_serverinfomanager.cpp - * Copyright (C) 2006,2019 Remko Troncon, Sergey Ilinykh + * Copyright (C) 2006-2019 Remko Troncon, Sergey Ilinykh * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -18,19 +18,18 @@ */ #include "xmpp_serverinfomanager.h" -#include "xmpp_tasks.h" + #include "xmpp_caps.h" +#include "xmpp_client.h" +#include "xmpp_tasks.h" namespace XMPP { - -ServerInfoManager::ServerInfoManager(Client* client): - QObject(client), - _client(client), - _canMessageCarbons(false) +ServerInfoManager::ServerInfoManager(Client *client) : QObject(client), _client(client), _canMessageCarbons(false) { deinitialize(); // NOTE we can use this class for any server, but for this we shouldn't use roster signal here - connect(_client, SIGNAL(rosterRequestFinished(bool, int, const QString &)), SLOT(initialize()), Qt::QueuedConnection); + connect(_client, SIGNAL(rosterRequestFinished(bool, int, const QString &)), SLOT(initialize()), + Qt::QueuedConnection); } void ServerInfoManager::reset() @@ -59,39 +58,31 @@ void ServerInfoManager::deinitialize() emit featuresChanged(); } -const QString& ServerInfoManager::multicastService() const -{ - return _multicastService; -} +const QString &ServerInfoManager::multicastService() const { return _multicastService; } -bool ServerInfoManager::hasPEP() const -{ - return _hasPEP; -} +bool ServerInfoManager::hasPEP() const { return _hasPEP; } -bool ServerInfoManager::canMessageCarbons() const -{ - return _canMessageCarbons; -} +bool ServerInfoManager::canMessageCarbons() const { return _canMessageCarbons; } void ServerInfoManager::queryServicesList() { _servicesListState = ST_InProgress; - auto jtitems = new JT_DiscoItems(_client->rootTask()); - connect(jtitems, &JT_DiscoItems::finished, this, [=]() - { - _servicesInfo.clear(); // - if (jtitems->success()) { - _servicesListState = ST_Ready; - for (const auto &item: jtitems->items()) { - _servicesInfo.insert(item.jid().full(), {ST_NotQueried, item, QMap()}); + auto jtitems = new JT_DiscoItems(_client->rootTask()); + connect( + jtitems, &JT_DiscoItems::finished, this, + [=]() { + _servicesInfo.clear(); // + if (jtitems->success()) { + _servicesListState = ST_Ready; + for (const auto &item : jtitems->items()) { + _servicesInfo.insert(item.jid().full(), { ST_NotQueried, item, QMap() }); + } + } else { + _servicesListState = ST_Failed; } - } - else { - _servicesListState = ST_Failed; - } - checkPendingServiceQueries(); - }, Qt::QueuedConnection); + checkPendingServiceQueries(); + }, + Qt::QueuedConnection); jtitems->get(_client->jid().domain()); jtitems->go(true); } @@ -103,7 +94,7 @@ void ServerInfoManager::checkPendingServiceQueries() if (_servicesListState == ST_Failed) { const auto sqs = _serviceQueries; _serviceQueries.clear(); - for (const auto &q: sqs) { + for (const auto &q : sqs) { q.callback(QList()); } } @@ -112,30 +103,30 @@ void ServerInfoManager::checkPendingServiceQueries() // services list is ready here and we can start checking it and sending disco#info to not cached entries auto sqIt = _serviceQueries.begin(); - while(sqIt != _serviceQueries.end()) { + while (sqIt != _serviceQueries.end()) { // populate services to query for this service request if (!sqIt->servicesToQueryDefined) { sqIt->spareServicesToQuery.clear(); // grep all suitble service jids. moving forward preferred ones - QMapIterator si(_servicesInfo); + QMapIterator si(_servicesInfo); while (si.hasNext()) { si.next(); - if (!sqIt->nameHint.isEmpty()) { - if (sqIt->nameHint.isEmpty() || sqIt->nameHint.exactMatch(si.key())) { - sqIt->servicesToQuery.append(si.key()); + if (sqIt->nameHint.isValid()) { + if (!sqIt->nameHint.isValid() || sqIt->nameHint.match(si.key()).hasMatch()) { + sqIt->servicesToQuery.push_back(si.key()); } else if (sqIt->options & SQ_CheckAllOnNoMatch) { - sqIt->spareServicesToQuery.append(si.key()); + sqIt->spareServicesToQuery.push_back(si.key()); } } else { - sqIt->servicesToQuery.append(si.key()); + sqIt->servicesToQuery.push_back(si.key()); } } - if (sqIt->servicesToQuery.isEmpty()) { + if (sqIt->servicesToQuery.empty()) { sqIt->servicesToQuery = sqIt->spareServicesToQuery; sqIt->spareServicesToQuery.clear(); } - if (sqIt->servicesToQuery.isEmpty()) { + if (sqIt->servicesToQuery.empty()) { sqIt->callback(QList()); _serviceQueries.erase(sqIt++); continue; @@ -145,33 +136,31 @@ void ServerInfoManager::checkPendingServiceQueries() // now `sqIt->servicesToQuery` definitely has something to check. maybe some info is already in cache. bool hasInProgress = false; - auto jidIt = sqIt->servicesToQuery.begin(); - //bool foundMatch = false; + auto jidIt = sqIt->servicesToQuery.begin(); + // bool foundMatch = false; while (jidIt != sqIt->servicesToQuery.end()) { auto si = _servicesInfo.find(*jidIt); // find cached service corresponding to one of matched jids if (si == _servicesInfo.end() || si->state == ST_Failed) { // the map was updated after the first service list request, or info request failed. sqIt->servicesToQuery.erase(jidIt++); continue; - } else - if (si->state == ST_Ready) { // disco info finished successfully for current jid from `servicesToQuery` + } else if (si->state + == ST_Ready) { // disco info finished successfully for current jid from `servicesToQuery` bool foundIdentity = sqIt->category.isEmpty() && sqIt->type.isEmpty(); if (!foundIdentity) { - for (auto &i: si->item.identities()) { - if ((sqIt->category.isEmpty() || sqIt->category == i.category) && - (sqIt->type.isEmpty() || sqIt->type == i.type)) - { + for (auto &i : si->item.identities()) { + if ((sqIt->category.isEmpty() || sqIt->category == i.category) + && (sqIt->type.isEmpty() || sqIt->type == i.type)) { foundIdentity = true; break; } } } - if (foundIdentity && (sqIt->features.isEmpty() || std::accumulate(sqIt->features.constBegin(), - sqIt->features.constEnd(), false, - [&si](bool a, const QSet &b) { - return a || si->item.features().test(b); - }))) - { + if (foundIdentity + && (sqIt->features.isEmpty() + || std::accumulate( + sqIt->features.constBegin(), sqIt->features.constEnd(), false, + [&si](bool a, const QSet &b) { return a || si->item.features().test(b); }))) { sqIt->result.append(si->item); if (sqIt->options & SQ_FinishOnFirstMatch) { break; @@ -185,14 +174,14 @@ void ServerInfoManager::checkPendingServiceQueries() Q_ASSERT(si->state == ST_NotQueried || si->state == ST_InProgress); hasInProgress = true; if (si->state == ST_NotQueried) { // if not queried then let's query - si->state = ST_InProgress; + si->state = ST_InProgress; auto jtinfo = new JT_DiscoInfo(_client->rootTask()); - connect(jtinfo, &DiscoInfoTask::finished, this, [this, jtinfo](){ + connect(jtinfo, &DiscoInfoTask::finished, this, [this, jtinfo]() { auto si = _servicesInfo.find(jtinfo->jid().full()); if (si != _servicesInfo.end()) { if (jtinfo->success()) { si.value().state = ST_Ready; - si.value().item = jtinfo->item(); + si.value().item = jtinfo->item(); } else { si.value().state = ST_Failed; } @@ -205,8 +194,9 @@ void ServerInfoManager::checkPendingServiceQueries() ++jidIt; } - if (sqIt->result.isEmpty() && !hasInProgress && !sqIt->spareServicesToQuery.isEmpty()) { - // we don't check sqIt->servicesToQuery.isEmpty() since it comes from other conditions (sqIt->result.isEmpty() && !hasInProgress) + if (sqIt->result.isEmpty() && !hasInProgress && !sqIt->spareServicesToQuery.empty()) { + // we don't check sqIt->servicesToQuery.isEmpty() since it comes from other conditions + // (sqIt->result.isEmpty() && !hasInProgress) sqIt->servicesToQuery = sqIt->spareServicesToQuery; sqIt->spareServicesToQuery.clear(); continue; // continue with the same ServiceQuery but with different jids list @@ -217,7 +207,7 @@ void ServerInfoManager::checkPendingServiceQueries() // if nothing in progress then we have full result set or nothing found even in spare list if (forceFinish || !hasInProgress) { // self explanatory auto callback = std::move(sqIt->callback); - auto result = sqIt->result; + auto result = sqIt->result; _serviceQueries.erase(sqIt++); callback(result); } else { @@ -239,8 +229,9 @@ void ServerInfoManager::appendQuery(const ServiceQuery &q) } } -void ServerInfoManager::queryServiceInfo(const QString &category, const QString &type, const QList> &features, - const QRegExp &nameHint, SQOptions options, std::function &items)> callback) +void ServerInfoManager::queryServiceInfo(const QString &category, const QString &type, + const QList> &features, const QRegularExpression &nameHint, + SQOptions options, std::function &items)> callback) { appendQuery(ServiceQuery(type, category, features, nameHint, options, std::move(callback))); } @@ -275,17 +266,17 @@ void ServerInfoManager::disco_finished() // Identities DiscoItem::Identities is = jt->item().identities(); - foreach(DiscoItem::Identity i, is) { + for (const DiscoItem::Identity &i : is) { if (i.category == "pubsub" && i.type == "pep") _hasPEP = true; } - for (const auto &x: jt->item().extensions()) { - if (x.type() == XData::Data_Result && x.registrarType() == QLatin1String("http://jabber.org/network/serverinfo")) { - for (const auto &f: x.fields()) { - if (f.type() == XData::Field::Field_ListMulti) { - _extraServerInfo.insert(f.var(), f.value()); // covers XEP-0157 - } + auto servInfo + = jt->item().findExtension(XData::Data_Result, QLatin1String("http://jabber.org/network/serverinfo")); + if (servInfo.isValid()) { + for (const auto &f : servInfo.fields()) { + if (f.type() == XData::Field::Field_ListMulti) { + _extraServerInfo.insert(f.var(), f.value()); // covers XEP-0157 } } } @@ -293,5 +284,4 @@ void ServerInfoManager::disco_finished() emit featuresChanged(); } } - } // namespace XMPP diff --git a/src/xmpp/xmpp-im/xmpp_serverinfomanager.h b/src/xmpp/xmpp-im/xmpp_serverinfomanager.h index b5957c14..8a6a795a 100644 --- a/src/xmpp/xmpp-im/xmpp_serverinfomanager.h +++ b/src/xmpp/xmpp-im/xmpp_serverinfomanager.h @@ -20,24 +20,23 @@ #ifndef SERVERINFOMANAGER_H #define SERVERINFOMANAGER_H -#include "xmpp_caps.h" #include "xmpp_discoitem.h" +#include "xmpp_status.h" #include +#include #include -#include #include #include +#include namespace XMPP { - class Client; class Features; class Jid; -class ServerInfoManager : public QObject -{ +class ServerInfoManager : public QObject { Q_OBJECT public: enum SQOption { @@ -49,51 +48,41 @@ class ServerInfoManager : public QObject private: struct ServiceQuery { - const QString type; - const QString category; - const QList> features; - const QRegExp nameHint; - const SQOptions options; + const QString type; + const QString category; + const QList> features; + const QRegularExpression nameHint; + const SQOptions options; const std::function &item)> callback; - QLinkedList servicesToQuery; - QLinkedList spareServicesToQuery; // usually a fallback when the above is not matched - bool servicesToQueryDefined = false; - QList result; - - ServiceQuery(const QString &type, - const QString &category, - const QList> &features, - const QRegExp &nameHint, - const SQOptions &options, - const std::function &item)> &&callback - ) : + std::list servicesToQuery; + std::list spareServicesToQuery; // usually a fallback when the above is not matched + bool servicesToQueryDefined = false; + QList result; + + ServiceQuery(const QString &type, const QString &category, const QList> &features, + const QRegularExpression &nameHint, const SQOptions &options, + const std::function &item)> &&callback) : type(type), category(category), features(features), nameHint(nameHint), options(options), callback(callback) - { } + { + } }; - enum ServicesState { - ST_NotQueried, - ST_InProgress, - ST_Ready, - ST_Failed - }; + enum ServicesState { ST_NotQueried, ST_InProgress, ST_Ready, ST_Failed }; struct ServiceInfo { - ServicesState state; - DiscoItem item; - QMap meta; + ServicesState state; + DiscoItem item; + QMap meta; }; - public: + ServerInfoManager(XMPP::Client *client); - ServerInfoManager(XMPP::Client* client); - - const QString& multicastService() const; - bool hasPEP() const; - inline const Features &features() const { return _features; } - bool canMessageCarbons() const; - inline const QMap &extraServerInfo() const { return _extraServerInfo; } + const QString &multicastService() const; + bool hasPEP() const; + inline const Features &features() const { return _features; } + bool canMessageCarbons() const; + inline const QMap &extraServerInfo() const { return _extraServerInfo; } /* empty type/category/features/nameHint means it won't be checked. @@ -112,10 +101,10 @@ class ServerInfoManager : public QObject nameHint = (http\..*|) // search for service name like http.jabber.ru Result: disco info for upload.jabber.ru will be returned. */ - void queryServiceInfo(const QString &category, const QString &type, const QList> &features, - const QRegExp &nameHint, SQOptions options, - std::function &items)> callback); - void setServiceMeta(const Jid &service, const QString &key, const QVariant &value); + void queryServiceInfo(const QString &category, const QString &type, const QList> &features, + const QRegularExpression &nameHint, SQOptions options, + std::function &items)> callback); + void setServiceMeta(const Jid &service, const QString &key, const QVariant &value); QVariant serviceMeta(const Jid &service, const QString &key); signals: @@ -134,21 +123,21 @@ private slots: void appendQuery(const ServiceQuery &q); private: - XMPP::Client* _client = nullptr; - CapsSpec _caps; - Features _features; - QString _multicastService; - QMap _extraServerInfo; // XEP-0128, XEP-0157 + XMPP::Client *_client = nullptr; + CapsSpec _caps; + Features _features; + QString _multicastService; + QMap _extraServerInfo; // XEP-0128, XEP-0157 std::list _serviceQueries; // a storage of pending requests as result of `queryService` call - ServicesState _servicesListState = ST_NotQueried; - QMap _servicesInfo; // all the diso#info requests for services of this server jid=>(state,info) + ServicesState _servicesListState = ST_NotQueried; + QMap + _servicesInfo; // all the diso#info requests for services of this server jid=>(state,info) bool _featuresRequested; bool _hasPEP; bool _canMessageCarbons; }; - } // namespace XMPP -#endif +#endif // SERVERINFOMANAGER_H diff --git a/src/xmpp/xmpp-im/xmpp_status.h b/src/xmpp/xmpp-im/xmpp_status.h index e78af9ab..17c649cb 100644 --- a/src/xmpp/xmpp-im/xmpp_status.h +++ b/src/xmpp/xmpp-im/xmpp_status.h @@ -20,135 +20,131 @@ #ifndef XMPP_STATUS_H #define XMPP_STATUS_H +#include "xmpp_bitsofbinary.h" +#include "xmpp_muc.h" + +#include +#include #include #include #include -#include -#include - -#include "xmpp_muc.h" -#include "xmpp_bitsofbinary.h" -namespace XMPP -{ - class DiscoItem; - class CapsSpec - { - public: - typedef QMap CryptoMap; - static const QCryptographicHash::Algorithm invalidAlgo = (QCryptographicHash::Algorithm)255; - - CapsSpec(); - CapsSpec(const QString& node, QCryptographicHash::Algorithm hashAlgo, const QString& ver = QString::null); - CapsSpec(const DiscoItem &disco, QCryptographicHash::Algorithm hashAlgo = QCryptographicHash::Sha1); - - bool isValid() const; - const QString& node() const; - const QString& version() const; - QCryptographicHash::Algorithm hashAlgorithm() const; - inline const QStringList &ext() const { return ext_; } - QString flatten() const; - - void resetVersion(); - - bool operator==(const CapsSpec&) const; - bool operator!=(const CapsSpec&) const; - bool operator<(const CapsSpec&) const; - - QDomElement toXml(QDomDocument *doc) const; - static CapsSpec fromXml(const QDomElement &e); - - static CryptoMap &cryptoMap(); - - private: - QString node_, ver_; - QCryptographicHash::Algorithm hashAlgo_; - QStringList ext_; - }; - - class StatusPrivate; - - class Status - { - public: - enum Type { Offline, Online, Away, XA, DND, Invisible, FFC }; - - Status(const QString &show=QString(), const QString &status=QString(), int priority=0, bool available=true); - Status(Type type, const QString& status=QString(), int priority=0); - Status(const Status &); - Status &operator=(const Status &); - ~Status(); - - int priority() const; - Type type() const; - QString typeString() const; - const QString & show() const; - const QString & status() const; - QDateTime timeStamp() const; - const QString & keyID() const; - bool isAvailable() const; - bool isAway() const; - bool isInvisible() const; - bool hasError() const; - int errorCode() const; - const QString & errorString() const; - - const QString & xsigned() const; - const QString & songTitle() const; - const CapsSpec & caps() const; - - bool isMUC() const; - bool hasMUCItem() const; - const MUCItem & mucItem() const; - bool hasMUCDestroy() const; - const MUCDestroy & mucDestroy() const; - const QList& getMUCStatuses() const; - const QString& mucPassword() const; - bool hasMUCHistory() const; - int mucHistoryMaxChars() const; - int mucHistoryMaxStanzas() const; - int mucHistorySeconds() const; - const QDateTime & mucHistorySince() const; - - static Type txt2type(const QString& stat); - - void setPriority(int); - void setType(Type); - void setType(const QString &); - void setShow(const QString &); - void setStatus(const QString &); - void setTimeStamp(const QDateTime &); - void setKeyID(const QString &); - void setIsAvailable(bool); - void setIsInvisible(bool); - void setError(int, const QString &); - void setCaps(const CapsSpec&); - - void setMUC(); - void setMUCItem(const MUCItem&); - void setMUCDestroy(const MUCDestroy&); - void addMUCStatus(int); - void setMUCPassword(const QString&); - void setMUCHistory(int maxchars, int maxstanzas, int seconds, const QDateTime &since); - - void setXSigned(const QString &); - void setSongTitle(const QString &); - - // XEP-153: VCard-based Avatars - const QString& photoHash() const; - void setPhotoHash(const QString&); - bool hasPhotoHash() const; - - // XEP-0231 bits of binary - void addBoBData(const BoBData &); - QList bobDataList() const; - - private: - QSharedDataPointer d; - }; - -} +namespace XMPP { +class DiscoItem; +class StatusPrivate; + +class CapsSpec { +public: + typedef QMap CryptoMap; + static const QCryptographicHash::Algorithm invalidAlgo = (QCryptographicHash::Algorithm)255; + + CapsSpec(); + CapsSpec(const QString &node, QCryptographicHash::Algorithm hashAlgo, const QString &ver = QString()); + CapsSpec(const DiscoItem &disco, QCryptographicHash::Algorithm hashAlgo = QCryptographicHash::Sha1); + + bool isValid() const; + const QString &node() const; + const QString &version() const; + QCryptographicHash::Algorithm hashAlgorithm() const; + inline const QStringList &ext() const { return ext_; } + QString flatten() const; + + void resetVersion(); + + bool operator==(const CapsSpec &) const; + bool operator!=(const CapsSpec &) const; + bool operator<(const CapsSpec &) const; + + QDomElement toXml(QDomDocument *doc) const; + static CapsSpec fromXml(const QDomElement &e); + + static CryptoMap &cryptoMap(); + +private: + QString node_, ver_; + QCryptographicHash::Algorithm hashAlgo_; + QStringList ext_; +}; + +class Status { +public: + enum Type { Offline, Online, Away, XA, DND, Invisible, FFC }; + + Status(const QString &show = QString(), const QString &status = QString(), int priority = 0, bool available = true); + Status(Type type, const QString &status = QString(), int priority = 0); + Status(const Status &); + Status &operator=(const Status &); + ~Status(); + + int priority() const; + Type type() const; + QString typeString() const; + const QString &show() const; + const QString &status() const; + QDateTime timeStamp() const; + const QString &keyID() const; + bool isAvailable() const; + bool isAway() const; + bool isInvisible() const; + bool hasError() const; + int errorCode() const; + const QString &errorString() const; + + const QString &xsigned() const; + const QString &songTitle() const; + const CapsSpec &caps() const; + + bool isMUC() const; + bool hasMUCItem() const; + const MUCItem &mucItem() const; + bool hasMUCDestroy() const; + const MUCDestroy &mucDestroy() const; + const QList &getMUCStatuses() const; + const QString &mucPassword() const; + bool hasMUCHistory() const; + int mucHistoryMaxChars() const; + int mucHistoryMaxStanzas() const; + int mucHistorySeconds() const; + const QDateTime &mucHistorySince() const; + + static Type txt2type(const QString &stat); + + void setPriority(int); + void setType(Type); + void setType(const QString &); + void setShow(const QString &); + void setStatus(const QString &); + void setTimeStamp(const QDateTime &); + void setKeyID(const QString &); + void setIsAvailable(bool); + void setIsInvisible(bool); + void setError(int, const QString &); + void setCaps(const CapsSpec &); + + void setMUC(); + void setMUCItem(const MUCItem &); + void setMUCDestroy(const MUCDestroy &); + void addMUCStatus(int); + void setMUCPassword(const QString &); + void setMUCHistory(int maxchars, int maxstanzas, int seconds, const QDateTime &since); + + void setXSigned(const QString &); + void setSongTitle(const QString &); + + // XEP-0153: vCard-Based Avatars + const QByteArray &photoHash() const; + void setPhotoHash(const QByteArray &); + bool hasPhotoHash() const; + + // XEP-0231 bits of binary + void addBoBData(const BoBData &); + QList bobDataList() const; + +private: + QSharedDataPointer d; +}; +} // namespace XMPP Q_DECLARE_METATYPE(XMPP::Status) -#endif +#endif // XMPP_STATUS_H diff --git a/src/xmpp/xmpp-im/xmpp_subsets.cpp b/src/xmpp/xmpp-im/xmpp_subsets.cpp index 014ddccc..5925bbab 100644 --- a/src/xmpp/xmpp-im/xmpp_subsets.cpp +++ b/src/xmpp/xmpp-im/xmpp_subsets.cpp @@ -18,27 +18,27 @@ */ #include "xmpp_subsets.h" + #include "xmpp_xmlcommon.h" using namespace XMPP; static QLatin1String xmlns_ns_rsm("http://jabber.org/protocol/rsm"); -class SubsetsClientManager::Private -{ +class SubsetsClientManager::Private { public: enum QueryType { None, Count, First, Last, Next, Previous, Index }; struct { QueryType type; - int max; - int index; + int max; + int index; } query; struct { - int count; - int index; - bool first; - bool last; - int itemsCount; + int count; + int index; + bool first; + bool last; + int itemsCount; QString firstId; QString lastId; } result; @@ -46,18 +46,17 @@ class SubsetsClientManager::Private void resetResult() { - result.count = -1; - result.index = -1; - result.first = false; - result.last = false; + result.count = -1; + result.index = -1; + result.first = false; + result.last = false; result.itemsCount = 0; - valid = false; + valid = false; } QDomElement mainElement(QDomDocument *doc) { - QDomElement e = doc->createElement(QStringLiteral("set")); - e.setAttribute(QStringLiteral("xmlns"), xmlns_ns_rsm); + QDomElement e = doc->createElementNS(xmlns_ns_rsm, QStringLiteral("set")); return e; } @@ -86,16 +85,16 @@ class SubsetsClientManager::Private bool updateFromElement(const QDomElement &el) { - valid = true; - bool ok = false; - QDomElement e = el.firstChildElement(QLatin1String("count")); + valid = true; + bool ok = false; + QDomElement e = el.firstChildElement(QLatin1String("count")); if (!e.isNull()) result.count = tagContent(e).toInt(&ok); if (!ok || result.count < 0) result.count = -1; result.index = -1; - e = el.firstChildElement(QLatin1String("first")); + e = el.firstChildElement(QLatin1String("first")); if (!e.isNull()) { result.firstId = tagContent(e); if (result.firstId.isEmpty()) @@ -103,8 +102,7 @@ class SubsetsClientManager::Private int i = e.attribute(QLatin1String("index")).toInt(&ok); if (ok && i >= 0) result.index = i; - } - else + } else result.firstId = ""; e = el.firstChildElement(QLatin1String("last")); @@ -112,18 +110,17 @@ class SubsetsClientManager::Private result.lastId = tagContent(e); if (result.lastId.isEmpty()) valid = false; - } - else + } else result.lastId = ""; if (result.firstId.isEmpty() != result.lastId.isEmpty()) valid = false; - result.first = query.type == First || result.index == 0 || - (result.itemsCount == 0 && result.index == -1 && (query.type == Last || query.type == Previous)); - result.last = query.type == Last || - (result.index != -1 && result.count != -1 && result.count - result.itemsCount <= result.index) || - (result.itemsCount == 0 && result.index == -1 && (query.type == First || query.type == Next)); + result.first = query.type == First || result.index == 0 + || (result.itemsCount == 0 && result.index == -1 && (query.type == Last || query.type == Previous)); + result.last = query.type == Last + || (result.index != -1 && result.count != -1 && result.count - result.itemsCount <= result.index) + || (result.itemsCount == 0 && result.index == -1 && (query.type == First || query.type == Next)); if (result.firstId.isEmpty() && result.lastId.isEmpty()) { switch (query.type) { case Previous: @@ -147,55 +144,37 @@ SubsetsClientManager::SubsetsClientManager() reset(); } -SubsetsClientManager::~SubsetsClientManager() -{ - delete d; -} +SubsetsClientManager::~SubsetsClientManager() { delete d; } void SubsetsClientManager::reset() { d->query.type = Private::None; d->query.max = 50; d->query.index = -1; - d->result.firstId = QString::null; - d->result.lastId = QString::null; + d->result.firstId = QString(); + d->result.lastId = QString(); d->resetResult(); } -bool SubsetsClientManager::isValid() const -{ - return d->valid; -} +bool SubsetsClientManager::isValid() const { return d->valid; } -bool SubsetsClientManager::isFirst() const -{ - return d->result.first; -} +bool SubsetsClientManager::isFirst() const { return d->result.first; } -bool SubsetsClientManager::isLast() const -{ - return d->result.last; -} +bool SubsetsClientManager::isLast() const { return d->result.last; } -int SubsetsClientManager::count() const -{ - return d->result.count; -} +int SubsetsClientManager::count() const { return d->result.count; } -void SubsetsClientManager::setMax(int max) -{ - d->query.max = max; -} +void SubsetsClientManager::setMax(int max) { d->query.max = max; } QDomElement SubsetsClientManager::findElement(const QDomElement &el, bool child) { - if (el.tagName() == QLatin1String("set") && el.attribute(QLatin1String("xmlns")) == xmlns_ns_rsm) + if (el.tagName() == QLatin1String("set") && el.namespaceURI() == xmlns_ns_rsm) return el; if (child) { QDomElement e = el.firstChildElement(QLatin1String("set")); while (!e.isNull()) { - if (e.attribute(QLatin1String("xmlns")) == xmlns_ns_rsm) { + if (e.namespaceURI() == xmlns_ns_rsm) { return e; } e = e.nextSiblingElement(QLatin1String("set")); @@ -260,7 +239,7 @@ QDomElement SubsetsClientManager::makeQueryElement(QDomDocument *doc) const d->insertMaxElement(doc, &e, 0); break; case Private::Last: - d->insertBeforeElement(doc, &e, QString::null); + d->insertBeforeElement(doc, &e, QString()); break; case Private::Next: d->insertAfterElement(doc, &e, d->result.lastId); diff --git a/src/xmpp/xmpp-im/xmpp_subsets.h b/src/xmpp/xmpp-im/xmpp_subsets.h index 51b75387..4e6bbd24 100644 --- a/src/xmpp/xmpp-im/xmpp_subsets.h +++ b/src/xmpp/xmpp-im/xmpp_subsets.h @@ -22,36 +22,34 @@ #include -namespace XMPP -{ - class SubsetsClientManager - { - public: - SubsetsClientManager(); - ~SubsetsClientManager(); - - void reset(); - bool isValid() const; - bool isFirst() const; - bool isLast() const; - int count() const; - void setMax(int max); - - void getCount(); - void getFirst(); - void getNext(); - void getLast(); - void getPrevious(); - void getByIndex(); - - static QDomElement findElement(const QDomElement &el, bool child); - bool updateFromElement(const QDomElement &el, int itemsCount); - QDomElement makeQueryElement(QDomDocument *doc) const; - - private: - class Private; - Private *d; - }; -} - -#endif +namespace XMPP { +class SubsetsClientManager { +public: + SubsetsClientManager(); + ~SubsetsClientManager(); + + void reset(); + bool isValid() const; + bool isFirst() const; + bool isLast() const; + int count() const; + void setMax(int max); + + void getCount(); + void getFirst(); + void getNext(); + void getLast(); + void getPrevious(); + void getByIndex(); + + static QDomElement findElement(const QDomElement &el, bool child); + bool updateFromElement(const QDomElement &el, int itemsCount); + QDomElement makeQueryElement(QDomDocument *doc) const; + +private: + class Private; + Private *d; +}; +} // namespace XMPP + +#endif // XMPP_SUBSETS_H diff --git a/src/xmpp/xmpp-im/xmpp_task.cpp b/src/xmpp/xmpp-im/xmpp_task.cpp index d72cc875..bdd7c2db 100644 --- a/src/xmpp/xmpp-im/xmpp_task.cpp +++ b/src/xmpp/xmpp-im/xmpp_task.cpp @@ -16,47 +16,45 @@ * */ -#include - #include "xmpp_task.h" + #include "xmpp_client.h" -#include "xmpp_xmlcommon.h" #include "xmpp_stanza.h" +#include "xmpp_xmlcommon.h" + +#include #define DEFAULT_TIMEOUT 120 using namespace XMPP; -class Task::TaskPrivate -{ +class Task::TaskPrivate { public: TaskPrivate() = default; - QString id; - bool success = false; - int statusCode = 0; - QString statusString; + QString id; + bool success = false; + int statusCode = 0; + QString statusString; XMPP::Stanza::Error error; - Client *client = nullptr; - bool insig = false; - bool deleteme = false; - bool autoDelete = false; - bool done = false; - int timeout = 0; + Client *client = nullptr; + bool insig = false; + bool deleteme = false; + bool autoDelete = false; + bool done = false; + int timeout = 0; }; -Task::Task(Task *parent) -:QObject(parent) +Task::Task(Task *parent) : QObject(parent) { init(); d->client = parent->client(); - d->id = client()->genUniqueId(); + d->id = client()->genUniqueId(); connect(d->client, SIGNAL(disconnected()), SLOT(clientDisconnected())); } -Task::Task(Client *parent, bool) -:QObject(nullptr) +Task::Task(Client *parent, bool) : QObject(nullptr) { init(); @@ -64,71 +62,38 @@ Task::Task(Client *parent, bool) connect(d->client, SIGNAL(disconnected()), SLOT(clientDisconnected())); } -Task::~Task() -{ - delete d; -} +Task::~Task() { delete d; } void Task::init() { - d = new TaskPrivate; - d->success = false; - d->insig = false; - d->deleteme = false; + d = new TaskPrivate; + d->success = false; + d->insig = false; + d->deleteme = false; d->autoDelete = false; - d->done = false; - d->timeout = DEFAULT_TIMEOUT; + d->done = false; + d->timeout = DEFAULT_TIMEOUT; } -Task *Task::parent() const -{ - return (Task *)QObject::parent(); -} +Task *Task::parent() const { return (Task *)QObject::parent(); } -Client *Task::client() const -{ - return d->client; -} +Client *Task::client() const { return d->client; } -QDomDocument *Task::doc() const -{ - return client()->doc(); -} +QDomDocument *Task::doc() const { return client()->doc(); } -QString Task::id() const -{ - return d->id; -} +QString Task::id() const { return d->id; } -bool Task::success() const -{ - return d->success; -} +bool Task::success() const { return d->success; } -int Task::statusCode() const -{ - return d->statusCode; -} +int Task::statusCode() const { return d->statusCode; } -const QString & Task::statusString() const -{ - return d->statusString; -} +const QString &Task::statusString() const { return d->statusString; } -const Stanza::Error &Task::error() const -{ - return d->error; -} +const Stanza::Error &Task::error() const { return d->error; } -void Task::setTimeout(int seconds) const -{ - d->timeout = seconds; -} +void Task::setTimeout(int seconds) const { d->timeout = seconds; } -int Task::timeout() -{ - return d->timeout; -} +int Task::timeout() { return d->timeout; } void Task::go(bool autoDelete) { @@ -139,8 +104,7 @@ void Task::go(bool autoDelete) if (autoDelete) { deleteLater(); } - } - else { + } else { onGo(); if (d->timeout) { QTimer::singleShot(d->timeout * 1000, this, SLOT(timeoutFinished())); @@ -154,13 +118,12 @@ bool Task::take(const QDomElement &x) // pass along the xml Task *t; - for(QObjectList::ConstIterator it = p.begin(); it != p.end(); ++it) { - QObject *obj = *it; - if(!obj->inherits("XMPP::Task")) + for (QObject *obj : p) { + if (!obj->inherits("XMPP::Task")) continue; - t = static_cast(obj); - if(t->take(x)) // don't check for done here. it will hurt server tasks + t = static_cast(obj); + if (t->take(x)) // don't check for done here. it will hurt server tasks return true; } @@ -169,23 +132,21 @@ bool Task::take(const QDomElement &x) void Task::safeDelete() { - if(d->deleteme) + if (d->deleteme) return; d->deleteme = true; - if(!d->insig) + if (!d->insig) deleteLater(); } -void Task::onGo() -{ -} +void Task::onGo() { } void Task::onDisconnect() { - if(!d->done) { - d->success = false; - d->statusCode = ErrDisc; + if (!d->done) { + d->success = false; + d->statusCode = ErrDisc; d->statusString = tr("Disconnected"); // delay this so that tasks that react don't block the shutdown @@ -197,24 +158,21 @@ void Task::onDisconnect() void Task::onTimeout() { - if(!d->done) { - d->success = false; - d->statusCode = ErrTimeout; + if (!d->done) { + d->success = false; + d->statusCode = ErrTimeout; d->statusString = tr("Request timed out"); done(); } } -void Task::send(const QDomElement &x) -{ - client()->send(x); -} +void Task::send(const QDomElement &x) { client()->send(x); } void Task::setSuccess(int code, const QString &str) { - if(!d->done) { - d->success = true; - d->statusCode = code; + if (!d->done) { + d->success = true; + d->statusCode = code; d->statusString = str; done(); } @@ -222,17 +180,17 @@ void Task::setSuccess(int code, const QString &str) void Task::setError(const QDomElement &e) { - if(!d->done) { + if (!d->done) { d->success = false; QDomElement tag = e.firstChildElement("error"); - if(tag.isNull()) + if (tag.isNull()) return; XMPP::Stanza::Error err; err.fromXml(tag, d->client->streamBaseNS()); - d->error = err; - d->statusCode = err.code(); + d->error = err; + d->statusCode = err.code(); d->statusString = err.toString(); done(); } @@ -240,9 +198,9 @@ void Task::setError(const QDomElement &e) void Task::setError(int code, const QString &str) { - if(!d->done) { - d->success = false; - d->statusCode = code; + if (!d->done) { + d->success = false; + d->statusCode = code; d->statusString = str; done(); } @@ -250,25 +208,22 @@ void Task::setError(int code, const QString &str) void Task::done() { - if(d->done || d->insig) + if (d->done || d->insig) return; d->done = true; - if(d->autoDelete) + if (d->autoDelete) d->deleteme = true; d->insig = true; emit finished(); d->insig = false; - if(d->deleteme) + if (d->deleteme) deleteLater(); } -void Task::clientDisconnected() -{ - onDisconnect(); -} +void Task::clientDisconnected() { onDisconnect(); } void Task::timeoutFinished() { @@ -280,17 +235,12 @@ void Task::debug(const char *fmt, ...) { va_list ap; va_start(ap, fmt); - QString str; - str.vsprintf(fmt, ap); + QString str = QString::vasprintf(fmt, ap); va_end(ap); debug(str); } -void Task::debug(const QString &str) -{ - client()->debug(QString("%1: ").arg(metaObject()->className()) + str); -} - +void Task::debug(const QString &str) { client()->debug(QString("%1: ").arg(metaObject()->className()) + str); } /** * \brief verifiys a stanza is a IQ reply for this task @@ -304,45 +254,68 @@ void Task::debug(const QString &str) * \param id the id of the send IQ * \param xmlns the expected namespace if the reply (if non empty) * \return true if it's a valid reply -*/ + */ bool Task::iqVerify(const QDomElement &x, const Jid &to, const QString &id, const QString &xmlns) { - if(x.tagName() != QStringLiteral("iq")) + if (x.tagName() != QStringLiteral("iq")) return false; Jid from(x.attribute(QStringLiteral("from"))); - Jid local = client()->jid(); + Jid local = client()->jid(); Jid server = client()->host(); // empty 'from' ? - if(from.isEmpty()) { + if (from.isEmpty()) { // allowed if we are querying the server - if(!to.isEmpty() && !to.compare(server)) + if (!to.isEmpty() && !to.compare(server)) return false; } // from ourself? - else if(from.compare(local, false) || from.compare(local.domain(),false)) { + else if (from.compare(local, false) || from.compare(local.domain(), false)) { // allowed if we are querying ourself or the server - if(!to.isEmpty() && !to.compare(local, false) && !to.compare(server)) + if (!to.isEmpty() && !to.compare(local, false) && !to.compare(server)) return false; } // from anywhere else? else { - if(!from.compare(to)) + if (!from.compare(to)) return false; } - if(!id.isEmpty()) { - if(x.attribute(QStringLiteral("id")) != id) + if (!id.isEmpty()) { + if (x.attribute(QStringLiteral("id")) != id) return false; } - if(!xmlns.isEmpty()) { - if(queryNS(x) != xmlns) + if (!xmlns.isEmpty()) { + if (queryNS(x) != xmlns) return false; } return true; } +QString Task::encryptionProtocol(const QDomElement &e) const +{ + if (e.elementsByTagNameNS("urn:xmpp:eme:0", "encryption").isEmpty()) + return QString(); + + QDomElement encryption = e.elementsByTagNameNS("urn:xmpp:eme:0", "encryption").at(0).toElement(); + const QString &&ns = encryption.attribute("namespace"); + + // https://xmpp.org/extensions/xep-0380.html#protocols + QString protocol; + if (ns == "urn:xmpp:otr:0") { + protocol = "OTR"; + } else if (ns == "jabber:x:encrypted") { + protocol = "Legacy OpenPGP"; + } else if (ns == "urn:xmpp:openpgp:0") { + protocol = "OpenPGP for XMPP"; + } else if (ns == "eu.siacs.conversations.axolotl") { + protocol = "OMEMO"; + } else if (encryption.attribute("name").isEmpty()) { + protocol = encryption.attribute("name"); + } + return protocol; +} diff --git a/src/xmpp/xmpp-im/xmpp_task.h b/src/xmpp/xmpp-im/xmpp_task.h index 91daae14..15e2d260 100644 --- a/src/xmpp/xmpp-im/xmpp_task.h +++ b/src/xmpp/xmpp-im/xmpp_task.h @@ -20,7 +20,7 @@ #ifndef XMPP_TASK_H #define XMPP_TASK_H -#include "xmpp_stanza.h" +#include "iris/xmpp_stanza.h" #include #include @@ -29,61 +29,61 @@ class QDomDocument; class QDomElement; namespace XMPP { - class Client; - class Jid; - - class Task : public QObject - { - Q_OBJECT - public: - enum { ErrDisc, ErrTimeout }; - Task(Task *parent); - Task(Client *, bool isRoot); - virtual ~Task(); - - Task *parent() const; - Client *client() const; - QDomDocument *doc() const; - QString id() const; - - bool success() const; - int statusCode() const; - const QString & statusString() const; - const Stanza::Error & error() const; - - void setTimeout(int seconds) const; - int timeout(); - - void go(bool autoDelete=false); - virtual bool take(const QDomElement &); - void safeDelete(); - - signals: - void finished(); - - protected: - virtual void onGo(); - virtual void onDisconnect(); - virtual void onTimeout(); - void send(const QDomElement &); - void setSuccess(int code=0, const QString &str=""); - void setError(const QDomElement &); - void setError(int code=0, const QString &str=""); - void debug(const char *, ...); - void debug(const QString &); - bool iqVerify(const QDomElement &x, const Jid &to, const QString &id, const QString &xmlns=""); - - private slots: - void clientDisconnected(); - void timeoutFinished(); - void done(); - - private: - void init(); - - class TaskPrivate; - TaskPrivate *d; - }; -} - -#endif +class Client; +class Jid; + +class Task : public QObject { + Q_OBJECT +public: + enum { ErrDisc, ErrTimeout }; + Task(Task *parent); + Task(Client *, bool isRoot); + virtual ~Task(); + + Task *parent() const; + Client *client() const; + QDomDocument *doc() const; + QString id() const; + + bool success() const; + int statusCode() const; + const QString &statusString() const; + const Stanza::Error &error() const; + + void setTimeout(int seconds) const; + int timeout(); + + void go(bool autoDelete = false); + virtual bool take(const QDomElement &); + void safeDelete(); + +signals: + void finished(); + +protected: + virtual void onGo(); + virtual void onDisconnect(); + virtual void onTimeout(); + void send(const QDomElement &); + void setSuccess(int code = 0, const QString &str = ""); + void setError(const QDomElement &); + void setError(int code = 0, const QString &str = ""); + void debug(const char *, ...); + void debug(const QString &); + bool iqVerify(const QDomElement &x, const Jid &to, const QString &id, const QString &xmlns = ""); + QString encryptionProtocol(const QDomElement &) const; + +private slots: + void clientDisconnected(); + void timeoutFinished(); + void done(); + +private: + void init(); + + class TaskPrivate; + TaskPrivate *d; +}; +} // namespace XMPP + +#endif // XMPP_TASK_H diff --git a/src/xmpp/xmpp-im/xmpp_tasks.cpp b/src/xmpp/xmpp-im/xmpp_tasks.cpp index ea863af4..429b95f9 100644 --- a/src/xmpp/xmpp-im/xmpp_tasks.cpp +++ b/src/xmpp/xmpp-im/xmpp_tasks.cpp @@ -1,6 +1,6 @@ /* * tasks.cpp - basic tasks - * Copyright (C) 2001, 2002 Justin Karneges + * Copyright (C) 2001-2002 Justin Karneges * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -17,28 +17,35 @@ * */ -#include -#include -#include -#include - #include "xmpp_tasks.h" -#include "xmpp_xmlcommon.h" -#include "xmpp_vcard.h" -#include "xmpp_bitsofbinary.h" -#include "xmpp_captcha.h" + #include "xmpp/base/timezone.h" +#include "xmpp_bitsofbinary.h" #include "xmpp_caps.h" +#include "xmpp_captcha.h" +#include "xmpp_client.h" +#include "xmpp_roster.h" +#include "xmpp_vcard.h" +#include "xmpp_xmlcommon.h" + +#include +#include +#include +#include using namespace XMPP; -#define GET_SUBSCRIBER_ITERATOR(list, sbs) std::find_if(list.begin(), list.end(), [sbs](const Private::SubsData &value) { return value.sbs == sbs; }) +#define GET_SUBSCRIBER_ITERATOR(list, sbs) \ + std::find_if(list.begin(), list.end(), [sbs](const Private::SubsData &value) { return value.sbs == sbs; }) static QString lineEncode(QString str) { - str.replace(QRegExp("\\\\"), "\\\\"); // backslash to double-backslash - str.replace(QRegExp("\\|"), "\\p"); // pipe to \p - str.replace(QRegExp("\n"), "\\n"); // newline to \n + static QRegularExpression backslash("\\\\"); + static QRegularExpression pipe("\\|"); + static QRegularExpression newline("\n"); + str.replace(backslash, "\\\\"); // backslash to double-backslash + str.replace(pipe, "\\p"); // pipe to \p + str.replace(newline, "\\n"); // newline to \n return str; } @@ -46,20 +53,19 @@ static QString lineDecode(const QString &str) { QString ret; - for(int n = 0; n < str.length(); ++n) { - if(str.at(n) == '\\') { + for (int n = 0; n < str.length(); ++n) { + if (str.at(n) == '\\') { ++n; - if(n >= str.length()) + if (n >= str.length()) break; - if(str.at(n) == 'n') + if (str.at(n) == 'n') ret.append('\n'); - if(str.at(n) == 'p') + if (str.at(n) == 'p') ret.append('|'); - if(str.at(n) == '\\') + if (str.at(n) == '\\') ret.append('\\'); - } - else { + } else { ret.append(str.at(n)); } } @@ -71,16 +77,16 @@ static Roster xmlReadRoster(const QDomElement &q, bool push) { Roster r; - for(QDomNode n = q.firstChild(); !n.isNull(); n = n.nextSibling()) { + for (QDomNode n = q.firstChild(); !n.isNull(); n = n.nextSibling()) { QDomElement i = n.toElement(); - if(i.isNull()) + if (i.isNull()) continue; - if(i.tagName() == "item") { + if (i.tagName() == "item") { RosterItem item; item.fromXml(i); - if(push) + if (push) item.setIsPush(true); r += item; @@ -94,35 +100,31 @@ static Roster xmlReadRoster(const QDomElement &q, bool push) // JT_Session //---------------------------------------------------------------------------- // -#include "protocol.h" +#include "xmpp/xmpp-core/protocol.h" -JT_Session::JT_Session(Task *parent) : Task(parent) -{ -} +JT_Session::JT_Session(Task *parent) : Task(parent) { } void JT_Session::onGo() { - QDomElement iq = createIQ(doc(), "set", "", id()); - QDomElement session = doc()->createElement("session"); - session.setAttribute("xmlns",NS_SESSION); + QDomElement iq = createIQ(doc(), "set", "", id()); + QDomElement session = doc()->createElementNS(NS_SESSION, "session"); iq.appendChild(session); send(iq); } -bool JT_Session::take(const QDomElement& x) +bool JT_Session::take(const QDomElement &x) { QString from = x.attribute("from"); if (!from.endsWith("chat.facebook.com")) { // remove this code when chat.facebook.com is disabled completely from.clear(); } - if(!iqVerify(x, from, id())) + if (!iqVerify(x, from, id())) return false; - if(x.attribute("type") == "result") { + if (x.attribute("type") == "result") { setSuccess(); - } - else { + } else { setError(x); } return true; @@ -131,40 +133,34 @@ bool JT_Session::take(const QDomElement& x) //---------------------------------------------------------------------------- // JT_Register //---------------------------------------------------------------------------- -class JT_Register::Private -{ +class JT_Register::Private { public: Private() = default; - Form form; + Form form; XData xdata; - bool hasXData; - bool registered; - Jid jid; - int type; + bool hasXData; + bool registered; + Jid jid; + int type; }; -JT_Register::JT_Register(Task *parent) -:Task(parent) +JT_Register::JT_Register(Task *parent) : Task(parent) { - d = new Private; - d->type = -1; - d->hasXData = false; + d = new Private; + d->type = -1; + d->hasXData = false; d->registered = false; } -JT_Register::~JT_Register() -{ - delete d; -} +JT_Register::~JT_Register() { delete d; } void JT_Register::reg(const QString &user, const QString &pass) { - d->type = 0; - to = client()->host(); - iq = createIQ(doc(), "set", to.full(), id()); - QDomElement query = doc()->createElement("query"); - query.setAttribute("xmlns", "jabber:iq:register"); + d->type = 0; + to = client()->host(); + iq = createIQ(doc(), "set", to.full(), id()); + QDomElement query = doc()->createElementNS("jabber:iq:register", "query"); iq.appendChild(query); query.appendChild(textTag(doc(), "username", user)); query.appendChild(textTag(doc(), "password", pass)); @@ -172,11 +168,10 @@ void JT_Register::reg(const QString &user, const QString &pass) void JT_Register::changepw(const QString &pass) { - d->type = 1; - to = client()->host(); - iq = createIQ(doc(), "set", to.full(), id()); - QDomElement query = doc()->createElement("query"); - query.setAttribute("xmlns", "jabber:iq:register"); + d->type = 1; + to = client()->host(); + iq = createIQ(doc(), "set", to.full(), id()); + QDomElement query = doc()->createElementNS("jabber:iq:register", "query"); iq.appendChild(query); query.appendChild(textTag(doc(), "username", client()->user())); query.appendChild(textTag(doc(), "password", pass)); @@ -184,15 +179,14 @@ void JT_Register::changepw(const QString &pass) void JT_Register::unreg(const Jid &j) { - d->type = 2; - to = j.isEmpty() ? client()->host() : j.full(); - iq = createIQ(doc(), "set", to.full(), id()); - QDomElement query = doc()->createElement("query"); - query.setAttribute("xmlns", "jabber:iq:register"); + d->type = 2; + to = j.isEmpty() ? client()->host() : j.full(); + iq = createIQ(doc(), "set", to.full(), id()); + QDomElement query = doc()->createElementNS("jabber:iq:register", "query"); iq.appendChild(query); // this may be useful - if(!d->form.key().isEmpty()) + if (!d->form.key().isEmpty()) query.appendChild(textTag(doc(), "key", d->form.key())); query.appendChild(doc()->createElement("remove")); @@ -200,102 +194,81 @@ void JT_Register::unreg(const Jid &j) void JT_Register::getForm(const Jid &j) { - d->type = 3; - to = j; - iq = createIQ(doc(), "get", to.full(), id()); - QDomElement query = doc()->createElement("query"); - query.setAttribute("xmlns", "jabber:iq:register"); + d->type = 3; + to = j; + iq = createIQ(doc(), "get", to.full(), id()); + QDomElement query = doc()->createElementNS("jabber:iq:register", "query"); iq.appendChild(query); } void JT_Register::setForm(const Form &form) { - d->type = 4; - to = form.jid(); - iq = createIQ(doc(), "set", to.full(), id()); - QDomElement query = doc()->createElement("query"); - query.setAttribute("xmlns", "jabber:iq:register"); + d->type = 4; + to = form.jid(); + iq = createIQ(doc(), "set", to.full(), id()); + QDomElement query = doc()->createElementNS("jabber:iq:register", "query"); iq.appendChild(query); // key? - if(!form.key().isEmpty()) + if (!form.key().isEmpty()) query.appendChild(textTag(doc(), "key", form.key())); // fields - for(Form::ConstIterator it = form.begin(); it != form.end(); ++it) { - const FormField &f = *it; + for (const auto &f : form) { query.appendChild(textTag(doc(), f.realName(), f.value())); } } -void JT_Register::setForm(const Jid& to, const XData& xdata) +void JT_Register::setForm(const Jid &to, const XData &xdata) { - d->type = 4; - iq = createIQ(doc(), "set", to.full(), id()); - QDomElement query = doc()->createElement("query"); - query.setAttribute("xmlns", "jabber:iq:register"); + d->type = 4; + iq = createIQ(doc(), "set", to.full(), id()); + QDomElement query = doc()->createElementNS("jabber:iq:register", "query"); iq.appendChild(query); query.appendChild(xdata.toXml(doc(), true)); } -const Form & JT_Register::form() const -{ - return d->form; -} +const Form &JT_Register::form() const { return d->form; } -bool JT_Register::hasXData() const -{ - return d->hasXData; -} +bool JT_Register::hasXData() const { return d->hasXData; } -const XData& JT_Register::xdata() const -{ - return d->xdata; -} +const XData &JT_Register::xdata() const { return d->xdata; } -bool JT_Register::isRegistered() const -{ - return d->registered; -} +bool JT_Register::isRegistered() const { return d->registered; } -void JT_Register::onGo() -{ - send(iq); -} +void JT_Register::onGo() { send(iq); } bool JT_Register::take(const QDomElement &x) { - if(!iqVerify(x, to, id())) + if (!iqVerify(x, to, id())) return false; Jid from(x.attribute("from")); - if(x.attribute("type") == "result") { - if(d->type == 3) { + if (x.attribute("type") == "result") { + if (d->type == 3) { d->form.clear(); d->form.setJid(from); QDomElement q = queryTag(x); - for(QDomNode n = q.firstChild(); !n.isNull(); n = n.nextSibling()) { + for (QDomNode n = q.firstChild(); !n.isNull(); n = n.nextSibling()) { QDomElement i = n.toElement(); - if(i.isNull()) + if (i.isNull()) continue; - if(i.tagName() == "instructions") + if (i.tagName() == "instructions") d->form.setInstructions(tagContent(i)); - else if(i.tagName() == "key") + else if (i.tagName() == "key") d->form.setKey(tagContent(i)); else if (i.tagName() == QLatin1String("registered")) d->registered = true; - else if(i.tagName() == "x" && i.attribute("xmlns") == "jabber:x:data") { + else if (i.tagName() == "x" && i.namespaceURI() == "jabber:x:data") { d->xdata.fromXml(i); d->hasXData = true; - } - else if(i.tagName() == "data" && i.attribute("xmlns") == "urn:xmpp:bob") { + } else if (i.tagName() == "data" && i.namespaceURI() == "urn:xmpp:bob") { client()->bobManager()->append(BoBData(i)); // xep-0231 - } - else { + } else { FormField f; - if(f.setType(i.tagName())) { + if (f.setType(i.tagName())) { f.setValue(tagContent(i)); d->form += f; } @@ -304,8 +277,7 @@ bool JT_Register::take(const QDomElement &x) } setSuccess(); - } - else + } else setError(x); return true; @@ -314,20 +286,18 @@ bool JT_Register::take(const QDomElement &x) //---------------------------------------------------------------------------- // JT_UnRegister //---------------------------------------------------------------------------- -class JT_UnRegister::Private -{ +class JT_UnRegister::Private { public: Private() = default; - Jid j; + Jid j; JT_Register *jt_reg = nullptr; }; -JT_UnRegister::JT_UnRegister(Task *parent) -: Task(parent) +JT_UnRegister::JT_UnRegister(Task *parent) : Task(parent) { - d = new Private; - d->jt_reg = 0; + d = new Private; + d->jt_reg = nullptr; } JT_UnRegister::~JT_UnRegister() @@ -336,10 +306,7 @@ JT_UnRegister::~JT_UnRegister() delete d; } -void JT_UnRegister::unreg(const Jid &j) -{ - d->j = j; -} +void JT_UnRegister::unreg(const Jid &j) { d->j = j; } void JT_UnRegister::onGo() { @@ -353,7 +320,7 @@ void JT_UnRegister::onGo() void JT_UnRegister::getFormFinished() { - disconnect(d->jt_reg, 0, this, 0); + disconnect(d->jt_reg, nullptr, this, nullptr); if (d->jt_reg->isRegistered()) { d->jt_reg->unreg(d->j); connect(d->jt_reg, SIGNAL(finished()), SLOT(unregFinished())); @@ -365,122 +332,153 @@ void JT_UnRegister::getFormFinished() void JT_UnRegister::unregFinished() { - if ( d->jt_reg->success() ) + if (d->jt_reg->success()) setSuccess(); else setError(d->jt_reg->statusCode(), d->jt_reg->statusString()); delete d->jt_reg; - d->jt_reg = 0; + d->jt_reg = nullptr; } //---------------------------------------------------------------------------- // JT_Roster //---------------------------------------------------------------------------- -class JT_Roster::Private -{ +class JT_Roster::Private { public: Private() = default; - Roster roster; + Roster roster; + QString groupsDelimiter; QList itemList; }; -JT_Roster::JT_Roster(Task *parent) -:Task(parent) +JT_Roster::JT_Roster(Task *parent) : Task(parent) { - type = -1; - d = new Private; + type = Unknown; + d = new Private; } -JT_Roster::~JT_Roster() -{ - delete d; -} +JT_Roster::~JT_Roster() { delete d; } void JT_Roster::get() { - type = 0; - //to = client()->host(); - iq = createIQ(doc(), "get", to.full(), id()); - QDomElement query = doc()->createElement("query"); - query.setAttribute("xmlns", "jabber:iq:roster"); + type = Get; + // to = client()->host(); + iq = createIQ(doc(), "get", to.full(), id()); + QDomElement query = doc()->createElementNS("jabber:iq:roster", "query"); iq.appendChild(query); } void JT_Roster::set(const Jid &jid, const QString &name, const QStringList &groups) { - type = 1; - //to = client()->host(); + type = Set; + // to = client()->host(); QDomElement item = doc()->createElement("item"); item.setAttribute("jid", jid.full()); - if(!name.isEmpty()) + if (!name.isEmpty()) item.setAttribute("name", name); - for(QStringList::ConstIterator it = groups.begin(); it != groups.end(); ++it) - item.appendChild(textTag(doc(), "group", *it)); + for (const auto &group : groups) + item.appendChild(textTag(doc(), "group", group)); d->itemList += item; } void JT_Roster::remove(const Jid &jid) { - type = 1; - //to = client()->host(); + type = Remove; + // to = client()->host(); QDomElement item = doc()->createElement("item"); item.setAttribute("jid", jid.full()); item.setAttribute("subscription", "remove"); d->itemList += item; } +void JT_Roster::getGroupsDelimiter() +{ + type = GetDelimiter; + // to = client()->host(); + iq = createIQ(doc(), "get", to.full(), id()); + + QDomElement roster = doc()->createElement("roster"); + roster.setAttribute("xmlns", "roster:delimiter"); + + QDomElement query = doc()->createElement("query"); + query.setAttribute("xmlns", "jabber:iq:private"); + query.appendChild(roster); + + iq.appendChild(query); +} + +void JT_Roster::setGroupsDelimiter(const QString &groupsDelimiter) +{ + type = SetDelimiter; + // to = client()->host(); + iq = createIQ(doc(), "set", to.full(), id()); + + QDomText text = doc()->createTextNode(groupsDelimiter); + + QDomElement roster = doc()->createElement("roster"); + roster.setAttribute("xmlns", "roster:delimiter"); + roster.appendChild(text); + + QDomElement query = doc()->createElement("query"); + query.setAttribute("xmlns", "jabber:iq:private"); + query.appendChild(roster); + + iq.appendChild(query); +} + void JT_Roster::onGo() { - if(type == 0) + if (type == Get) send(iq); - else if(type == 1) { - //to = client()->host(); - iq = createIQ(doc(), "set", to.full(), id()); - QDomElement query = doc()->createElement("query"); - query.setAttribute("xmlns", "jabber:iq:roster"); + else if (type == Set || type == Remove) { + // to = client()->host(); + iq = createIQ(doc(), "set", to.full(), id()); + QDomElement query = doc()->createElementNS("jabber:iq:roster", "query"); iq.appendChild(query); - foreach (const QDomElement& it, d->itemList) + for (const QDomElement &it : std::as_const(d->itemList)) query.appendChild(it); send(iq); + } else if (type == GetDelimiter) { + send(iq); + } else if (type == SetDelimiter) { + send(iq); } } -const Roster & JT_Roster::roster() const -{ - return d->roster; -} +const Roster &JT_Roster::roster() const { return d->roster; } + +QString JT_Roster::groupsDelimiter() const { return d->groupsDelimiter; } QString JT_Roster::toString() const { - if(type != 1) + if (type != Set) return ""; QDomElement i = doc()->createElement("request"); i.setAttribute("type", "JT_Roster"); - foreach (const QDomElement& it, d->itemList) + for (const QDomElement &it : std::as_const(d->itemList)) i.appendChild(it); return lineEncode(Stream::xmlToString(i)); - return ""; } bool JT_Roster::fromString(const QString &str) { QDomDocument *dd = new QDomDocument; - if(!dd->setContent(lineDecode(str).toUtf8())) + if (!dd->setContent(lineDecode(str).toUtf8())) return false; QDomElement e = doc()->importNode(dd->documentElement(), true).toElement(); delete dd; - if(e.tagName() != "request" || e.attribute("type") != "JT_Roster") + if (e.tagName() != "request" || e.attribute("type") != "JT_Roster") return false; - type = 1; + type = Set; d->itemList.clear(); - for(QDomNode n = e.firstChild(); !n.isNull(); n = n.nextSibling()) { + for (QDomNode n = e.firstChild(); !n.isNull(); n = n.nextSibling()) { QDomElement i = n.toElement(); - if(i.isNull()) + if (i.isNull()) continue; d->itemList += i; } @@ -490,33 +488,40 @@ bool JT_Roster::fromString(const QString &str) bool JT_Roster::take(const QDomElement &x) { - if(!iqVerify(x, client()->host(), id())) + if (!iqVerify(x, client()->host(), id())) return false; - // get - if(type == 0) { - if(x.attribute("type") == "result") { + if (type == Get) { + if (x.attribute("type") == "result") { QDomElement q = queryTag(x); - d->roster = xmlReadRoster(q, false); + d->roster = xmlReadRoster(q, false); setSuccess(); - } - else { + } else { setError(x); } return true; - } - // set - else if(type == 1) { - if(x.attribute("type") == "result") + } else if (type == Set) { + if (x.attribute("type") == "result") setSuccess(); else setError(x); return true; - } - // remove - else if(type == 2) { + } else if (type == Remove) { + setSuccess(); + return true; + } else if (type == GetDelimiter) { + if (x.attribute("type") == "result") { + QDomElement q = queryTag(x); + QDomElement delimiter = q.firstChild().toElement(); + d->groupsDelimiter = delimiter.firstChild().toText().data(); + setSuccess(); + } else { + setError(x); + } + return true; + } else if (type == SetDelimiter) { setSuccess(); return true; } @@ -524,98 +529,84 @@ bool JT_Roster::take(const QDomElement &x) return false; } - //---------------------------------------------------------------------------- // JT_PushRoster //---------------------------------------------------------------------------- -JT_PushRoster::JT_PushRoster(Task *parent) : Task(parent) -{ -} +JT_PushRoster::JT_PushRoster(Task *parent) : Task(parent) { } -JT_PushRoster::~JT_PushRoster() -{ -} +JT_PushRoster::~JT_PushRoster() { } bool JT_PushRoster::take(const QDomElement &e) { // must be an iq-set tag - if(e.tagName() != "iq" || e.attribute("type") != "set") + if (e.tagName() != "iq" || e.attribute("type") != "set") return false; - if(!iqVerify(e, client()->host(), "", "jabber:iq:roster")) + if (!iqVerify(e, client()->host(), "", "jabber:iq:roster")) return false; - roster(xmlReadRoster(queryTag(e), true)); + emit roster(xmlReadRoster(queryTag(e), true)); send(createIQ(doc(), "result", e.attribute("from"), e.attribute("id"))); return true; } - //---------------------------------------------------------------------------- // JT_Presence //---------------------------------------------------------------------------- -JT_Presence::JT_Presence(Task *parent) : Task(parent) -{ -} +JT_Presence::JT_Presence(Task *parent) : Task(parent) { } -JT_Presence::~JT_Presence() -{ -} +JT_Presence::~JT_Presence() { } void JT_Presence::pres(const Status &s) { type = 0; tag = doc()->createElement("presence"); - if(!s.isAvailable()) { + if (!s.isAvailable()) { tag.setAttribute("type", "unavailable"); - if(!s.status().isEmpty()) + if (!s.status().isEmpty()) tag.appendChild(textTag(doc(), "status", s.status())); - } - else { - if(s.isInvisible()) + } else { + if (s.isInvisible()) tag.setAttribute("type", "invisible"); - if(!s.show().isEmpty()) + if (!s.show().isEmpty()) tag.appendChild(textTag(doc(), "show", s.show())); - if(!s.status().isEmpty()) + if (!s.status().isEmpty()) tag.appendChild(textTag(doc(), "status", s.status())); - tag.appendChild( textTag(doc(), "priority", QString("%1").arg(s.priority()) ) ); + tag.appendChild(textTag(doc(), "priority", QString("%1").arg(s.priority()))); - if(!s.keyID().isEmpty()) { - QDomElement x = textTag(doc(), "x", s.keyID()); - x.setAttribute("xmlns", "http://jabber.org/protocol/e2e"); + if (!s.keyID().isEmpty()) { + QDomElement x = textTagNS(doc(), "http://jabber.org/protocol/e2e", "x", s.keyID()); tag.appendChild(x); } - if(!s.xsigned().isEmpty()) { - QDomElement x = textTag(doc(), "x", s.xsigned()); - x.setAttribute("xmlns", "jabber:x:signed"); + if (!s.xsigned().isEmpty()) { + QDomElement x = textTagNS(doc(), "jabber:x:signed", "x", s.xsigned()); tag.appendChild(x); } - if (client()->capsManager()->isEnabled()) { + if (client()->capsManager()->isEnabled() && !client()->capsOptimizationAllowed()) { CapsSpec cs = client()->caps(); if (cs.isValid()) { tag.appendChild(cs.toXml(doc())); } } - if(s.isMUC()) { - QDomElement m = doc()->createElement("x"); - m.setAttribute("xmlns","http://jabber.org/protocol/muc"); + if (s.isMUC()) { + QDomElement m = doc()->createElementNS("http://jabber.org/protocol/muc", "x"); if (!s.mucPassword().isEmpty()) { - m.appendChild(textTag(doc(),"password",s.mucPassword())); + m.appendChild(textTag(doc(), "password", s.mucPassword())); } if (s.hasMUCHistory()) { QDomElement h = doc()->createElement("history"); if (s.mucHistoryMaxChars() >= 0) - h.setAttribute("maxchars",s.mucHistoryMaxChars()); + h.setAttribute("maxchars", s.mucHistoryMaxChars()); if (s.mucHistoryMaxStanzas() >= 0) - h.setAttribute("maxstanzas",s.mucHistoryMaxStanzas()); + h.setAttribute("maxstanzas", s.mucHistoryMaxStanzas()); if (s.mucHistorySeconds() >= 0) - h.setAttribute("seconds",s.mucHistorySeconds()); + h.setAttribute("seconds", s.mucHistorySeconds()); if (!s.mucHistorySince().isNull()) h.setAttribute("since", s.mucHistorySince().toUTC().addSecs(1).toString(Qt::ISODate)); m.appendChild(h); @@ -623,15 +614,15 @@ void JT_Presence::pres(const Status &s) tag.appendChild(m); } - if(s.hasPhotoHash()) { - QDomElement m = doc()->createElement("x"); - m.setAttribute("xmlns", "vcard-temp:x:update"); - m.appendChild(textTag(doc(), "photo", s.photoHash())); + if (s.hasPhotoHash()) { + QDomElement m = doc()->createElementNS("vcard-temp:x:update", "x"); + m.appendChild(textTag(doc(), "photo", QString::fromLatin1(s.photoHash().toHex()))); tag.appendChild(m); } // bits of binary - foreach(const BoBData &bd, s.bobDataList()) { + const auto &bdlist = s.bobDataList(); + for (const BoBData &bd : bdlist) { tag.appendChild(bd.toXml(doc())); } } @@ -643,18 +634,17 @@ void JT_Presence::pres(const Jid &to, const Status &s) tag.setAttribute("to", to.full()); } -void JT_Presence::sub(const Jid &to, const QString &subType, const QString& nick) +void JT_Presence::sub(const Jid &to, const QString &subType, const QString &nick) { type = 1; tag = doc()->createElement("presence"); tag.setAttribute("to", to.full()); tag.setAttribute("type", subType); - if (!nick.isEmpty() && (subType == QLatin1String("subscribe") || subType == QLatin1String("subscribed") || - subType == QLatin1String("unsubscribe") || subType == QLatin1String("unsubscribed"))) - { - QDomElement nick_tag = textTag(doc(),"nick",nick); - nick_tag.setAttribute("xmlns","http://jabber.org/protocol/nick"); + if (!nick.isEmpty() + && (subType == QLatin1String("subscribe") || subType == QLatin1String("subscribed") + || subType == QLatin1String("unsubscribe") || subType == QLatin1String("unsubscribed"))) { + QDomElement nick_tag = textTagNS(doc(), "http://jabber.org/protocol/nick", "nick", nick); tag.appendChild(nick_tag); } } @@ -674,46 +664,38 @@ void JT_Presence::onGo() setSuccess(); } - //---------------------------------------------------------------------------- // JT_PushPresence //---------------------------------------------------------------------------- -JT_PushPresence::JT_PushPresence(Task *parent) - : Task(parent) -{ -} +JT_PushPresence::JT_PushPresence(Task *parent) : Task(parent) { } -JT_PushPresence::~JT_PushPresence() -{ -} +JT_PushPresence::~JT_PushPresence() { } bool JT_PushPresence::take(const QDomElement &e) { - if(e.tagName() != "presence") + if (e.tagName() != "presence") return false; - Jid j(e.attribute("from")); + Jid j(e.attribute("from")); Status p; - if(e.hasAttribute("type")) { + if (e.hasAttribute("type")) { QString type = e.attribute("type"); - if(type == "unavailable") { + if (type == "unavailable") { p.setIsAvailable(false); - } - else if(type == "error") { - QString str = ""; - int code = 0; + } else if (type == "error") { + QString str = ""; + int code = 0; getErrorFromElement(e, client()->stream().baseNS(), &code, &str); p.setError(code, str); - } - else if(type == QLatin1String("subscribe") || type == QLatin1String("subscribed") || - type == QLatin1String("unsubscribe") || type == QLatin1String("unsubscribed")) { - QString nick; + } else if (type == QLatin1String("subscribe") || type == QLatin1String("subscribed") + || type == QLatin1String("unsubscribe") || type == QLatin1String("unsubscribed")) { + QString nick; QDomElement tag = e.firstChildElement("nick"); - if (!tag.isNull() && tag.attribute("xmlns") == "http://jabber.org/protocol/nick") { + if (!tag.isNull() && tag.namespaceURI() == "http://jabber.org/protocol/nick") { nick = tagContent(tag); } - subscription(j, type, nick); + emit subscription(j, type, nick); return true; } } @@ -721,71 +703,61 @@ bool JT_PushPresence::take(const QDomElement &e) QDomElement tag; tag = e.firstChildElement("status"); - if(!tag.isNull()) + if (!tag.isNull()) p.setStatus(tagContent(tag)); tag = e.firstChildElement("show"); - if(!tag.isNull()) + if (!tag.isNull()) p.setShow(tagContent(tag)); tag = e.firstChildElement("priority"); - if(!tag.isNull()) + if (!tag.isNull()) p.setPriority(tagContent(tag).toInt()); QDateTime stamp; - for(QDomNode n = e.firstChild(); !n.isNull(); n = n.nextSibling()) { + for (QDomNode n = e.firstChild(); !n.isNull(); n = n.nextSibling()) { QDomElement i = n.toElement(); - if(i.isNull()) + if (i.isNull()) continue; - if(i.tagName() == "x" && i.attribute("xmlns") == "jabber:x:delay") { - if(i.hasAttribute("stamp") && !stamp.isValid()) { + if (i.tagName() == "x" && i.namespaceURI() == "jabber:x:delay") { + if (i.hasAttribute("stamp") && !stamp.isValid()) { stamp = stamp2TS(i.attribute("stamp")); } - } - else if(i.tagName() == "delay" && i.attribute("xmlns") == "urn:xmpp:delay") { - if(i.hasAttribute("stamp") && !stamp.isValid()) { + } else if (i.tagName() == "delay" && i.namespaceURI() == "urn:xmpp:delay") { + if (i.hasAttribute("stamp") && !stamp.isValid()) { stamp = QDateTime::fromString(i.attribute("stamp").left(19), Qt::ISODate); } - } - else if(i.tagName() == "x" && i.attribute("xmlns") == "gabber:x:music:info") { + } else if (i.tagName() == "x" && i.namespaceURI() == "gabber:x:music:info") { QDomElement t; - QString title, state; + QString title, state; t = i.firstChildElement("title"); - if(!t.isNull()) + if (!t.isNull()) title = tagContent(t); t = i.firstChildElement("state"); - if(!t.isNull()) + if (!t.isNull()) state = tagContent(t); - if(!title.isEmpty() && state == "playing") + if (!title.isEmpty() && state == "playing") p.setSongTitle(title); - } - else if(i.tagName() == "x" && i.attribute("xmlns") == "jabber:x:signed") { + } else if (i.tagName() == "x" && i.namespaceURI() == "jabber:x:signed") { p.setXSigned(tagContent(i)); - } - else if(i.tagName() == "x" && i.attribute("xmlns") == "http://jabber.org/protocol/e2e") { + } else if (i.tagName() == "x" && i.namespaceURI() == "http://jabber.org/protocol/e2e") { p.setKeyID(tagContent(i)); - } - else if(i.tagName() == "c" && i.attribute("xmlns") == NS_CAPS) { + } else if (i.tagName() == "c" && i.namespaceURI() == NS_CAPS) { p.setCaps(CapsSpec::fromXml(i)); - if(!e.hasAttribute("type") && p.caps().isValid()) { + if (!e.hasAttribute("type") && p.caps().isValid()) { client()->capsManager()->updateCaps(j, p.caps()); } - } - else if(i.tagName() == "x" && i.attribute("xmlns") == "vcard-temp:x:update") { + } else if (i.tagName() == "x" && i.namespaceURI() == "vcard-temp:x:update") { QDomElement t; t = i.firstChildElement("photo"); if (!t.isNull()) - p.setPhotoHash(tagContent(t).toLower()); // if hash is empty this may mean photo removal + p.setPhotoHash( + QByteArray::fromHex(tagContent(t).toLatin1())); // if hash is empty this may mean photo removal // else vcard.hasPhotoHash() returns false and that's mean user is not yet ready to advertise his image - } - else if(i.tagName() == "x" && i.attribute("xmlns") == "http://jabber.org/protocol/muc#user") { - for(QDomNode muc_n = i.firstChild(); !muc_n.isNull(); muc_n = muc_n.nextSibling()) { - QDomElement muc_e = muc_n.toElement(); - if(muc_e.isNull()) - continue; - + } else if (i.tagName() == "x" && i.namespaceURI() == "http://jabber.org/protocol/muc#user") { + for (QDomElement muc_e = i.firstChildElement(); !muc_e.isNull(); muc_e = muc_e.nextSiblingElement()) { if (muc_e.tagName() == "item") p.setMUCItem(MUCItem(muc_e)); else if (muc_e.tagName() == "status") @@ -793,15 +765,13 @@ bool JT_PushPresence::take(const QDomElement &e) else if (muc_e.tagName() == "destroy") p.setMUCDestroy(MUCDestroy(muc_e)); } - } - else if (i.tagName() == "data" && i.attribute("xmlns") == "urn:xmpp:bob") { + } else if (i.tagName() == "data" && i.namespaceURI() == "urn:xmpp:bob") { BoBData bd(i); client()->bobManager()->append(bd); p.addBoBData(bd); } } - if (stamp.isValid()) { if (client()->manualTimeZoneOffset()) { stamp = stamp.addSecs(client()->timeZoneOffset() * 3600); @@ -812,12 +782,11 @@ bool JT_PushPresence::take(const QDomElement &e) p.setTimeStamp(stamp); } - presence(j, p); + emit presence(j, p); return true; } - //---------------------------------------------------------------------------- // JT_Message //---------------------------------------------------------------------------- @@ -825,32 +794,32 @@ static QDomElement oldStyleNS(const QDomElement &e) { // find closest parent with a namespace QDomNode par = e.parentNode(); - while(!par.isNull() && par.namespaceURI().isNull()) + while (!par.isNull() && par.namespaceURI().isNull()) par = par.parentNode(); bool noShowNS = false; - if(!par.isNull() && par.namespaceURI() == e.namespaceURI()) + if (!par.isNull() && par.namespaceURI() == e.namespaceURI()) noShowNS = true; QDomElement i; - int x; - //if(noShowNS) - i = e.ownerDocument().createElement(e.tagName()); - //else + int x; + // if(noShowNS) + i = e.ownerDocument().createElement(e.tagName()); + // else // i = e.ownerDocument().createElementNS(e.namespaceURI(), e.tagName()); // copy attributes QDomNamedNodeMap al = e.attributes(); - for(x = 0; x < al.count(); ++x) + for (x = 0; x < al.count(); ++x) i.setAttributeNode(al.item(x).cloneNode().toAttr()); - if(!noShowNS) + if (!noShowNS) i.setAttribute("xmlns", e.namespaceURI()); // copy children QDomNodeList nl = e.childNodes(); - for(x = 0; x < nl.count(); ++x) { + for (x = 0; x < nl.count(); ++x) { QDomNode n = nl.item(x); - if(n.isElement()) + if (n.isElement()) i.appendChild(oldStyleNS(n.toElement())); else i.appendChild(n.cloneNode()); @@ -858,27 +827,28 @@ static QDomElement oldStyleNS(const QDomElement &e) return i; } -JT_Message::JT_Message(Task *parent, Message &msg) - : Task(parent) - , m(msg) +JT_Message::JT_Message(Task *parent, Message &msg) : Task(parent), m(msg) { if (msg.id().isEmpty()) msg.setId(id()); } -JT_Message::~JT_Message() -{ -} +JT_Message::~JT_Message() { } void JT_Message::onGo() { - Stanza s = m.toStanza(&(client()->stream())); - QDomElement e = oldStyleNS(s.element()); + Stanza s = m.toStanza(&(client()->stream())); + QDomElement e = s.element(); // oldStyleNS(s.element()); - auto encryptionHandler = client()->encryptionHandler(); - bool wasEncrypted = encryptionHandler && encryptionHandler->encryptMessageElement(e); + if (auto encryptionHandler = client()->encryptionHandler()) { + Q_UNUSED(encryptionHandler->encryptMessageElement(e)); + } + + // See: XEP-0380: Explicit Message Encryption + const bool wasEncrypted = !e.firstChildElement("encryption").isNull(); m.setWasEncrypted(wasEncrypted); + m.setEncryptionProtocol(encryptionProtocol(e)); // if the element is null, then the encryption is happening asynchronously if (!e.isNull()) { @@ -887,32 +857,28 @@ void JT_Message::onGo() setSuccess(); } - //---------------------------------------------------------------------------- // JT_PushMessage //---------------------------------------------------------------------------- class JT_PushMessage::Private { public: struct SubsData { - Subscriber *sbs = nullptr; - int userData = -1; + Subscriber *sbs = nullptr; + int userData = -1; }; using SubsDataList = QVector; - EncryptionHandler *m_encryptionHandler; + EncryptionHandler *m_encryptionHandler; QHash subsData; - SubsDataList subsMData; + SubsDataList subsMData; - QString genKey(const QString &s1, const QString &s2) - { - return QString::fromLatin1("%1&%2").arg(s1, s2); - } + QString genKey(const QString &s1, const QString &s2) { return QString::fromLatin1("%1&%2").arg(s1, s2); } bool processChildStanzaNode(const QDomElement &root, QDomElement &e, Client *c, bool nested) { - QString tagName = e.tagName(); + QString tagName = e.tagName(); QString xmlnsStr = e.attribute(QString::fromLatin1("xmlns")); - QString key = genKey(tagName, xmlnsStr); - auto it = subsData.constFind(key); + QString key = genKey(tagName, xmlnsStr); + auto it = subsData.constFind(key); if (it != subsData.constEnd()) { foreach (const SubsData &sd, it.value()) { if (sd.sbs->xmlEvent(root, e, c, sd.userData, nested)) @@ -924,7 +890,8 @@ class JT_PushMessage::Private { return false; } - bool processMessage(Message &msg, bool nested) { + bool processMessage(Message &msg, bool nested) + { foreach (const SubsData &sd, subsMData) { if (sd.sbs->messageEvent(msg, sd.userData, nested)) return true; @@ -933,9 +900,7 @@ class JT_PushMessage::Private { } }; -JT_PushMessage::Subscriber::~Subscriber() -{ -} +JT_PushMessage::Subscriber::~Subscriber() { } bool JT_PushMessage::Subscriber::xmlEvent(const QDomElement &root, QDomElement &e, Client *c, int userData, bool nested) { @@ -955,38 +920,34 @@ bool JT_PushMessage::Subscriber::messageEvent(Message &msg, int userData, bool n return false; } -JT_PushMessage::JT_PushMessage(Task *parent, EncryptionHandler *encryptionHandler) - : Task(parent) - , d(new Private) +JT_PushMessage::JT_PushMessage(Task *parent, EncryptionHandler *encryptionHandler) : Task(parent), d(new Private) { d->m_encryptionHandler = encryptionHandler; } -JT_PushMessage::~JT_PushMessage() -{ -} +JT_PushMessage::~JT_PushMessage() { } void JT_PushMessage::subscribeXml(Subscriber *sbs, const QString &tagName, const QString &xmlnsStr, int userData) { QString key = d->genKey(tagName, xmlnsStr); - auto it = d->subsData.find(key); + auto it = d->subsData.find(key); if (it != d->subsData.end()) { Private::SubsDataList &list = it.value(); - auto lit = GET_SUBSCRIBER_ITERATOR(list, sbs); + auto lit = GET_SUBSCRIBER_ITERATOR(list, sbs); if (lit == list.end()) list.append({ sbs, userData }); } else { - d->subsData.insert(key, {{ sbs, userData }}); + d->subsData.insert(key, { { sbs, userData } }); } } void JT_PushMessage::unsubscribeXml(Subscriber *sbs, const QString &tagName, const QString &xmlnsStr) { QString key = d->genKey(tagName, xmlnsStr); - auto it = d->subsData.find(key); + auto it = d->subsData.find(key); if (it != d->subsData.end()) { Private::SubsDataList &list = it.value(); - auto lit = GET_SUBSCRIBER_ITERATOR(list, sbs); + auto lit = GET_SUBSCRIBER_ITERATOR(list, sbs); if (lit != list.end()) { list.erase(lit); if (list.isEmpty()) @@ -998,7 +959,7 @@ void JT_PushMessage::unsubscribeXml(Subscriber *sbs, const QString &tagName, con void JT_PushMessage::subscribeMessage(Subscriber *sbs, int userData) { auto &list = d->subsMData; - auto lit = GET_SUBSCRIBER_ITERATOR(list, sbs); + auto lit = GET_SUBSCRIBER_ITERATOR(list, sbs); if (lit == list.end()) list.append({ sbs, userData }); } @@ -1006,18 +967,18 @@ void JT_PushMessage::subscribeMessage(Subscriber *sbs, int userData) void JT_PushMessage::unsubscribeMessage(Subscriber *sbs) { auto &list = d->subsMData; - auto lit = GET_SUBSCRIBER_ITERATOR(list, sbs); + auto lit = GET_SUBSCRIBER_ITERATOR(list, sbs); if (lit != list.end()) list.erase(lit); } bool JT_PushMessage::processXmlSubscribers(QDomElement &el, Client *client, bool nested) { - bool processed = false; - QDomElement ch = el.firstChildElement(); + bool processed = false; + QDomElement ch = el.firstChildElement(); while (!ch.isNull()) { QDomElement next = ch.nextSiblingElement(); - bool res = d->processChildStanzaNode(el, ch, client, nested); + bool res = d->processChildStanzaNode(el, ch, client, nested); if (res) processed = true; if (res || ch.isNull()) @@ -1027,43 +988,46 @@ bool JT_PushMessage::processXmlSubscribers(QDomElement &el, Client *client, bool return (processed && el.childNodes().length() == 0); } -bool JT_PushMessage::processMessageSubscribers(Message &msg, bool nested) -{ - return d->processMessage(msg, nested); -} +bool JT_PushMessage::processMessageSubscribers(Message &msg, bool nested) { return d->processMessage(msg, nested); } bool JT_PushMessage::take(const QDomElement &e) { - if(e.tagName() != "message") + if (e.tagName() != "message") return false; QDomElement e1 = e; - bool wasEncrypted = d->m_encryptionHandler != nullptr && d->m_encryptionHandler->decryptMessageElement(e1) && !e1.isNull(); - if (wasEncrypted && e1.isNull()) { - // The message was processed, but has to be discarded for some reason - return true; + if (d->m_encryptionHandler) { + if (d->m_encryptionHandler->decryptMessageElement(e1)) { + if (e1.isNull()) { + // The message was processed, but has to be discarded for some reason + return true; + } + } } if (processXmlSubscribers(e1, client(), false)) return true; Stanza s = client()->stream().createStanza(addCorrectNS(e1)); - if(s.isNull()) { - //printf("take: bad stanza??\n"); + if (s.isNull()) { + // printf("take: bad stanza??\n"); return false; } Message m; - if(!m.fromStanza(s, client()->manualTimeZoneOffset(), client()->timeZoneOffset())) { - //printf("bad message\n"); + if (!m.fromStanza(s, client()->manualTimeZoneOffset(), client()->timeZoneOffset())) { + // printf("bad message\n"); return false; } if (processMessageSubscribers(m, false)) return true; + // See: XEP-0380: Explicit Message Encryption + const bool wasEncrypted = !e1.firstChildElement("encryption").isNull(); m.setWasEncrypted(wasEncrypted); + m.setEncryptionProtocol(encryptionProtocol(e)); emit message(m); return true; @@ -1072,391 +1036,314 @@ bool JT_PushMessage::take(const QDomElement &e) //---------------------------------------------------------------------------- // JT_VCard //---------------------------------------------------------------------------- -class JT_VCard::Private -{ +class JT_VCard::Private { public: Private() = default; QDomElement iq; - Jid jid; - VCard vcard; + Jid jid; + VCard vcard; }; -JT_VCard::JT_VCard(Task *parent) -:Task(parent) +JT_VCard::JT_VCard(Task *parent) : Task(parent) { type = -1; - d = new Private; + d = new Private; } -JT_VCard::~JT_VCard() -{ - delete d; -} +JT_VCard::~JT_VCard() { delete d; } void JT_VCard::get(const Jid &_jid) { - type = 0; - d->jid = _jid; - d->iq = createIQ(doc(), "get", type == 1 ? Jid().full() : d->jid.full(), id()); - QDomElement v = doc()->createElement("vCard"); - v.setAttribute("xmlns", "vcard-temp"); + type = 0; + d->jid = _jid; + d->iq = createIQ(doc(), "get", type == 1 ? Jid().full() : d->jid.full(), id()); + QDomElement v = doc()->createElementNS("vcard-temp", "vCard"); d->iq.appendChild(v); } -const Jid & JT_VCard::jid() const -{ - return d->jid; -} +const Jid &JT_VCard::jid() const { return d->jid; } -const VCard & JT_VCard::vcard() const -{ - return d->vcard; -} +const VCard &JT_VCard::vcard() const { return d->vcard; } void JT_VCard::set(const VCard &card) { - type = 1; + type = 1; d->vcard = card; - d->jid = ""; - d->iq = createIQ(doc(), "set", d->jid.full(), id()); - d->iq.appendChild(card.toXml(doc()) ); + d->jid = ""; + d->iq = createIQ(doc(), "set", d->jid.full(), id()); + d->iq.appendChild(card.toXml(doc())); } -// isTarget is when we setting target's vcard. for example in case of muc own vcard +// isTarget is when we setting target's vCard. for example in case of muc own vCard void JT_VCard::set(const Jid &j, const VCard &card, bool isTarget) { - type = 1; + type = 1; d->vcard = card; - d->jid = j; - d->iq = createIQ(doc(), "set", isTarget? j.full() : "", id()); - d->iq.appendChild(card.toXml(doc()) ); + d->jid = j; + d->iq = createIQ(doc(), "set", isTarget ? j.full() : "", id()); + d->iq.appendChild(card.toXml(doc())); } -void JT_VCard::onGo() -{ - send(d->iq); -} +void JT_VCard::onGo() { send(d->iq); } bool JT_VCard::take(const QDomElement &x) { Jid to = d->jid; if (to.bare() == client()->jid().bare()) to = client()->host(); - if(!iqVerify(x, to, id())) + if (!iqVerify(x, to, id())) return false; - if(x.attribute("type") == "result") { - if(type == 0) { - for(QDomNode n = x.firstChild(); !n.isNull(); n = n.nextSibling()) { + if (x.attribute("type") == "result") { + if (type == 0) { + for (QDomNode n = x.firstChild(); !n.isNull(); n = n.nextSibling()) { QDomElement q = n.toElement(); - if(q.isNull()) + if (q.isNull()) continue; - if(q.tagName().toUpper() == "VCARD") { + if (q.tagName().toUpper() == "VCARD") { d->vcard = VCard::fromXml(q); - if(d->vcard) { + if (d->vcard) { setSuccess(); return true; } } } - setError(ErrDisc + 1, tr("No VCard available")); + setError(ErrDisc + 1, tr("No vCard available")); return true; - } - else { + } else { setSuccess(); return true; } - } - else { + } else { setError(x); } return true; } - //---------------------------------------------------------------------------- // JT_Search //---------------------------------------------------------------------------- -class JT_Search::Private -{ +class JT_Search::Private { public: Private() = default; - Jid jid; - Form form; - bool hasXData = false; - XData xdata; + Jid jid; + Form form; + bool hasXData = false; + XData xdata; QList resultList; }; -JT_Search::JT_Search(Task *parent) -:Task(parent) +JT_Search::JT_Search(Task *parent) : Task(parent) { - d = new Private; + d = new Private; type = -1; } -JT_Search::~JT_Search() -{ - delete d; -} +JT_Search::~JT_Search() { delete d; } void JT_Search::get(const Jid &jid) { - type = 0; - d->jid = jid; - d->hasXData = false; - d->xdata = XData(); - iq = createIQ(doc(), "get", d->jid.full(), id()); - QDomElement query = doc()->createElement("query"); - query.setAttribute("xmlns", "jabber:iq:search"); + type = 0; + d->jid = jid; + d->hasXData = false; + d->xdata = XData(); + iq = createIQ(doc(), "get", d->jid.full(), id()); + QDomElement query = doc()->createElementNS("jabber:iq:search", "query"); iq.appendChild(query); } void JT_Search::set(const Form &form) { - type = 1; - d->jid = form.jid(); - d->hasXData = false; - d->xdata = XData(); - iq = createIQ(doc(), "set", d->jid.full(), id()); - QDomElement query = doc()->createElement("query"); - query.setAttribute("xmlns", "jabber:iq:search"); + type = 1; + d->jid = form.jid(); + d->hasXData = false; + d->xdata = XData(); + iq = createIQ(doc(), "set", d->jid.full(), id()); + QDomElement query = doc()->createElementNS("jabber:iq:search", "query"); iq.appendChild(query); // key? - if(!form.key().isEmpty()) + if (!form.key().isEmpty()) query.appendChild(textTag(doc(), "key", form.key())); // fields - for(Form::ConstIterator it = form.begin(); it != form.end(); ++it) { - const FormField &f = *it; + for (const auto &f : form) { query.appendChild(textTag(doc(), f.realName(), f.value())); } } void JT_Search::set(const Jid &jid, const XData &form) { - type = 1; - d->jid = jid; - d->hasXData = false; - d->xdata = XData(); - iq = createIQ(doc(), "set", d->jid.full(), id()); - QDomElement query = doc()->createElement("query"); - query.setAttribute("xmlns", "jabber:iq:search"); + type = 1; + d->jid = jid; + d->hasXData = false; + d->xdata = XData(); + iq = createIQ(doc(), "set", d->jid.full(), id()); + QDomElement query = doc()->createElementNS("jabber:iq:search", "query"); iq.appendChild(query); query.appendChild(form.toXml(doc(), true)); } -const Form & JT_Search::form() const -{ - return d->form; -} +const Form &JT_Search::form() const { return d->form; } -const QList & JT_Search::results() const -{ - return d->resultList; -} +const QList &JT_Search::results() const { return d->resultList; } -bool JT_Search::hasXData() const -{ - return d->hasXData; -} +bool JT_Search::hasXData() const { return d->hasXData; } -const XData & JT_Search::xdata() const -{ - return d->xdata; -} +const XData &JT_Search::xdata() const { return d->xdata; } -void JT_Search::onGo() -{ - send(iq); -} +void JT_Search::onGo() { send(iq); } bool JT_Search::take(const QDomElement &x) { - if(!iqVerify(x, d->jid, id())) + if (!iqVerify(x, d->jid, id())) return false; Jid from(x.attribute("from")); - if(x.attribute("type") == "result") { - if(type == 0) { + if (x.attribute("type") == "result") { + if (type == 0) { d->form.clear(); d->form.setJid(from); QDomElement q = queryTag(x); - for(QDomNode n = q.firstChild(); !n.isNull(); n = n.nextSibling()) { + for (QDomNode n = q.firstChild(); !n.isNull(); n = n.nextSibling()) { QDomElement i = n.toElement(); - if(i.isNull()) + if (i.isNull()) continue; - if(i.tagName() == "instructions") + if (i.tagName() == "instructions") d->form.setInstructions(tagContent(i)); - else if(i.tagName() == "key") + else if (i.tagName() == "key") d->form.setKey(tagContent(i)); - else if(i.tagName() == "x" && i.attribute("xmlns") == "jabber:x:data") { + else if (i.tagName() == "x" && i.namespaceURI() == "jabber:x:data") { d->xdata.fromXml(i); d->hasXData = true; - } - else { + } else { FormField f; - if(f.setType(i.tagName())) { + if (f.setType(i.tagName())) { f.setValue(tagContent(i)); d->form += f; } } } - } - else { + } else { d->resultList.clear(); QDomElement q = queryTag(x); - for(QDomNode n = q.firstChild(); !n.isNull(); n = n.nextSibling()) { + for (QDomNode n = q.firstChild(); !n.isNull(); n = n.nextSibling()) { QDomElement i = n.toElement(); - if(i.isNull()) + if (i.isNull()) continue; - if(i.tagName() == "item") { + if (i.tagName() == "item") { SearchResult r(Jid(i.attribute("jid"))); QDomElement tag; tag = i.firstChildElement("nick"); - if(!tag.isNull()) + if (!tag.isNull()) r.setNick(tagContent(tag)); tag = i.firstChildElement("first"); - if(!tag.isNull()) + if (!tag.isNull()) r.setFirst(tagContent(tag)); tag = i.firstChildElement("last"); - if(!tag.isNull()) + if (!tag.isNull()) r.setLast(tagContent(tag)); tag = i.firstChildElement("email"); - if(!tag.isNull()) + if (!tag.isNull()) r.setEmail(tagContent(tag)); d->resultList += r; - } - else if(i.tagName() == "x" && i.attribute("xmlns") == "jabber:x:data") { + } else if (i.tagName() == "x" && i.namespaceURI() == "jabber:x:data") { d->xdata.fromXml(i); d->hasXData = true; } } } setSuccess(); - } - else { + } else { setError(x); } return true; } - //---------------------------------------------------------------------------- // JT_ClientVersion //---------------------------------------------------------------------------- -JT_ClientVersion::JT_ClientVersion(Task *parent) -:Task(parent) -{ -} +JT_ClientVersion::JT_ClientVersion(Task *parent) : Task(parent) { } void JT_ClientVersion::get(const Jid &jid) { - j = jid; - iq = createIQ(doc(), "get", j.full(), id()); - QDomElement query = doc()->createElement("query"); - query.setAttribute("xmlns", "jabber:iq:version"); + j = jid; + iq = createIQ(doc(), "get", j.full(), id()); + QDomElement query = doc()->createElementNS("jabber:iq:version", "query"); iq.appendChild(query); } -void JT_ClientVersion::onGo() -{ - send(iq); -} +void JT_ClientVersion::onGo() { send(iq); } bool JT_ClientVersion::take(const QDomElement &x) { - if(!iqVerify(x, j, id())) + if (!iqVerify(x, j, id())) return false; - if(x.attribute("type") == "result") { + if (x.attribute("type") == "result") { QDomElement q = queryTag(x); QDomElement tag; tag = q.firstChildElement("name"); - if(!tag.isNull()) + if (!tag.isNull()) v_name = tagContent(tag); tag = q.firstChildElement("version"); - if(!tag.isNull()) + if (!tag.isNull()) v_ver = tagContent(tag); tag = q.firstChildElement("os"); - if(!tag.isNull()) + if (!tag.isNull()) v_os = tagContent(tag); setSuccess(); - } - else { + } else { setError(x); } return true; } -const Jid & JT_ClientVersion::jid() const -{ - return j; -} +const Jid &JT_ClientVersion::jid() const { return j; } -const QString & JT_ClientVersion::name() const -{ - return v_name; -} +const QString &JT_ClientVersion::name() const { return v_name; } -const QString & JT_ClientVersion::version() const -{ - return v_ver; -} - -const QString & JT_ClientVersion::os() const -{ - return v_os; -} +const QString &JT_ClientVersion::version() const { return v_ver; } +const QString &JT_ClientVersion::os() const { return v_os; } //---------------------------------------------------------------------------- // JT_EntityTime //---------------------------------------------------------------------------- -JT_EntityTime::JT_EntityTime(Task* parent) : Task(parent) -{ -} +JT_EntityTime::JT_EntityTime(Task *parent) : Task(parent) { } /** * \brief Queried entity's JID. */ -const Jid & JT_EntityTime::jid() const -{ - return j; -} +const Jid &JT_EntityTime::jid() const { return j; } /** * \brief Prepares the task to get information from JID. */ void JT_EntityTime::get(const Jid &jid) { - j = jid; - iq = createIQ(doc(), "get", jid.full(), id()); - QDomElement time = doc()->createElement("time"); - time.setAttribute("xmlns", "urn:xmpp:time"); + j = jid; + iq = createIQ(doc(), "get", jid.full(), id()); + QDomElement time = doc()->createElementNS("urn:xmpp:time", "time"); iq.appendChild(time); } -void JT_EntityTime::onGo() -{ - send(iq); -} +void JT_EntityTime::onGo() { send(iq); } bool JT_EntityTime::take(const QDomElement &x) { @@ -1482,72 +1369,55 @@ bool JT_EntityTime::take(const QDomElement &x) } setSuccess(); return true; - } - while (false); + } while (false); setError(406); - } - else { + } else { setError(x); } return true; } -const QDateTime & JT_EntityTime::dateTime() const -{ - return utc; -} - -int JT_EntityTime::timezoneOffset() const -{ - return tzo; -} +const QDateTime &JT_EntityTime::dateTime() const { return utc; } +int JT_EntityTime::timezoneOffset() const { return tzo; } //---------------------------------------------------------------------------- // JT_ServInfo //---------------------------------------------------------------------------- -JT_ServInfo::JT_ServInfo(Task *parent) -:Task(parent) -{ -} +JT_ServInfo::JT_ServInfo(Task *parent) : Task(parent) { } -JT_ServInfo::~JT_ServInfo() -{ -} +JT_ServInfo::~JT_ServInfo() { } bool JT_ServInfo::take(const QDomElement &e) { - if(e.tagName() != "iq" || e.attribute("type") != "get") + if (e.tagName() != "iq" || e.attribute("type") != "get") return false; QString ns = queryNS(e); - if(ns == "jabber:iq:version") { - QDomElement iq = createIQ(doc(), "result", e.attribute("from"), e.attribute("id")); - QDomElement query = doc()->createElement("query"); - query.setAttribute("xmlns", "jabber:iq:version"); + if (ns == "jabber:iq:version") { + QDomElement iq = createIQ(doc(), "result", e.attribute("from"), e.attribute("id")); + QDomElement query = doc()->createElementNS("jabber:iq:version", "query"); iq.appendChild(query); query.appendChild(textTag(doc(), "name", client()->clientName())); query.appendChild(textTag(doc(), "version", client()->clientVersion())); query.appendChild(textTag(doc(), "os", client()->OSName() + ' ' + client()->OSVersion())); send(iq); return true; - } - else if(ns == "http://jabber.org/protocol/disco#info") { + } else if (ns == "http://jabber.org/protocol/disco#info") { // Find out the node - QString node; + QString node; QDomElement q = e.firstChildElement("query"); - if(!q.isNull()) // NOTE: Should always be true, since a NS was found above + if (!q.isNull()) // NOTE: Should always be true, since a NS was found above node = q.attribute("node"); if (node.isEmpty() || node == client()->caps().flatten()) { - QDomElement iq = createIQ(doc(), "result", e.attribute("from"), e.attribute("id")); - DiscoItem item = client()->makeDiscoResult(node); + QDomElement iq = createIQ(doc(), "result", e.attribute("from"), e.attribute("id")); + DiscoItem item = client()->makeDiscoResult(node); iq.appendChild(item.toDiscoInfoResult(doc())); send(iq); - } - else { + } else { // Create error reply QDomElement error_reply = createIQ(doc(), "result", e.attribute("from"), e.attribute("id")); @@ -1558,10 +1428,9 @@ bool JT_ServInfo::take(const QDomElement &e) // Add error QDomElement error = doc()->createElement("error"); - error.setAttribute("type","cancel"); + error.setAttribute("type", "cancel"); error_reply.appendChild(error); - QDomElement error_type = doc()->createElement("item-not-found"); - error_type.setAttribute("xmlns","urn:ietf:params:xml:ns:xmpp-stanzas"); + QDomElement error_type = doc()->createElementNS("urn:ietf:params:xml:ns:xmpp-stanzas", "item-not-found"); error.appendChild(error_type); send(error_reply); } @@ -1571,17 +1440,16 @@ bool JT_ServInfo::take(const QDomElement &e) return false; } - ns = e.firstChildElement("time").attribute("xmlns"); + ns = e.firstChildElement("time").namespaceURI(); if (ns == "urn:xmpp:time") { - QDomElement iq = createIQ(doc(), "result", e.attribute("from"), e.attribute("id")); - QDomElement time = doc()->createElement("time"); - time.setAttribute("xmlns", ns); + QDomElement iq = createIQ(doc(), "result", e.attribute("from"), e.attribute("id")); + QDomElement time = doc()->createElementNS(ns, "time"); iq.appendChild(time); QDateTime local = QDateTime::currentDateTime(); - int off = TimeZone::offsetFromUtc(); - QTime t = QTime(0, 0).addSecs(qAbs(off)*60); + int off = TimeZone::offsetFromUtc(); + QTime t = QTime(0, 0).addSecs(qAbs(off) * 60); QString tzo = (off < 0 ? "-" : "+") + t.toString("HH:mm"); time.appendChild(textTag(doc(), "tzo", tzo)); QString localTimeStr = local.toUTC().toString(Qt::ISODate); @@ -1596,70 +1464,48 @@ bool JT_ServInfo::take(const QDomElement &e) return false; } - //---------------------------------------------------------------------------- // JT_Gateway //---------------------------------------------------------------------------- -JT_Gateway::JT_Gateway(Task *parent) -:Task(parent) -{ - type = -1; -} +JT_Gateway::JT_Gateway(Task *parent) : Task(parent) { type = -1; } void JT_Gateway::get(const Jid &jid) { - type = 0; - v_jid = jid; - iq = createIQ(doc(), "get", v_jid.full(), id()); - QDomElement query = doc()->createElement("query"); - query.setAttribute("xmlns", "jabber:iq:gateway"); + type = 0; + v_jid = jid; + iq = createIQ(doc(), "get", v_jid.full(), id()); + QDomElement query = doc()->createElementNS("jabber:iq:gateway", "query"); iq.appendChild(query); } void JT_Gateway::set(const Jid &jid, const QString &prompt) { - type = 1; - v_jid = jid; - v_prompt = prompt; - iq = createIQ(doc(), "set", v_jid.full(), id()); - QDomElement query = doc()->createElement("query"); - query.setAttribute("xmlns", "jabber:iq:gateway"); + type = 1; + v_jid = jid; + v_prompt = prompt; + iq = createIQ(doc(), "set", v_jid.full(), id()); + QDomElement query = doc()->createElementNS("jabber:iq:gateway", "query"); iq.appendChild(query); query.appendChild(textTag(doc(), "prompt", v_prompt)); } -void JT_Gateway::onGo() -{ - send(iq); -} +void JT_Gateway::onGo() { send(iq); } -Jid JT_Gateway::jid() const -{ - return v_jid; -} +Jid JT_Gateway::jid() const { return v_jid; } -QString JT_Gateway::desc() const -{ - return v_desc; -} +QString JT_Gateway::desc() const { return v_desc; } -QString JT_Gateway::prompt() const -{ - return v_prompt; -} +QString JT_Gateway::prompt() const { return v_prompt; } -Jid JT_Gateway::translatedJid() const -{ - return v_translatedJid; -} +Jid JT_Gateway::translatedJid() const { return v_translatedJid; } bool JT_Gateway::take(const QDomElement &x) { - if(!iqVerify(x, v_jid, id())) + if (!iqVerify(x, v_jid, id())) return false; - if(x.attribute("type") == "result") { - if(type == 0) { + if (x.attribute("type") == "result") { + if (type == 0) { QDomElement query = queryTag(x); QDomElement tag; tag = query.firstChildElement("desc"); @@ -1670,8 +1516,7 @@ bool JT_Gateway::take(const QDomElement &x) if (!tag.isNull()) { v_prompt = tagContent(tag); } - } - else { + } else { QDomElement query = queryTag(x); QDomElement tag; tag = query.firstChildElement("jid"); @@ -1687,8 +1532,7 @@ bool JT_Gateway::take(const QDomElement &x) } setSuccess(); - } - else { + } else { setError(x); } @@ -1698,43 +1542,31 @@ bool JT_Gateway::take(const QDomElement &x) //---------------------------------------------------------------------------- // JT_DiscoItems //---------------------------------------------------------------------------- -class JT_DiscoItems::Private -{ +class JT_DiscoItems::Private { public: Private() { } QDomElement iq; - Jid jid; - DiscoList items; + Jid jid; + DiscoList items; QDomElement subsetsEl; }; -JT_DiscoItems::JT_DiscoItems(Task *parent) -: Task(parent) -{ - d = new Private; -} +JT_DiscoItems::JT_DiscoItems(Task *parent) : Task(parent) { d = new Private; } -JT_DiscoItems::~JT_DiscoItems() -{ - delete d; -} +JT_DiscoItems::~JT_DiscoItems() { delete d; } -void JT_DiscoItems::get(const DiscoItem &item) -{ - get(item.jid(), item.node()); -} +void JT_DiscoItems::get(const DiscoItem &item) { get(item.jid(), item.node()); } -void JT_DiscoItems::get (const Jid &j, const QString &node) +void JT_DiscoItems::get(const Jid &j, const QString &node) { d->items.clear(); - d->jid = j; - d->iq = createIQ(doc(), "get", d->jid.full(), id()); - QDomElement query = doc()->createElement("query"); - query.setAttribute("xmlns", "http://jabber.org/protocol/disco#items"); + d->jid = j; + d->iq = createIQ(doc(), "get", d->jid.full(), id()); + QDomElement query = doc()->createElementNS("http://jabber.org/protocol/disco#items", "query"); - if ( !node.isEmpty() ) + if (!node.isEmpty()) query.setAttribute("node", node); if (!d->subsetsEl.isNull()) { @@ -1745,14 +1577,11 @@ void JT_DiscoItems::get (const Jid &j, const QString &node) d->iq.appendChild(query); } -const DiscoList &JT_DiscoItems::items() const -{ - return d->items; -} +const DiscoList &JT_DiscoItems::items() const { return d->items; } void JT_DiscoItems::includeSubsetQuery(const SubsetsClientManager &subsets) { - d->subsetsEl = subsets.makeQueryElement(doc()); + d->subsetsEl = subsets.makeQueryElement(doc()); } bool JT_DiscoItems::extractSubsetInfo(SubsetsClientManager &subsets) @@ -1760,42 +1589,37 @@ bool JT_DiscoItems::extractSubsetInfo(SubsetsClientManager &subsets) return d->subsetsEl.isNull() ? false : subsets.updateFromElement(d->subsetsEl, d->items.count()); } -void JT_DiscoItems::onGo () -{ - send(d->iq); -} +void JT_DiscoItems::onGo() { send(d->iq); } bool JT_DiscoItems::take(const QDomElement &x) { - if(!iqVerify(x, d->jid, id())) + if (!iqVerify(x, d->jid, id())) return false; - if(x.attribute("type") == "result") { + if (x.attribute("type") == "result") { QDomElement q = queryTag(x); - for(QDomNode n = q.firstChild(); !n.isNull(); n = n.nextSibling()) { + for (QDomNode n = q.firstChild(); !n.isNull(); n = n.nextSibling()) { QDomElement e = n.toElement(); - if( e.isNull() ) + if (e.isNull()) continue; - if ( e.tagName() == "item" ) { + if (e.tagName() == "item") { DiscoItem item; - item.setJid ( e.attribute("jid") ); - item.setName( e.attribute("name") ); - item.setNode( e.attribute("node") ); - item.setAction( DiscoItem::string2action(e.attribute("action")) ); + item.setJid(e.attribute("jid")); + item.setName(e.attribute("name")); + item.setNode(e.attribute("node")); + item.setAction(DiscoItem::string2action(e.attribute("action"))); - d->items.append( item ); - } - else if (d->subsetsEl.isNull()) { + d->items.append(item); + } else if (d->subsetsEl.isNull()) { d->subsetsEl = SubsetsClientManager::findElement(e, false); } } setSuccess(); - } - else { + } else { setError(x); } @@ -1805,86 +1629,67 @@ bool JT_DiscoItems::take(const QDomElement &x) //---------------------------------------------------------------------------- // JT_DiscoPublish //---------------------------------------------------------------------------- -class JT_DiscoPublish::Private -{ +class JT_DiscoPublish::Private { public: Private() { } QDomElement iq; - Jid jid; - DiscoList list; + Jid jid; + DiscoList list; }; -JT_DiscoPublish::JT_DiscoPublish(Task *parent) -: Task(parent) -{ - d = new Private; -} +JT_DiscoPublish::JT_DiscoPublish(Task *parent) : Task(parent) { d = new Private; } -JT_DiscoPublish::~JT_DiscoPublish() -{ - delete d; -} +JT_DiscoPublish::~JT_DiscoPublish() { delete d; } void JT_DiscoPublish::set(const Jid &j, const DiscoList &list) { d->list = list; - d->jid = j; + d->jid = j; - d->iq = createIQ(doc(), "set", d->jid.full(), id()); - QDomElement query = doc()->createElement("query"); - query.setAttribute("xmlns", "http://jabber.org/protocol/disco#items"); + d->iq = createIQ(doc(), "set", d->jid.full(), id()); + QDomElement query = doc()->createElementNS("http://jabber.org/protocol/disco#items", "query"); // FIXME: unsure about this - //if ( !node.isEmpty() ) + // if ( !node.isEmpty() ) // query.setAttribute("node", node); - DiscoList::ConstIterator it = list.begin(); - for ( ; it != list.end(); ++it) { + for (const auto &discoItem : list) { QDomElement w = doc()->createElement("item"); - w.setAttribute("jid", (*it).jid().full()); - if ( !(*it).name().isEmpty() ) - w.setAttribute("name", (*it).name()); - if ( !(*it).node().isEmpty() ) - w.setAttribute("node", (*it).node()); - w.setAttribute("action", DiscoItem::action2string((*it).action())); + w.setAttribute("jid", discoItem.jid().full()); + if (!discoItem.name().isEmpty()) + w.setAttribute("name", discoItem.name()); + if (!discoItem.node().isEmpty()) + w.setAttribute("node", discoItem.node()); + w.setAttribute("action", DiscoItem::action2string(discoItem.action())); - query.appendChild( w ); + query.appendChild(w); } d->iq.appendChild(query); } -void JT_DiscoPublish::onGo () -{ - send(d->iq); -} +void JT_DiscoPublish::onGo() { send(d->iq); } bool JT_DiscoPublish::take(const QDomElement &x) { - if(!iqVerify(x, d->jid, id())) + if (!iqVerify(x, d->jid, id())) return false; - if(x.attribute("type") == "result") { + if (x.attribute("type") == "result") { setSuccess(); - } - else { + } else { setError(x); } return true; } - // --------------------------------------------------------- // JT_BoBServer // --------------------------------------------------------- -JT_BoBServer::JT_BoBServer(Task *parent) - : Task(parent) -{ - -} +JT_BoBServer::JT_BoBServer(Task *parent) : Task(parent) { } bool JT_BoBServer::take(const QDomElement &e) { @@ -1892,17 +1697,14 @@ bool JT_BoBServer::take(const QDomElement &e) return false; QDomElement data = e.firstChildElement("data"); - if (data.attribute("xmlns") == "urn:xmpp:bob") { + if (data.namespaceURI() == "urn:xmpp:bob") { QDomElement iq; - BoBData bd = client()->bobManager()->bobData(data.attribute("cid")); + BoBData bd = client()->bobManager()->bobData(data.attribute("cid")); if (bd.isNull()) { - iq = createIQ(client()->doc(), "error", - e.attribute("from"), e.attribute("id")); - Stanza::Error error(Stanza::Error::Cancel, - Stanza::Error::ItemNotFound); + iq = createIQ(client()->doc(), "error", e.attribute("from"), e.attribute("id")); + Stanza::Error error(Stanza::Error::Cancel, Stanza::Error::ItemNotFound); iq.appendChild(error.toXml(*doc(), client()->stream().baseNS())); - } - else { + } else { iq = createIQ(doc(), "result", e.attribute("from"), e.attribute("id")); iq.appendChild(bd.toXml(doc())); } @@ -1912,31 +1714,22 @@ bool JT_BoBServer::take(const QDomElement &e) return false; } - //---------------------------------------------------------------------------- // JT_BitsOfBinary //---------------------------------------------------------------------------- -class JT_BitsOfBinary::Private -{ +class JT_BitsOfBinary::Private { public: Private() { } QDomElement iq; - Jid jid; - QString cid; - BoBData data; + Jid jid; + QString cid; + BoBData data; }; -JT_BitsOfBinary::JT_BitsOfBinary(Task *parent) -: Task(parent) -{ - d = new Private; -} +JT_BitsOfBinary::JT_BitsOfBinary(Task *parent) : Task(parent) { d = new Private; } -JT_BitsOfBinary::~JT_BitsOfBinary() -{ - delete d; -} +JT_BitsOfBinary::~JT_BitsOfBinary() { delete d; } void JT_BitsOfBinary::get(const Jid &j, const QString &cid) { @@ -1945,9 +1738,8 @@ void JT_BitsOfBinary::get(const Jid &j, const QString &cid) d->data = client()->bobManager()->bobData(cid); if (d->data.isNull()) { - d->iq = createIQ(doc(), "get", d->jid.full(), id()); - QDomElement data = doc()->createElement("data"); - data.setAttribute("xmlns", "urn:xmpp:bob"); + d->iq = createIQ(doc(), "get", d->jid.full(), id()); + QDomElement data = doc()->createElementNS("urn:xmpp:bob", "data"); data.setAttribute("cid", cid); d->iq.appendChild(data); } @@ -1957,8 +1749,7 @@ void JT_BitsOfBinary::onGo() { if (d->data.isNull()) { send(d->iq); - } - else { + } else { setSuccess(); } } @@ -1978,18 +1769,14 @@ bool JT_BitsOfBinary::take(const QDomElement &x) } setSuccess(); - } - else { + } else { setError(x); } return true; } -BoBData &JT_BitsOfBinary::data() -{ - return d->data; -} +BoBData &JT_BitsOfBinary::data() { return d->data; } //---------------------------------------------------------------------------- // JT_PongServer @@ -1999,11 +1786,7 @@ BoBData &JT_BitsOfBinary::data() * \brief Answers XMPP Pings */ -JT_PongServer::JT_PongServer(Task *parent) -:Task(parent) -{ - -} +JT_PongServer::JT_PongServer(Task *parent) : Task(parent) { } bool JT_PongServer::take(const QDomElement &e) { @@ -2011,7 +1794,7 @@ bool JT_PongServer::take(const QDomElement &e) return false; QDomElement ping = e.firstChildElement("ping"); - if (!e.isNull() && ping.attribute("xmlns") == "urn:xmpp:ping") { + if (!e.isNull() && ping.namespaceURI() == "urn:xmpp:ping") { QDomElement iq = createIQ(doc(), "result", e.attribute("from"), e.attribute("id")); send(iq); return true; @@ -2022,27 +1805,19 @@ bool JT_PongServer::take(const QDomElement &e) //--------------------------------------------------------------------------- // JT_CaptchaChallenger //--------------------------------------------------------------------------- -class JT_CaptchaChallenger::Private -{ +class JT_CaptchaChallenger::Private { public: - Jid j; + Jid j; CaptchaChallenge challenge; }; -JT_CaptchaChallenger::JT_CaptchaChallenger(Task *parent) : - Task(parent), - d(new Private) -{ -} +JT_CaptchaChallenger::JT_CaptchaChallenger(Task *parent) : Task(parent), d(new Private) { } -JT_CaptchaChallenger::~JT_CaptchaChallenger() -{ - delete d; -} +JT_CaptchaChallenger::~JT_CaptchaChallenger() { delete d; } void JT_CaptchaChallenger::set(const Jid &j, const CaptchaChallenge &c) { - d->j = j; + d->j = j; d->challenge = c; } @@ -2055,8 +1830,8 @@ void JT_CaptchaChallenger::onGo() m.setBody(d->challenge.explanation()); m.setUrlList(d->challenge.urls()); - XData form = d->challenge.form(); - XData::FieldList fl = form.fields(); + XData form = d->challenge.form(); + XData::FieldList fl = form.fields(); XData::FieldList::Iterator it; for (it = fl.begin(); it < fl.end(); ++it) { if (it->var() == "challenge" && it->type() == XData::Field::Field_Hidden) { @@ -2079,27 +1854,24 @@ void JT_CaptchaChallenger::onGo() bool JT_CaptchaChallenger::take(const QDomElement &x) { - if(x.tagName() == "message" && x.attribute("id") == id() && - Jid(x.attribute("from")) == d->j && !x.firstChildElement("error").isNull()) - { + if (x.tagName() == "message" && x.attribute("id") == id() && Jid(x.attribute("from")) == d->j + && !x.firstChildElement("error").isNull()) { setError(x); return true; } XDomNodeList nl; - XData xd; - QString rid = x.attribute("id"); - if (rid.isEmpty() || x.tagName() != "iq" || - Jid(x.attribute("from")) != d->j || x.attribute("type") != "set" || - (nl = childElementsByTagNameNS(x, "urn:xmpp:captcha", "captcha")).isEmpty() || - (nl = childElementsByTagNameNS(nl.item(0).toElement(), "jabber:x:data", "x")).isEmpty() || - (xd.fromXml(nl.item(0).toElement()), xd.getField("challenge").value().value(0) != id())) - { + XData xd; + QString rid = x.attribute("id"); + if (rid.isEmpty() || x.tagName() != "iq" || Jid(x.attribute("from")) != d->j || x.attribute("type") != "set" + || (nl = childElementsByTagNameNS(x, "urn:xmpp:captcha", "captcha")).isEmpty() + || (nl = childElementsByTagNameNS(nl.item(0).toElement(), "jabber:x:data", "x")).isEmpty() + || (xd.fromXml(nl.item(0).toElement()), xd.getField("challenge").value().value(0) != id())) { return false; } CaptchaChallenge::Result r = d->challenge.validateResponse(xd); - QDomElement iq; + QDomElement iq; if (r == CaptchaChallenge::Passed) { iq = createIQ(doc(), "result", d->j.full(), rid); } else { @@ -2120,27 +1892,20 @@ bool JT_CaptchaChallenger::take(const QDomElement &x) return true; } - //--------------------------------------------------------------------------- // JT_CaptchaSender //--------------------------------------------------------------------------- -JT_CaptchaSender::JT_CaptchaSender(Task *parent) : - Task(parent) -{} +JT_CaptchaSender::JT_CaptchaSender(Task *parent) : Task(parent) { } void JT_CaptchaSender::set(const Jid &j, const XData &xd) { to = j; iq = createIQ(doc(), "set", to.full(), id()); - iq.appendChild(doc()->createElementNS("urn:xmpp:captcha", "captcha")) - .appendChild(xd.toXml(doc(), true)); + iq.appendChild(doc()->createElementNS("urn:xmpp:captcha", "captcha")).appendChild(xd.toXml(doc(), true)); } -void JT_CaptchaSender::onGo() -{ - send(iq); -} +void JT_CaptchaSender::onGo() { send(iq); } bool JT_CaptchaSender::take(const QDomElement &x) { @@ -2150,10 +1915,47 @@ bool JT_CaptchaSender::take(const QDomElement &x) if (x.attribute("type") == "result") { setSuccess(); - } - else { + } else { setError(x); } return true; } + +//---------------------------------------------------------------------------- +// JT_MessageCarbons +//---------------------------------------------------------------------------- +JT_MessageCarbons::JT_MessageCarbons(Task *parent) : Task(parent) { } + +void JT_MessageCarbons::enable() +{ + _iq = createIQ(doc(), "set", "", id()); + + QDomElement enable = doc()->createElementNS("urn:xmpp:carbons:2", "enable"); + + _iq.appendChild(enable); +} + +void JT_MessageCarbons::disable() +{ + _iq = createIQ(doc(), "set", "", id()); + + QDomElement disable = doc()->createElementNS("urn:xmpp:carbons:2", "disable"); + + _iq.appendChild(disable); +} + +void JT_MessageCarbons::onGo() +{ + send(_iq); + setSuccess(); +} + +bool JT_MessageCarbons::take(const QDomElement &e) +{ + if (e.tagName() != "iq" || e.attribute("type") != "result") + return false; + + bool res = iqVerify(e, Jid(), id()); + return res; +} diff --git a/src/xmpp/xmpp-im/xmpp_tasks.h b/src/xmpp/xmpp-im/xmpp_tasks.h index 036e7fe1..da3ecdec 100644 --- a/src/xmpp/xmpp-im/xmpp_tasks.h +++ b/src/xmpp/xmpp-im/xmpp_tasks.h @@ -1,6 +1,6 @@ /* * tasks.h - basic tasks - * Copyright (C) 2001, 2002 Justin Karneges + * Copyright (C) 2001-2002 Justin Karneges * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -17,459 +17,441 @@ * */ -#ifndef JABBER_TASKS_H -#define JABBER_TASKS_H +#ifndef XMPP_TASKS_H +#define XMPP_TASKS_H -#include -#include -#include - -#include "im.h" -#include "xmpp_vcard.h" #include "xmpp_discoinfotask.h" -#include "xmpp_subsets.h" #include "xmpp_encryptionhandler.h" +#include "xmpp_form.h" +#include "xmpp_message.h" +#include "xmpp_subsets.h" +#include "xmpp_vcard.h" -namespace XMPP -{ - class Roster; - class Status; - class Client; - class BoBData; - class CaptchaChallenge; - - class JT_Register : public Task - { - Q_OBJECT - public: - JT_Register(Task *parent); - ~JT_Register(); - - // OLd style registration - void reg(const QString &user, const QString &pass); - - void changepw(const QString &pass); - void unreg(const Jid &j=""); - - const Form & form() const; - bool hasXData() const; - const XData& xdata() const; - bool isRegistered() const; - void getForm(const Jid &); - void setForm(const Form &); - void setForm(const Jid&, const XData &); - - void onGo(); - bool take(const QDomElement &); - - private: - QDomElement iq; - Jid to; - - class Private; - Private *d = nullptr; - }; - - class JT_UnRegister : public Task - { - Q_OBJECT - public: - JT_UnRegister(Task *parent); - ~JT_UnRegister(); - - void unreg(const Jid &); - - void onGo(); - - private slots: - void getFormFinished(); - void unregFinished(); - - private: - class Private; - Private *d = nullptr; - }; - - class JT_Roster : public Task - { - Q_OBJECT - public: - JT_Roster(Task *parent); - ~JT_Roster(); - - void get(); - void set(const Jid &, const QString &name, const QStringList &groups); - void remove(const Jid &); - - const Roster & roster() const; - - QString toString() const; - bool fromString(const QString &); - - void onGo(); - bool take(const QDomElement &x); - - private: - int type; - QDomElement iq; - Jid to; - - class Private; - Private *d = nullptr; - }; - - class JT_PushRoster : public Task - { - Q_OBJECT - public: - JT_PushRoster(Task *parent); - ~JT_PushRoster(); - - bool take(const QDomElement &); - - signals: - void roster(const Roster &); - - private: - class Private; - Private *d = nullptr; - }; - - class JT_Presence : public Task - { - Q_OBJECT - public: - JT_Presence(Task *parent); - ~JT_Presence(); - - void pres(const Status &); - void pres(const Jid &, const Status &); - void sub(const Jid &, const QString &subType, const QString& nick = QString()); - void probe(const Jid &to); - - void onGo(); - - private: - QDomElement tag; - int type = -1; - - class Private; - Private *d = nullptr; - }; - - class JT_PushPresence : public Task - { - Q_OBJECT - public: - JT_PushPresence(Task *parent); - ~JT_PushPresence(); - - bool take(const QDomElement &); - - signals: - void presence(const Jid &, const Status &); - void subscription(const Jid &, const QString &, const QString&); - - private: - class Private; - Private *d = nullptr; - }; - - class JT_Session : public Task - { - public: - JT_Session(Task *parent); - void onGo(); - bool take(const QDomElement&); - }; - - class JT_Message : public Task - { - Q_OBJECT - public: - JT_Message(Task *parent, Message &); - ~JT_Message(); - - void onGo(); - - private: - Message m; - - class Private; - Private *d = nullptr; - }; - - class JT_PushMessage : public Task - { - Q_OBJECT - public: - class Subscriber - { - public: - virtual ~Subscriber(); - virtual bool xmlEvent(const QDomElement &root, QDomElement &e, Client *c, int userData, bool nested); - virtual bool messageEvent(Message &msg, int userData, bool nested); - }; - - JT_PushMessage(Task *parent, EncryptionHandler *encryptionHandler = nullptr); - ~JT_PushMessage(); - - void subscribeXml(Subscriber *sbs, const QString &tagName, const QString &xmlnsStr, int userData); - void unsubscribeXml(Subscriber *sbs, const QString &tagName, const QString &xmlnsStr); - void subscribeMessage(Subscriber *sbs, int userData); - void unsubscribeMessage(Subscriber *sbs); - bool processXmlSubscribers(QDomElement &el, Client *client, bool nested); - bool processMessageSubscribers(Message &msg, bool nested); - - bool take(const QDomElement &); - - signals: - void message(const Message &); - - private: - class Private; - std::unique_ptr d; - }; - - class JT_VCard : public Task - { - Q_OBJECT - public: - JT_VCard(Task *parent); - ~JT_VCard(); - - void get(const Jid &); - void set(const VCard &); - void set(const Jid &, const VCard &, bool isTarget = false); - - const Jid & jid() const; - const VCard & vcard() const; - - void onGo(); - bool take(const QDomElement &x); - - private: - int type; - - class Private; - Private *d = nullptr; - }; - - class JT_Search : public Task - { - Q_OBJECT - public: - JT_Search(Task *parent); - ~JT_Search(); - - const Form & form() const; - const QList & results() const; - - bool hasXData() const; - const XData & xdata() const; +#include +#include +#include - void get(const Jid &); - void set(const Form &); - void set(const Jid &, const XData &); +namespace XMPP { +class BoBData; +class CaptchaChallenge; +class Roster; +class Status; - void onGo(); - bool take(const QDomElement &x); +class JT_Register : public Task { + Q_OBJECT +public: + JT_Register(Task *parent); + ~JT_Register(); - private: - QDomElement iq; - int type; + // OLd style registration + void reg(const QString &user, const QString &pass); + + void changepw(const QString &pass); + void unreg(const Jid &j = ""); - class Private; - Private *d = nullptr; - }; + const Form &form() const; + bool hasXData() const; + const XData &xdata() const; + bool isRegistered() const; + void getForm(const Jid &); + void setForm(const Form &); + void setForm(const Jid &, const XData &); - class JT_ClientVersion : public Task - { - Q_OBJECT - public: - JT_ClientVersion(Task *); + void onGo(); + bool take(const QDomElement &); - void get(const Jid &); - void onGo(); - bool take(const QDomElement &); +private: + QDomElement iq; + Jid to; - const Jid & jid() const; - const QString & name() const; - const QString & version() const; - const QString & os() const; + class Private; + Private *d = nullptr; +}; - private: - QDomElement iq; +class JT_UnRegister : public Task { + Q_OBJECT +public: + JT_UnRegister(Task *parent); + ~JT_UnRegister(); + + void unreg(const Jid &); - Jid j; - QString v_name, v_ver, v_os; - }; + void onGo(); + +private slots: + void getFormFinished(); + void unregFinished(); + +private: + class Private; + Private *d = nullptr; +}; + +class JT_Roster : public Task { + Q_OBJECT +public: + JT_Roster(Task *parent); + ~JT_Roster(); - class JT_EntityTime : public Task - { - public: - JT_EntityTime(Task*); + void get(); + void set(const Jid &, const QString &name, const QStringList &groups); + void remove(const Jid &); - void onGo(); - bool take(const QDomElement &); - void get(const XMPP::Jid &j); - const XMPP::Jid & jid() const; + // XEP-0083 + void getGroupsDelimiter(); + void setGroupsDelimiter(const QString &groupsDelimiter); + + const Roster &roster() const; + QString groupsDelimiter() const; + + QString toString() const; + bool fromString(const QString &); + + void onGo(); + bool take(const QDomElement &x); + +private: + enum Type { Get, Set, Remove, GetDelimiter, SetDelimiter, Unknown = -1 }; + + int type; + QDomElement iq; + Jid to; + + class Private; + Private *d = nullptr; +}; + +class JT_PushRoster : public Task { + Q_OBJECT +public: + JT_PushRoster(Task *parent); + ~JT_PushRoster(); + + bool take(const QDomElement &); + +signals: + void roster(const Roster &); + +private: + class Private; + Private *d = nullptr; +}; + +class JT_Presence : public Task { + Q_OBJECT +public: + JT_Presence(Task *parent); + ~JT_Presence(); - const QDateTime & dateTime() const; - int timezoneOffset() const; + void pres(const Status &); + void pres(const Jid &, const Status &); + void sub(const Jid &, const QString &subType, const QString &nick = QString()); + void probe(const Jid &to); + + void onGo(); + +private: + QDomElement tag; + int type = -1; + + class Private; + Private *d = nullptr; +}; + +class JT_PushPresence : public Task { + Q_OBJECT +public: + JT_PushPresence(Task *parent); + ~JT_PushPresence(); - private: - QDomElement iq; - XMPP::Jid j; - QDateTime utc; - int tzo = 0; - }; + bool take(const QDomElement &); + +signals: + void presence(const Jid &, const Status &); + void subscription(const Jid &, const QString &, const QString &); + +private: + class Private; + Private *d = nullptr; +}; - class JT_ServInfo : public Task - { - Q_OBJECT - public: - JT_ServInfo(Task *); - ~JT_ServInfo(); +class JT_Session : public Task { +public: + JT_Session(Task *parent); + void onGo(); + bool take(const QDomElement &); +}; + +class JT_Message : public Task { + Q_OBJECT +public: + JT_Message(Task *parent, Message &); + ~JT_Message(); + + void onGo(); + +private: + Message m; - bool take(const QDomElement &); - }; + class Private; + Private *d = nullptr; +}; - class JT_Gateway : public Task - { - Q_OBJECT +class JT_PushMessage : public Task { + Q_OBJECT +public: + class Subscriber { public: - JT_Gateway(Task *); - - void get(const Jid &); - void set(const Jid &, const QString &prompt); - void onGo(); - bool take(const QDomElement &); - - Jid jid() const; - - QString desc() const; - QString prompt() const; - Jid translatedJid() const; - - private: - QDomElement iq; - - int type; - Jid v_jid; - Jid v_translatedJid; - QString v_prompt, v_desc; + virtual ~Subscriber(); + virtual bool xmlEvent(const QDomElement &root, QDomElement &e, Client *c, int userData, bool nested); + virtual bool messageEvent(Message &msg, int userData, bool nested); }; + JT_PushMessage(Task *parent, EncryptionHandler *encryptionHandler = nullptr); + ~JT_PushMessage(); - class JT_DiscoItems : public Task - { - Q_OBJECT - public: - JT_DiscoItems(Task *); - ~JT_DiscoItems(); + void subscribeXml(Subscriber *sbs, const QString &tagName, const QString &xmlnsStr, int userData); + void unsubscribeXml(Subscriber *sbs, const QString &tagName, const QString &xmlnsStr); + void subscribeMessage(Subscriber *sbs, int userData); + void unsubscribeMessage(Subscriber *sbs); + bool processXmlSubscribers(QDomElement &el, Client *client, bool nested); + bool processMessageSubscribers(Message &msg, bool nested); - void get(const Jid &, const QString &node = QString::null); - void get(const DiscoItem &); + bool take(const QDomElement &); + +signals: + void message(const Message &); + +private: + class Private; + std::unique_ptr d; +}; + +class JT_VCard : public Task { + Q_OBJECT +public: + JT_VCard(Task *parent); + ~JT_VCard(); + + void get(const Jid &); + void set(const VCard &); + void set(const Jid &, const VCard &, bool isTarget = false); + + const Jid &jid() const; + const VCard &vcard() const; + + void onGo(); + bool take(const QDomElement &x); + +private: + int type; - const DiscoList &items() const; + class Private; + Private *d = nullptr; +}; + +class JT_Search : public Task { + Q_OBJECT +public: + JT_Search(Task *parent); + ~JT_Search(); + + const Form &form() const; + const QList &results() const; + + bool hasXData() const; + const XData &xdata() const; - void includeSubsetQuery(const SubsetsClientManager &); - bool extractSubsetInfo(SubsetsClientManager &); + void get(const Jid &); + void set(const Form &); + void set(const Jid &, const XData &); - void onGo(); - bool take(const QDomElement &); + void onGo(); + bool take(const QDomElement &x); - private: - class Private; - Private *d = nullptr; - }; +private: + QDomElement iq; + int type; - class JT_DiscoPublish : public Task - { - Q_OBJECT - public: - JT_DiscoPublish(Task *); - ~JT_DiscoPublish(); + class Private; + Private *d = nullptr; +}; + +class JT_ClientVersion : public Task { + Q_OBJECT +public: + JT_ClientVersion(Task *); - void set(const Jid &, const DiscoList &); + void get(const Jid &); + void onGo(); + bool take(const QDomElement &); - void onGo(); - bool take(const QDomElement &); + const Jid &jid() const; + const QString &name() const; + const QString &version() const; + const QString &os() const; + +private: + QDomElement iq; - private: - class Private; - Private *d = nullptr; - }; + Jid j; + QString v_name, v_ver, v_os; +}; - class JT_BoBServer : public Task - { - Q_OBJECT +class JT_EntityTime : public Task { +public: + JT_EntityTime(Task *); + + void onGo(); + bool take(const QDomElement &); + void get(const XMPP::Jid &j); + const XMPP::Jid &jid() const; + + const QDateTime &dateTime() const; + int timezoneOffset() const; - public: - JT_BoBServer(Task *parent); - bool take(const QDomElement &); - }; +private: + QDomElement iq; + XMPP::Jid j; + QDateTime utc; + int tzo = 0; +}; - class JT_BitsOfBinary : public Task - { - Q_OBJECT - public: - JT_BitsOfBinary(Task *); - ~JT_BitsOfBinary(); +class JT_ServInfo : public Task { + Q_OBJECT +public: + JT_ServInfo(Task *); + ~JT_ServInfo(); + + bool take(const QDomElement &); +}; + +class JT_Gateway : public Task { + Q_OBJECT +public: + JT_Gateway(Task *); - void get(const Jid &, const QString &); + void get(const Jid &); + void set(const Jid &, const QString &prompt); + void onGo(); + bool take(const QDomElement &); - void onGo(); - bool take(const QDomElement &); - BoBData &data(); + Jid jid() const; + + QString desc() const; + QString prompt() const; + Jid translatedJid() const; + +private: + QDomElement iq; + + int type; + Jid v_jid; + Jid v_translatedJid; + QString v_prompt, v_desc; +}; + +class JT_DiscoItems : public Task { + Q_OBJECT +public: + JT_DiscoItems(Task *); + ~JT_DiscoItems(); + + void get(const Jid &, const QString &node = QString()); + void get(const DiscoItem &); + + const DiscoList &items() const; - private: - class Private; - Private *d = nullptr; - }; + void includeSubsetQuery(const SubsetsClientManager &); + bool extractSubsetInfo(SubsetsClientManager &); + + void onGo(); + bool take(const QDomElement &); + +private: + class Private; + Private *d = nullptr; +}; + +class JT_DiscoPublish : public Task { + Q_OBJECT +public: + JT_DiscoPublish(Task *); + ~JT_DiscoPublish(); + + void set(const Jid &, const DiscoList &); + + void onGo(); + bool take(const QDomElement &); + +private: + class Private; + Private *d = nullptr; +}; + +class JT_BoBServer : public Task { + Q_OBJECT + +public: + JT_BoBServer(Task *parent); + bool take(const QDomElement &); +}; + +class JT_BitsOfBinary : public Task { + Q_OBJECT +public: + JT_BitsOfBinary(Task *); + ~JT_BitsOfBinary(); + + void get(const Jid &, const QString &); + + void onGo(); + bool take(const QDomElement &); + BoBData &data(); + +private: + class Private; + Private *d = nullptr; +}; - class JT_PongServer : public Task - { - Q_OBJECT - public: - JT_PongServer(Task *); - bool take(const QDomElement &); - }; +class JT_PongServer : public Task { + Q_OBJECT +public: + JT_PongServer(Task *); + bool take(const QDomElement &); +}; - class JT_CaptchaChallenger : public Task - { - Q_OBJECT - public: - const static int CaptchaValidTimeout = 120; +class JT_CaptchaChallenger : public Task { + Q_OBJECT +public: + const static int CaptchaValidTimeout = 120; - JT_CaptchaChallenger(Task *); - ~JT_CaptchaChallenger(); + JT_CaptchaChallenger(Task *); + ~JT_CaptchaChallenger(); - void set(const Jid &, const CaptchaChallenge &); + void set(const Jid &, const CaptchaChallenge &); - void onGo(); - bool take(const QDomElement &); + void onGo(); + bool take(const QDomElement &); - private: - class Private; - Private *d = nullptr; - }; +private: + class Private; + Private *d = nullptr; +}; - class JT_CaptchaSender : public Task - { - Q_OBJECT - public: - JT_CaptchaSender(Task *); +class JT_CaptchaSender : public Task { + Q_OBJECT +public: + JT_CaptchaSender(Task *); - void set(const Jid &, const XData &); + void set(const Jid &, const XData &); - void onGo(); - bool take(const QDomElement &); + void onGo(); + bool take(const QDomElement &); - private: - Jid to; - QDomElement iq; - }; -} +private: + Jid to; + QDomElement iq; +}; +} // namespace XMPP -#endif +#endif // XMPP_TASKS_H diff --git a/src/xmpp/xmpp-im/xmpp_thumbs.h b/src/xmpp/xmpp-im/xmpp_thumbs.h index 268c414d..0fe02585 100644 --- a/src/xmpp/xmpp-im/xmpp_thumbs.h +++ b/src/xmpp/xmpp-im/xmpp_thumbs.h @@ -19,35 +19,32 @@ #ifndef XMPP_THUMBS_H #define XMPP_THUMBS_H +#include #include #include -#include #define XMPP_THUMBS_NS "urn:xmpp:thumbs:1" // TODO make nsdb.cpp/h with static declarations of all ns -namespace XMPP -{ - class Thumbnail +namespace XMPP { +class Thumbnail { +public: + inline Thumbnail() : width(0), height(0) { } + // data - for outgoing it's actual image data. for incoming - cid + inline Thumbnail(const QByteArray &data, const QString &mimeType = QString(), quint32 width = 0, + quint32 height = 0) : data(data), mimeType(mimeType), width(width), height(height) { - public: - inline Thumbnail() : width(0), height(0) {} - // data - for outgoing it's actual image data. for incoming - cid - inline Thumbnail(const QByteArray &data, - const QString &mimeType = QString(), - quint32 width = 0, quint32 height = 0) : - data(data), mimeType(mimeType), - width(width), height(height) { } - Thumbnail(const QDomElement &el); + } + Thumbnail(const QDomElement &el); - inline bool isValid() const { return uri.isValid(); } - QDomElement toXml(QDomDocument *doc) const; + inline bool isValid() const { return uri.isValid(); } + QDomElement toXml(QDomDocument *doc) const; - QUrl uri; - QByteArray data; - QString mimeType; - quint32 width; - quint32 height; - }; -} + QUrl uri; + QByteArray data; + QString mimeType; + quint32 width; + quint32 height; +}; +} // namespace XMPP -#endif +#endif // XMPP_THUMBS_H diff --git a/src/xmpp/xmpp-im/xmpp_url.h b/src/xmpp/xmpp-im/xmpp_url.h index 315e6d28..bee60f2a 100644 --- a/src/xmpp/xmpp-im/xmpp_url.h +++ b/src/xmpp/xmpp-im/xmpp_url.h @@ -21,28 +21,26 @@ class QString; -namespace XMPP -{ - class Url - { - public: - Url(const QString &url="", const QString &desc=""); - Url(const Url &); - Url & operator=(const Url &); - ~Url(); - - QString url() const; - QString desc() const; - - void setUrl(const QString &); - void setDesc(const QString &); - - private: - class Private; - Private *d; - }; - - typedef QList UrlList; -} - -#endif +namespace XMPP { +class Url { +public: + Url(const QString &url = "", const QString &desc = ""); + Url(const Url &); + Url &operator=(const Url &); + ~Url(); + + QString url() const; + QString desc() const; + + void setUrl(const QString &); + void setDesc(const QString &); + +private: + class Private; + Private *d; +}; + +typedef QList UrlList; +} // namespace XMPP + +#endif // XMPP_URL diff --git a/src/xmpp/xmpp-im/xmpp_vcard.cpp b/src/xmpp/xmpp-im/xmpp_vcard.cpp index 19851e2c..3f3d509a 100644 --- a/src/xmpp/xmpp-im/xmpp_vcard.cpp +++ b/src/xmpp/xmpp-im/xmpp_vcard.cpp @@ -19,52 +19,51 @@ #include "xmpp_vcard.h" -#include +#include "xmpp_xmlcommon.h" + +#include #include #include // needed for image format recognition -#include #include #include #include #include - -#include "xmpp_xmlcommon.h" +#include using namespace XMLHelper; //---------------------------------------------------------------------------- -// VCard +// vCard //---------------------------------------------------------------------------- QString openedImage2type(QIODevice *dev) { - QString format = QImageReader::imageFormat( dev ).toUpper(); + QString format = QImageReader::imageFormat(dev).toUpper(); // TODO: add more formats: PBM PGM PPM XBM XPM - if ( format == QLatin1String("PNG") || format == QLatin1String("PSIPNG") ) // PsiPNG in normal case + if (format == QLatin1String("PNG") || format == QLatin1String("PSIPNG")) // PsiPNG in normal case return QLatin1String("image/png"); - if ( format == QLatin1String("MNG") ) + if (format == QLatin1String("MNG")) return QLatin1String("video/x-mng"); - if ( format == QLatin1String("GIF") ) + if (format == QLatin1String("GIF")) return QLatin1String("image/gif"); - if ( format == QLatin1String("JPEG")) + if (format == QLatin1String("JPEG")) return QLatin1String("image/jpeg"); - if ( format == QLatin1String("BMP") ) + if (format == QLatin1String("BMP")) return QLatin1String("image/bmp"); - if ( format == QLatin1String("WEBP") ) + if (format == QLatin1String("WEBP")) return QLatin1String("image/webp"); - if ( format == QLatin1String("XPM") ) + if (format == QLatin1String("XPM")) return QLatin1String("image/x-xpm"); - if ( format == QLatin1String("SVG") ) + if (format == QLatin1String("SVG")) return QLatin1String("image/svg+xml"); - - qWarning() << QString("WARNING! VCard::image2type: unknown format = '%1'").arg(format.isNull() ? QString("UNKNOWN") : format); - - return QLatin1String("image/unknown"); + return QString(); } QString image2type(const QByteArray &ba) { + if (ba.isEmpty()) + return QString(); QBuffer buf; buf.setData(ba); buf.open(QIODevice::ReadOnly); @@ -72,14 +71,13 @@ QString image2type(const QByteArray &ba) } namespace XMPP { - // Long lines of encoded binary data SHOULD BE folded to 75 characters using the folding method defined in [MIME-DIR]. static QString foldString(const QString &s) { QString ret; for (int i = 0; i < (int)s.length(); i++) { - if ( !(i % 75) ) + if (!(i % 75)) ret += '\n'; ret += s[i]; } @@ -87,14 +85,13 @@ static QString foldString(const QString &s) return ret; } -class VCardPrivate : public QSharedData -{ +class VCardPrivate : public QSharedData { public: VCardPrivate(); ~VCardPrivate(); // do we need copy constructor? - //VCardPrivate(const VCardPrivate &other) : + // VCardPrivate(const VCardPrivate &other) : // QSharedData(other), version(other.version), fullName(other.fullName) { qDebug("Copy VCardPrivate"); } QString version; @@ -103,412 +100,363 @@ class VCardPrivate : public QSharedData QString nickName; QByteArray photo; - QString photoURI; + QString photoURI; - QString bday; + QString bday; VCard::AddressList addressList; - VCard::LabelList labelList; - VCard::PhoneList phoneList; - VCard::EmailList emailList; - QString jid; - QString mailer; - QString timezone; - VCard::Geo geo; - QString title; - QString role; + VCard::LabelList labelList; + VCard::PhoneList phoneList; + VCard::EmailList emailList; + QString jid; + QString mailer; + QString timezone; + VCard::Geo geo; + QString title; + QString role; QByteArray logo; - QString logoURI; + QString logoURI; QSharedPointer agent; - QString agentURI; + QString agentURI; - VCard::Org org; + VCard::Org org; QStringList categories; - QString note; - QString prodId; - QString rev; - QString sortString; + QString note; + QString prodId; + QString rev; + QString sortString; QByteArray sound; - QString soundURI, soundPhonetic; + QString soundURI, soundPhonetic; - QString uid; - QString url; - QString desc; + QString uid; + QString url; + QString desc; VCard::PrivacyClass privacyClass; - QByteArray key; + QByteArray key; bool isEmpty() const; }; -VCardPrivate::VCardPrivate() -{ - privacyClass = VCard::pcNone; -} - -VCardPrivate::~VCardPrivate() -{ +VCardPrivate::VCardPrivate() { privacyClass = VCard::pcNone; } -} +VCardPrivate::~VCardPrivate() { } bool VCardPrivate::isEmpty() const { - if ( !version.isEmpty() || - !fullName.isEmpty() || - !familyName.isEmpty() || !givenName.isEmpty() || !middleName.isEmpty() || !prefixName.isEmpty() || !suffixName.isEmpty() || - !nickName.isEmpty() || - !photo.isEmpty() || !photoURI.isEmpty() || - !bday.isEmpty() || - !addressList.isEmpty() || - !labelList.isEmpty() || - !phoneList.isEmpty() || - !emailList.isEmpty() || - !jid.isEmpty() || - !mailer.isEmpty() || - !timezone.isEmpty() || - !geo.lat.isEmpty() || !geo.lon.isEmpty() || - !title.isEmpty() || - !role.isEmpty() || - !logo.isEmpty() || !logoURI.isEmpty() || - (agent && !agent->isEmpty()) || !agentURI.isEmpty() || - !org.name.isEmpty() || !org.unit.isEmpty() || - !categories.isEmpty() || - !note.isEmpty() || - !prodId.isEmpty() || - !rev.isEmpty() || - !sortString.isEmpty() || - !sound.isEmpty() || !soundURI.isEmpty() || !soundPhonetic.isEmpty() || - !uid.isEmpty() || - !url.isEmpty() || - !desc.isEmpty() || - (privacyClass != VCard::pcNone) || - !key.isEmpty() ) - { - return false; - } - return true; + return !(!version.isEmpty() || !fullName.isEmpty() || !familyName.isEmpty() || !givenName.isEmpty() + || !middleName.isEmpty() || !prefixName.isEmpty() || !suffixName.isEmpty() || !nickName.isEmpty() + || !photo.isEmpty() || !photoURI.isEmpty() || !bday.isEmpty() || !addressList.isEmpty() + || !labelList.isEmpty() || !phoneList.isEmpty() || !emailList.isEmpty() || !jid.isEmpty() + || !mailer.isEmpty() || !timezone.isEmpty() || !geo.lat.isEmpty() || !geo.lon.isEmpty() || !title.isEmpty() + || !role.isEmpty() || !logo.isEmpty() || !logoURI.isEmpty() || (agent && !agent->isEmpty()) + || !agentURI.isEmpty() || !org.name.isEmpty() || !org.unit.isEmpty() || !categories.isEmpty() + || !note.isEmpty() || !prodId.isEmpty() || !rev.isEmpty() || !sortString.isEmpty() || !sound.isEmpty() + || !soundURI.isEmpty() || !soundPhonetic.isEmpty() || !uid.isEmpty() || !url.isEmpty() || !desc.isEmpty() + || (privacyClass != VCard::pcNone) || !key.isEmpty()); } -VCard::VCard() -{ -} +VCard::VCard() { } -VCard::VCard(const VCard &from) : - d(from.d) -{ -} +VCard::VCard(const VCard &from) : d(from.d) { } -VCard & VCard::operator=(const VCard &from) +VCard &VCard::operator=(const VCard &from) { d = from.d; return *this; } -VCard::~VCard() -{ -} +VCard::~VCard() { } QDomElement VCard::toXml(QDomDocument *doc) const { - QDomElement v = doc->createElement("vCard"); - v.setAttribute("xmlns", "vcard-temp"); + QDomElement v = doc->createElementNS("vcard-temp", "vCard"); - if ( !d->version.isEmpty() ) - v.appendChild( textTag(doc, "VERSION", d->version) ); - if ( !d->fullName.isEmpty() ) - v.appendChild( textTag(doc, "FN", d->fullName) ); + if (!d->version.isEmpty()) + v.appendChild(textTag(doc, "VERSION", d->version)); + if (!d->fullName.isEmpty()) + v.appendChild(textTag(doc, "FN", d->fullName)); - if ( !d->familyName.isEmpty() || !d->givenName.isEmpty() || !d->middleName.isEmpty() || - !d->prefixName.isEmpty() || !d->suffixName.isEmpty() ) { + if (!d->familyName.isEmpty() || !d->givenName.isEmpty() || !d->middleName.isEmpty() || !d->prefixName.isEmpty() + || !d->suffixName.isEmpty()) { QDomElement w = doc->createElement("N"); - if ( !d->familyName.isEmpty() ) - w.appendChild( textTag(doc, "FAMILY", d->familyName) ); - if ( !d->givenName.isEmpty() ) - w.appendChild( textTag(doc, "GIVEN", d->givenName) ); - if ( !d->middleName.isEmpty() ) - w.appendChild( textTag(doc, "MIDDLE", d->middleName) ); - if ( !d->prefixName.isEmpty() ) - w.appendChild( textTag(doc, "PREFIX", d->prefixName) ); - if ( !d->suffixName.isEmpty() ) - w.appendChild( textTag(doc, "SUFFIX", d->suffixName) ); + if (!d->familyName.isEmpty()) + w.appendChild(textTag(doc, "FAMILY", d->familyName)); + if (!d->givenName.isEmpty()) + w.appendChild(textTag(doc, "GIVEN", d->givenName)); + if (!d->middleName.isEmpty()) + w.appendChild(textTag(doc, "MIDDLE", d->middleName)); + if (!d->prefixName.isEmpty()) + w.appendChild(textTag(doc, "PREFIX", d->prefixName)); + if (!d->suffixName.isEmpty()) + w.appendChild(textTag(doc, "SUFFIX", d->suffixName)); v.appendChild(w); } - if ( !d->nickName.isEmpty() ) - v.appendChild( textTag(doc, "NICKNAME", d->nickName) ); + if (!d->nickName.isEmpty()) + v.appendChild(textTag(doc, "NICKNAME", d->nickName)); - if ( !d->photo.isEmpty() || !d->photoURI.isEmpty() ) { + if (!d->photo.isEmpty() || !d->photoURI.isEmpty()) { QDomElement w = doc->createElement("PHOTO"); - if ( !d->photo.isEmpty() ) { - w.appendChild( textTag(doc, "TYPE", image2type(d->photo)) ); - w.appendChild( textTag(doc, "BINVAL", foldString( QCA::Base64().arrayToString(d->photo)) ) ); - } - else if ( !d->photoURI.isEmpty() ) - w.appendChild( textTag(doc, "EXTVAL", d->photoURI) ); + if (!d->photo.isEmpty()) { + w.appendChild(textTag(doc, "TYPE", image2type(d->photo))); + w.appendChild(textTag(doc, "BINVAL", foldString(QCA::Base64().arrayToString(d->photo)))); + } else if (!d->photoURI.isEmpty()) + w.appendChild(textTag(doc, "EXTVAL", d->photoURI)); v.appendChild(w); } - if ( !d->bday.isEmpty() ) - v.appendChild( textTag(doc, "BDAY", d->bday) ); + if (!d->bday.isEmpty()) + v.appendChild(textTag(doc, "BDAY", d->bday)); - if ( !d->addressList.isEmpty() ) { - AddressList::ConstIterator it = d->addressList.constBegin(); - for ( ; it != d->addressList.end(); ++it ) { + if (!d->addressList.isEmpty()) { + for (const auto &a : std::as_const(d->addressList)) { QDomElement w = doc->createElement("ADR"); - const Address &a = *it; - - if ( a.home ) - w.appendChild( emptyTag(doc, "HOME") ); - if ( a.work ) - w.appendChild( emptyTag(doc, "WORK") ); - if ( a.postal ) - w.appendChild( emptyTag(doc, "POSTAL") ); - if ( a.parcel ) - w.appendChild( emptyTag(doc, "PARCEL") ); - if ( a.dom ) - w.appendChild( emptyTag(doc, "DOM") ); - if ( a.intl ) - w.appendChild( emptyTag(doc, "INTL") ); - if ( a.pref ) - w.appendChild( emptyTag(doc, "PREF") ); - - if ( !a.pobox.isEmpty() ) - w.appendChild( textTag(doc, "POBOX", a.pobox) ); - if ( !a.extaddr.isEmpty() ) - w.appendChild( textTag(doc, "EXTADR", a.extaddr) ); - if ( !a.street.isEmpty() ) - w.appendChild( textTag(doc, "STREET", a.street) ); - if ( !a.locality.isEmpty() ) - w.appendChild( textTag(doc, "LOCALITY", a.locality) ); - if ( !a.region.isEmpty() ) - w.appendChild( textTag(doc, "REGION", a.region) ); - if ( !a.pcode.isEmpty() ) - w.appendChild( textTag(doc, "PCODE", a.pcode) ); - if ( !a.country.isEmpty() ) - w.appendChild( textTag(doc, "CTRY", a.country) ); + + if (a.home) + w.appendChild(emptyTag(doc, "HOME")); + if (a.work) + w.appendChild(emptyTag(doc, "WORK")); + if (a.postal) + w.appendChild(emptyTag(doc, "POSTAL")); + if (a.parcel) + w.appendChild(emptyTag(doc, "PARCEL")); + if (a.dom) + w.appendChild(emptyTag(doc, "DOM")); + if (a.intl) + w.appendChild(emptyTag(doc, "INTL")); + if (a.pref) + w.appendChild(emptyTag(doc, "PREF")); + + if (!a.pobox.isEmpty()) + w.appendChild(textTag(doc, "POBOX", a.pobox)); + if (!a.extaddr.isEmpty()) + w.appendChild(textTag(doc, "EXTADR", a.extaddr)); + if (!a.street.isEmpty()) + w.appendChild(textTag(doc, "STREET", a.street)); + if (!a.locality.isEmpty()) + w.appendChild(textTag(doc, "LOCALITY", a.locality)); + if (!a.region.isEmpty()) + w.appendChild(textTag(doc, "REGION", a.region)); + if (!a.pcode.isEmpty()) + w.appendChild(textTag(doc, "PCODE", a.pcode)); + if (!a.country.isEmpty()) + w.appendChild(textTag(doc, "CTRY", a.country)); v.appendChild(w); } } - if ( !d->labelList.isEmpty() ) { - LabelList::ConstIterator it = d->labelList.constBegin(); - for ( ; it != d->labelList.end(); ++it ) { + if (!d->labelList.isEmpty()) { + for (const auto &l : std::as_const(d->labelList)) { QDomElement w = doc->createElement("LABEL"); - const Label &l = *it; - - if ( l.home ) - w.appendChild( emptyTag(doc, "HOME") ); - if ( l.work ) - w.appendChild( emptyTag(doc, "WORK") ); - if ( l.postal ) - w.appendChild( emptyTag(doc, "POSTAL") ); - if ( l.parcel ) - w.appendChild( emptyTag(doc, "PARCEL") ); - if ( l.dom ) - w.appendChild( emptyTag(doc, "DOM") ); - if ( l.intl ) - w.appendChild( emptyTag(doc, "INTL") ); - if ( l.pref ) - w.appendChild( emptyTag(doc, "PREF") ); - - if ( !l.lines.isEmpty() ) { - QStringList::ConstIterator it = l.lines.constBegin(); - for ( ; it != l.lines.end(); ++it ) - w.appendChild( textTag(doc, "LINE", *it) ); + + if (l.home) + w.appendChild(emptyTag(doc, "HOME")); + if (l.work) + w.appendChild(emptyTag(doc, "WORK")); + if (l.postal) + w.appendChild(emptyTag(doc, "POSTAL")); + if (l.parcel) + w.appendChild(emptyTag(doc, "PARCEL")); + if (l.dom) + w.appendChild(emptyTag(doc, "DOM")); + if (l.intl) + w.appendChild(emptyTag(doc, "INTL")); + if (l.pref) + w.appendChild(emptyTag(doc, "PREF")); + + if (!l.lines.isEmpty()) { + for (const auto &it : std::as_const(l.lines)) + w.appendChild(textTag(doc, "LINE", it)); } v.appendChild(w); } } - if ( !d->phoneList.isEmpty() ) { - PhoneList::ConstIterator it = d->phoneList.constBegin(); - for ( ; it != d->phoneList.end(); ++it ) { + if (!d->phoneList.isEmpty()) { + for (const auto &p : std::as_const(d->phoneList)) { QDomElement w = doc->createElement("TEL"); - const Phone &p = *it; - - if ( p.home ) - w.appendChild( emptyTag(doc, "HOME") ); - if ( p.work ) - w.appendChild( emptyTag(doc, "WORK") ); - if ( p.voice ) - w.appendChild( emptyTag(doc, "VOICE") ); - if ( p.fax ) - w.appendChild( emptyTag(doc, "FAX") ); - if ( p.pager ) - w.appendChild( emptyTag(doc, "PAGER") ); - if ( p.msg ) - w.appendChild( emptyTag(doc, "MSG") ); - if ( p.cell ) - w.appendChild( emptyTag(doc, "CELL") ); - if ( p.video ) - w.appendChild( emptyTag(doc, "VIDEO") ); - if ( p.bbs ) - w.appendChild( emptyTag(doc, "BBS") ); - if ( p.modem ) - w.appendChild( emptyTag(doc, "MODEM") ); - if ( p.isdn ) - w.appendChild( emptyTag(doc, "ISDN") ); - if ( p.pcs ) - w.appendChild( emptyTag(doc, "PCS") ); - if ( p.pref ) - w.appendChild( emptyTag(doc, "PREF") ); - - if ( !p.number.isEmpty() ) - w.appendChild( textTag(doc, "NUMBER", p.number) ); + + if (p.home) + w.appendChild(emptyTag(doc, "HOME")); + if (p.work) + w.appendChild(emptyTag(doc, "WORK")); + if (p.voice) + w.appendChild(emptyTag(doc, "VOICE")); + if (p.fax) + w.appendChild(emptyTag(doc, "FAX")); + if (p.pager) + w.appendChild(emptyTag(doc, "PAGER")); + if (p.msg) + w.appendChild(emptyTag(doc, "MSG")); + if (p.cell) + w.appendChild(emptyTag(doc, "CELL")); + if (p.video) + w.appendChild(emptyTag(doc, "VIDEO")); + if (p.bbs) + w.appendChild(emptyTag(doc, "BBS")); + if (p.modem) + w.appendChild(emptyTag(doc, "MODEM")); + if (p.isdn) + w.appendChild(emptyTag(doc, "ISDN")); + if (p.pcs) + w.appendChild(emptyTag(doc, "PCS")); + if (p.pref) + w.appendChild(emptyTag(doc, "PREF")); + + if (!p.number.isEmpty()) + w.appendChild(textTag(doc, "NUMBER", p.number)); v.appendChild(w); } } - if ( !d->emailList.isEmpty() ) { - EmailList::ConstIterator it = d->emailList.constBegin(); - for ( ; it != d->emailList.end(); ++it ) { + if (!d->emailList.isEmpty()) { + for (const auto &e : std::as_const(d->emailList)) { QDomElement w = doc->createElement("EMAIL"); - const Email &e = *it; - if ( e.home ) - w.appendChild( emptyTag(doc, "HOME") ); - if ( e.work ) - w.appendChild( emptyTag(doc, "WORK") ); - if ( e.internet ) - w.appendChild( emptyTag(doc, "INTERNET") ); - if ( e.x400 ) - w.appendChild( emptyTag(doc, "X400") ); + if (e.pref) + w.appendChild(emptyTag(doc, "PREF")); + if (e.home) + w.appendChild(emptyTag(doc, "HOME")); + if (e.work) + w.appendChild(emptyTag(doc, "WORK")); + if (e.internet) + w.appendChild(emptyTag(doc, "INTERNET")); + if (e.x400) + w.appendChild(emptyTag(doc, "X400")); - if ( !e.userid.isEmpty() ) - w.appendChild( textTag(doc, "USERID", e.userid) ); + if (!e.userid.isEmpty()) + w.appendChild(textTag(doc, "USERID", e.userid)); v.appendChild(w); } } - if ( !d->jid.isEmpty() ) - v.appendChild( textTag(doc, "JABBERID", d->jid) ); - if ( !d->mailer.isEmpty() ) - v.appendChild( textTag(doc, "MAILER", d->mailer) ); - if ( !d->timezone.isEmpty() ) - v.appendChild( textTag(doc, "TZ", d->timezone) ); + if (!d->jid.isEmpty()) + v.appendChild(textTag(doc, "JABBERID", d->jid)); + if (!d->mailer.isEmpty()) + v.appendChild(textTag(doc, "MAILER", d->mailer)); + if (!d->timezone.isEmpty()) + v.appendChild(textTag(doc, "TZ", d->timezone)); - if ( !d->geo.lat.isEmpty() || !d->geo.lon.isEmpty() ) { + if (!d->geo.lat.isEmpty() || !d->geo.lon.isEmpty()) { QDomElement w = doc->createElement("GEO"); - if ( !d->geo.lat.isEmpty() ) - w.appendChild( textTag(doc, "LAT", d->geo.lat) ); - if ( !d->geo.lon.isEmpty() ) - w.appendChild( textTag(doc, "LON", d->geo.lon)); + if (!d->geo.lat.isEmpty()) + w.appendChild(textTag(doc, "LAT", d->geo.lat)); + if (!d->geo.lon.isEmpty()) + w.appendChild(textTag(doc, "LON", d->geo.lon)); v.appendChild(w); } - if ( !d->title.isEmpty() ) - v.appendChild( textTag(doc, "TITLE", d->title) ); - if ( !d->role.isEmpty() ) - v.appendChild( textTag(doc, "ROLE", d->role) ); + if (!d->title.isEmpty()) + v.appendChild(textTag(doc, "TITLE", d->title)); + if (!d->role.isEmpty()) + v.appendChild(textTag(doc, "ROLE", d->role)); - if ( !d->logo.isEmpty() || !d->logoURI.isEmpty() ) { + if (!d->logo.isEmpty() || !d->logoURI.isEmpty()) { QDomElement w = doc->createElement("LOGO"); - if ( !d->logo.isEmpty() ) { - w.appendChild( textTag(doc, "TYPE", image2type(d->logo)) ); - w.appendChild( textTag(doc, "BINVAL", foldString( QCA::Base64().arrayToString(d->logo)) ) ); - } - else if ( !d->logoURI.isEmpty() ) - w.appendChild( textTag(doc, "EXTVAL", d->logoURI) ); + if (!d->logo.isEmpty()) { + w.appendChild(textTag(doc, "TYPE", image2type(d->logo))); + w.appendChild(textTag(doc, "BINVAL", foldString(QCA::Base64().arrayToString(d->logo)))); + } else if (!d->logoURI.isEmpty()) + w.appendChild(textTag(doc, "EXTVAL", d->logoURI)); v.appendChild(w); } - if ( !d->agentURI.isEmpty() || (d->agent && d->agent->isEmpty()) ) { + if (!d->agentURI.isEmpty() || (d->agent && d->agent->isEmpty())) { QDomElement w = doc->createElement("AGENT"); - if ( d->agent && !d->agent->isEmpty() ) - w.appendChild( d->agent->toXml(doc) ); - else if ( !d->agentURI.isEmpty() ) - w.appendChild( textTag(doc, "EXTVAL", d->agentURI) ); + if (d->agent && !d->agent->isEmpty()) + w.appendChild(d->agent->toXml(doc)); + else if (!d->agentURI.isEmpty()) + w.appendChild(textTag(doc, "EXTVAL", d->agentURI)); v.appendChild(w); } - if ( !d->org.name.isEmpty() || !d->org.unit.isEmpty() ) { + if (!d->org.name.isEmpty() || !d->org.unit.isEmpty()) { QDomElement w = doc->createElement("ORG"); - if ( !d->org.name.isEmpty() ) - w.appendChild( textTag(doc, "ORGNAME", d->org.name) ); + if (!d->org.name.isEmpty()) + w.appendChild(textTag(doc, "ORGNAME", d->org.name)); - if ( !d->org.unit.isEmpty() ) { - QStringList::ConstIterator it = d->org.unit.constBegin(); - for ( ; it != d->org.unit.end(); ++it ) - w.appendChild( textTag(doc, "ORGUNIT", *it) ); + if (!d->org.unit.isEmpty()) { + for (const auto &unit : std::as_const(d->org.unit)) + w.appendChild(textTag(doc, "ORGUNIT", unit)); } v.appendChild(w); } - if ( !d->categories.isEmpty() ) { + if (!d->categories.isEmpty()) { QDomElement w = doc->createElement("CATEGORIES"); - QStringList::ConstIterator it = d->categories.constBegin(); - for ( ; it != d->categories.end(); ++it ) - w.appendChild( textTag(doc, "KEYWORD", *it) ); + for (const auto &c : std::as_const(d->categories)) + w.appendChild(textTag(doc, "KEYWORD", c)); v.appendChild(w); } - if ( !d->note.isEmpty() ) - v.appendChild( textTag(doc, "NOTE", d->note) ); - if ( !d->prodId.isEmpty() ) - v.appendChild( textTag(doc, "PRODID", d->prodId) ); - if ( !d->rev.isEmpty() ) - v.appendChild( textTag(doc, "REV", d->rev) ); - if ( !d->sortString.isEmpty() ) - v.appendChild( textTag(doc, "SORT-STRING", d->sortString) ); + if (!d->note.isEmpty()) + v.appendChild(textTag(doc, "NOTE", d->note)); + if (!d->prodId.isEmpty()) + v.appendChild(textTag(doc, "PRODID", d->prodId)); + if (!d->rev.isEmpty()) + v.appendChild(textTag(doc, "REV", d->rev)); + if (!d->sortString.isEmpty()) + v.appendChild(textTag(doc, "SORT-STRING", d->sortString)); - if ( !d->sound.isEmpty() || !d->soundURI.isEmpty() || !d->soundPhonetic.isEmpty() ) { + if (!d->sound.isEmpty() || !d->soundURI.isEmpty() || !d->soundPhonetic.isEmpty()) { QDomElement w = doc->createElement("SOUND"); - if ( !d->sound.isEmpty() ) - w.appendChild( textTag(doc, "BINVAL", foldString( QCA::Base64().arrayToString(d->sound)) ) ); - else if ( !d->soundURI.isEmpty() ) - w.appendChild( textTag(doc, "EXTVAL", d->soundURI) ); - else if ( !d->soundPhonetic.isEmpty() ) - w.appendChild( textTag(doc, "PHONETIC", d->soundPhonetic) ); + if (!d->sound.isEmpty()) + w.appendChild(textTag(doc, "BINVAL", foldString(QCA::Base64().arrayToString(d->sound)))); + else if (!d->soundURI.isEmpty()) + w.appendChild(textTag(doc, "EXTVAL", d->soundURI)); + else if (!d->soundPhonetic.isEmpty()) + w.appendChild(textTag(doc, "PHONETIC", d->soundPhonetic)); v.appendChild(w); } - if ( !d->uid.isEmpty() ) - v.appendChild( textTag(doc, "UID", d->uid) ); - if ( !d->url.isEmpty() ) - v.appendChild( textTag(doc, "URL", d->url) ); - if ( !d->desc.isEmpty() ) - v.appendChild( textTag(doc, "DESC", d->desc) ); + if (!d->uid.isEmpty()) + v.appendChild(textTag(doc, "UID", d->uid)); + if (!d->url.isEmpty()) + v.appendChild(textTag(doc, "URL", d->url)); + if (!d->desc.isEmpty()) + v.appendChild(textTag(doc, "DESC", d->desc)); - if ( d->privacyClass != pcNone ) { + if (d->privacyClass != pcNone) { QDomElement w = doc->createElement("CLASS"); - if ( d->privacyClass == pcPublic ) - w.appendChild( emptyTag(doc, "PUBLIC") ); - else if ( d->privacyClass == pcPrivate ) - w.appendChild( emptyTag(doc, "PRIVATE") ); - else if ( d->privacyClass == pcConfidential ) - w.appendChild( emptyTag(doc, "CONFIDENTIAL") ); + if (d->privacyClass == pcPublic) + w.appendChild(emptyTag(doc, "PUBLIC")); + else if (d->privacyClass == pcPrivate) + w.appendChild(emptyTag(doc, "PRIVATE")); + else if (d->privacyClass == pcConfidential) + w.appendChild(emptyTag(doc, "CONFIDENTIAL")); v.appendChild(w); } - if ( !d->key.isEmpty() ) { + if (!d->key.isEmpty()) { QDomElement w = doc->createElement("KEY"); // TODO: Justin, please check out this code - w.appendChild( textTag(doc, "TYPE", "text/plain")); // FIXME - w.appendChild( textTag(doc, "CRED", QString::fromUtf8(d->key)) ); // FIXME + w.appendChild(textTag(doc, "TYPE", "text/plain")); // FIXME + w.appendChild(textTag(doc, "CRED", QString::fromUtf8(d->key))); // FIXME v.appendChild(w); } @@ -518,42 +466,41 @@ QDomElement VCard::toXml(QDomDocument *doc) const VCard VCard::fromXml(const QDomElement &q) { - if ( q.tagName().toUpper() != "VCARD" ) + if (q.tagName().toUpper() != "VCARD") return VCard(); VCard v; v.d = new VCardPrivate; QDomNode n = q.firstChild(); - for ( ; !n.isNull(); n = n.nextSibling() ) { + for (; !n.isNull(); n = n.nextSibling()) { QDomElement i = n.toElement(); - if ( i.isNull() ) + if (i.isNull()) continue; QString tag = i.tagName().toUpper(); QDomElement e; - if ( tag == "VERSION" ) + if (tag == "VERSION") v.d->version = i.text().trimmed(); - else if ( tag == "FN" ) + else if (tag == "FN") v.d->fullName = i.text().trimmed(); - else if ( tag == "N" ) { + else if (tag == "N") { v.d->familyName = subTagText(i, "FAMILY"); v.d->givenName = subTagText(i, "GIVEN"); v.d->middleName = subTagText(i, "MIDDLE"); v.d->prefixName = subTagText(i, "PREFIX"); v.d->suffixName = subTagText(i, "SUFFIX"); - } - else if ( tag == "NICKNAME" ) + } else if (tag == "NICKNAME") v.d->nickName = i.text().trimmed(); - else if ( tag == "PHOTO" ) { - v.d->photo = QCA::Base64().stringToArray(subTagText(i, "BINVAL").replace(QRegExp("[\r\n]+"),"")).toByteArray(); + else if (tag == "PHOTO") { + static QRegularExpression newlines("[\r\n]+"); + v.d->photo = QCA::Base64().stringToArray(subTagText(i, "BINVAL").replace(newlines, "")).toByteArray(); v.d->photoURI = subTagText(i, "EXTVAL"); - } - else if ( tag == "BDAY" ) + } else if (tag == "BDAY") v.d->bday = i.text().trimmed(); - else if ( tag == "ADR" ) { + else if (tag == "ADR") { Address a; a.home = hasSubTag(i, "HOME"); @@ -572,17 +519,16 @@ VCard VCard::fromXml(const QDomElement &q) a.pcode = subTagText(i, "PCODE"); a.country = subTagText(i, "CTRY"); - if ( a.country.isEmpty() ) // FIXME: Workaround for Psi prior to 0.9 - if ( hasSubTag(i, "COUNTRY") ) + if (a.country.isEmpty()) // FIXME: Workaround for Psi prior to 0.9 + if (hasSubTag(i, "COUNTRY")) a.country = subTagText(i, "COUNTRY"); - if ( a.extaddr.isEmpty() ) // FIXME: Workaround for Psi prior to 0.9 - if ( hasSubTag(i, "EXTADD") ) + if (a.extaddr.isEmpty()) // FIXME: Workaround for Psi prior to 0.9 + if (hasSubTag(i, "EXTADD")) a.extaddr = subTagText(i, "EXTADD"); - v.d->addressList.append ( a ); - } - else if ( tag == "LABEL" ) { + v.d->addressList.append(a); + } else if (tag == "LABEL") { Label l; l.home = hasSubTag(i, "HOME"); @@ -594,18 +540,17 @@ VCard VCard::fromXml(const QDomElement &q) l.pref = hasSubTag(i, "PREF"); QDomNode nn = i.firstChild(); - for ( ; !nn.isNull(); nn = nn.nextSibling() ) { + for (; !nn.isNull(); nn = nn.nextSibling()) { QDomElement ii = nn.toElement(); - if ( ii.isNull() ) + if (ii.isNull()) continue; - if ( ii.tagName().toUpper() == "LINE" ) - l.lines.append ( ii.text().trimmed() ); + if (ii.tagName().toUpper() == "LINE") + l.lines.append(ii.text().trimmed()); } - v.d->labelList.append ( l ); - } - else if ( tag == "TEL" ) { + v.d->labelList.append(l); + } else if (tag == "TEL") { Phone p; p.home = hasSubTag(i, "HOME"); @@ -624,13 +569,30 @@ VCard VCard::fromXml(const QDomElement &q) p.number = subTagText(i, "NUMBER"); - if ( p.number.isEmpty() ) // FIXME: Workaround for Psi prior to 0.9 - if ( hasSubTag(i, "VOICE") ) - p.number = subTagText(i, "VOICE"); - - v.d->phoneList.append ( p ); - } - else if ( tag == "EMAIL" ) { + if (!p.number.isEmpty()) { + v.d->phoneList.append(p); + + auto it = std::find_if(v.d->phoneList.begin(), v.d->phoneList.end(), + [number = p.number](const Phone &p) { return p.number == number; }); + if (it == v.d->phoneList.end()) { + v.d->phoneList.append(p); + } else { + it->home = (it->home || p.home); + it->work = (it->work || p.work); + it->voice = (it->voice || p.voice); + it->fax = (it->fax || p.fax); + it->pager = (it->pager || p.pager); + it->msg = (it->msg || p.msg); + it->cell = (it->cell || p.cell); + it->video = (it->video || p.video); + it->bbs = (it->bbs || p.bbs); + it->modem = (it->modem || p.modem); + it->isdn = (it->isdn || p.isdn); + it->pcs = (it->pcs || p.pcs); + it->pref = (it->pref || p.pref); + } + } + } else if (tag == "EMAIL") { Email m; m.home = hasSubTag(i, "HOME"); @@ -638,108 +600,110 @@ VCard VCard::fromXml(const QDomElement &q) m.internet = hasSubTag(i, "INTERNET"); m.x400 = hasSubTag(i, "X400"); - m.userid = subTagText(i, "USERID"); - - if ( m.userid.isEmpty() ) // FIXME: Workaround for Psi prior to 0.9 - if ( !i.text().isEmpty() ) - m.userid = i.text().trimmed(); + m.pref = hasSubTag(i, "PREF"); + + m.userid = subTagText(i, "USERID").trimmed(); + if (!m.userid.isEmpty()) { + auto it = std::find_if(v.d->emailList.begin(), v.d->emailList.end(), + [user_id = m.userid](const Email &e) { return e.userid == user_id; }); + if (it == v.d->emailList.end()) { + v.d->emailList.append(m); + } else { + it->home = (it->home || m.home); + it->work = (it->work || m.work); + it->internet = (it->internet || m.internet); + it->x400 = (it->x400 || m.x400); + it->pref = (it->pref || m.pref); + } + } - v.d->emailList.append ( m ); - } - else if ( tag == "JABBERID" ) + } else if (tag == "JABBERID") v.d->jid = i.text().trimmed(); - else if ( tag == "MAILER" ) + else if (tag == "MAILER") v.d->mailer = i.text().trimmed(); - else if ( tag == "TZ" ) + else if (tag == "TZ") v.d->timezone = i.text().trimmed(); - else if ( tag == "GEO" ) { + else if (tag == "GEO") { v.d->geo.lat = subTagText(i, "LAT"); v.d->geo.lon = subTagText(i, "LON"); - } - else if ( tag == "TITLE" ) + } else if (tag == "TITLE") v.d->title = i.text().trimmed(); - else if ( tag == "ROLE" ) + else if (tag == "ROLE") v.d->role = i.text().trimmed(); - else if ( tag == "LOGO" ) { - v.d->logo = QCA::Base64().stringToArray( subTagText(i, "BINVAL").replace("\n","") ).toByteArray(); + else if (tag == "LOGO") { + v.d->logo = QCA::Base64().stringToArray(subTagText(i, "BINVAL").replace("\n", "")).toByteArray(); v.d->logoURI = subTagText(i, "EXTVAL"); - } - else if ( tag == "AGENT" ) { + } else if (tag == "AGENT") { e = i.firstChildElement("VCARD"); - if ( !e.isNull() ) { + if (!e.isNull()) { VCard a; - if ( a.fromXml(e) ) { - if ( !v.d->agent ) + if (a.fromXml(e)) { + if (!v.d->agent) v.d->agent = QSharedPointer(new VCard); *(v.d->agent) = a; } } v.d->agentURI = subTagText(i, "EXTVAL"); - } - else if ( tag == "ORG" ) { + } else if (tag == "ORG") { v.d->org.name = subTagText(i, "ORGNAME"); QDomNode nn = i.firstChild(); - for ( ; !nn.isNull(); nn = nn.nextSibling() ) { + for (; !nn.isNull(); nn = nn.nextSibling()) { QDomElement ii = nn.toElement(); - if ( ii.isNull() ) + if (ii.isNull()) continue; - if ( ii.tagName().toUpper() == "ORGUNIT" ) - v.d->org.unit.append( ii.text().trimmed() ); + if (ii.tagName().toUpper() == "ORGUNIT") + v.d->org.unit.append(ii.text().trimmed()); } - } - else if ( tag == "CATEGORIES") { + } else if (tag == "CATEGORIES") { QDomNode nn = i.firstChild(); - for ( ; !nn.isNull(); nn = nn.nextSibling() ) { + for (; !nn.isNull(); nn = nn.nextSibling()) { QDomElement ee = nn.toElement(); - if ( ee.isNull() ) + if (ee.isNull()) continue; - if ( ee.tagName().toUpper() == "KEYWORD" ) + if (ee.tagName().toUpper() == "KEYWORD") v.d->categories << ee.text().trimmed(); } - } - else if ( tag == "NOTE" ) + } else if (tag == "NOTE") v.d->note = i.text().trimmed(); - else if ( tag == "PRODID" ) + else if (tag == "PRODID") v.d->prodId = i.text().trimmed(); - else if ( tag == "REV" ) + else if (tag == "REV") v.d->rev = i.text().trimmed(); - else if ( tag == "SORT-STRING" ) + else if (tag == "SORT-STRING") v.d->sortString = i.text().trimmed(); - else if ( tag == "SOUND" ) { - v.d->sound = QCA::Base64().stringToArray( subTagText(i, "BINVAL").replace("\n","") ).toByteArray(); + else if (tag == "SOUND") { + v.d->sound = QCA::Base64().stringToArray(subTagText(i, "BINVAL").replace("\n", "")).toByteArray(); v.d->soundURI = subTagText(i, "EXTVAL"); v.d->soundPhonetic = subTagText(i, "PHONETIC"); - } - else if ( tag == "UID" ) + } else if (tag == "UID") v.d->uid = i.text().trimmed(); - else if ( tag == "URL") + else if (tag == "URL") v.d->url = i.text().trimmed(); - else if ( tag == "DESC" ) + else if (tag == "DESC") v.d->desc = i.text().trimmed(); - else if ( tag == "CLASS" ) { - if ( hasSubTag(i, "PUBLIC") ) + else if (tag == "CLASS") { + if (hasSubTag(i, "PUBLIC")) v.d->privacyClass = pcPublic; - else if ( hasSubTag(i, "PRIVATE") ) + else if (hasSubTag(i, "PRIVATE")) v.d->privacyClass = pcPrivate; - else if ( hasSubTag(i, "CONFIDENTIAL") ) + else if (hasSubTag(i, "CONFIDENTIAL")) v.d->privacyClass = pcConfidential; - } - else if ( tag == "KEY" ) { + } else if (tag == "KEY") { // TODO: Justin, please check out this code - e = i.firstChildElement("TYPE"); + e = i.firstChildElement("TYPE"); QString type = "text/plain"; - if ( !e.isNull() ) + if (!e.isNull()) type = e.text().trimmed(); e = i.firstChildElement("CRED"); - if ( e.isNull() ) + if (e.isNull()) e = i.firstChildElement("BINVAL"); // case for very clever clients ;-) - if ( !e.isNull() ) + if (!e.isNull()) v.d->key = e.text().toUtf8(); // FIXME } } @@ -747,10 +711,7 @@ VCard VCard::fromXml(const QDomElement &q) return v; } -bool VCard::isEmpty() const -{ - return !d || d->isEmpty(); -} +bool VCard::isEmpty() const { return !d || d->isEmpty(); } VCard VCard::makeEmpty() { @@ -761,275 +722,118 @@ VCard VCard::makeEmpty() // Some constructors -VCard::Address::Address() -{ - home = work = postal = parcel = dom = intl = pref = false; -} +VCard::Address::Address() { home = work = postal = parcel = dom = intl = pref = false; } -VCard::Label::Label() -{ - home = work = postal = parcel = dom = intl = pref = false; -} +VCard::Label::Label() { home = work = postal = parcel = dom = intl = pref = false; } VCard::Phone::Phone() { home = work = voice = fax = pager = msg = cell = video = bbs = modem = isdn = pcs = pref = false; } -VCard::Email::Email() -{ - home = work = internet = x400 = false; -} +VCard::Email::Email() { home = work = internet = x400 = pref = false; } -VCard::Geo::Geo() -{ -} +VCard::Geo::Geo() { } -VCard::Org::Org() -{ -} +VCard::Org::Org() { } // vCard properties... -const QString &VCard::version() const -{ - return d->version; -} +const QString &VCard::version() const { return d->version; } -void VCard::setVersion(const QString &v) -{ - d->version = v; -} +void VCard::setVersion(const QString &v) { d->version = v; } -const QString &VCard::fullName() const -{ - return d->fullName; -} +const QString &VCard::fullName() const { return d->fullName; } -void VCard::setFullName(const QString &n) -{ - d->fullName = n; -} +void VCard::setFullName(const QString &n) { d->fullName = n; } -const QString &VCard::familyName() const -{ - return d->familyName; -} +const QString &VCard::familyName() const { return d->familyName; } -void VCard::setFamilyName(const QString &n) -{ - d->familyName = n; -} +void VCard::setFamilyName(const QString &n) { d->familyName = n; } -const QString &VCard::givenName() const -{ - return d->givenName; -} +const QString &VCard::givenName() const { return d->givenName; } -void VCard::setGivenName(const QString &n) -{ - d->givenName = n; -} +void VCard::setGivenName(const QString &n) { d->givenName = n; } -const QString &VCard::middleName() const -{ - return d->middleName; -} +const QString &VCard::middleName() const { return d->middleName; } -void VCard::setMiddleName(const QString &n) -{ - d->middleName = n; -} +void VCard::setMiddleName(const QString &n) { d->middleName = n; } -const QString &VCard::prefixName() const -{ - return d->prefixName; -} +const QString &VCard::prefixName() const { return d->prefixName; } -void VCard::setPrefixName(const QString &p) -{ - d->prefixName = p; -} +void VCard::setPrefixName(const QString &p) { d->prefixName = p; } -const QString &VCard::suffixName() const -{ - return d->suffixName; -} +const QString &VCard::suffixName() const { return d->suffixName; } -void VCard::setSuffixName(const QString &s) -{ - d->suffixName = s; -} +void VCard::setSuffixName(const QString &s) { d->suffixName = s; } -const QString &VCard::nickName() const -{ - return d->nickName; -} +const QString &VCard::nickName() const { return d->nickName; } -void VCard::setNickName(const QString &n) -{ - d->nickName = n; -} +void VCard::setNickName(const QString &n) { d->nickName = n; } -const QByteArray &VCard::photo() const -{ - return d->photo; -} +const QByteArray &VCard::photo() const { return d->photo; } -void VCard::setPhoto(const QByteArray &i) -{ - d->photo = i; -} +void VCard::setPhoto(const QByteArray &i) { d->photo = i; } -const QString &VCard::photoURI() const -{ - return d->photoURI; -} +const QString &VCard::photoURI() const { return d->photoURI; } -void VCard::setPhotoURI(const QString &p) -{ - d->photoURI = p; -} +void VCard::setPhotoURI(const QString &p) { d->photoURI = p; } -const QDate VCard::bday() const -{ - return QDate::fromString(d->bday); -} +const QDate VCard::bday() const { return QDate::fromString(d->bday); } -void VCard::setBday(const QDate &date) -{ - d->bday = date.toString(); -} +void VCard::setBday(const QDate &date) { d->bday = date.toString(); } -const QString &VCard::bdayStr() const -{ - return d->bday; -} +const QString &VCard::bdayStr() const { return d->bday; } -void VCard::setBdayStr(const QString &date) -{ - d->bday = date; -} +void VCard::setBdayStr(const QString &date) { d->bday = date; } -const VCard::AddressList &VCard::addressList() const -{ - return d->addressList; -} +const VCard::AddressList &VCard::addressList() const { return d->addressList; } -void VCard::setAddressList(const VCard::AddressList &a) -{ - d->addressList = a; -} +void VCard::setAddressList(const VCard::AddressList &a) { d->addressList = a; } -const VCard::LabelList &VCard::labelList() const -{ - return d->labelList; -} +const VCard::LabelList &VCard::labelList() const { return d->labelList; } -void VCard::setLabelList(const VCard::LabelList &l) -{ - d->labelList = l; -} +void VCard::setLabelList(const VCard::LabelList &l) { d->labelList = l; } -const VCard::PhoneList &VCard::phoneList() const -{ - return d->phoneList; -} +const VCard::PhoneList &VCard::phoneList() const { return d->phoneList; } -void VCard::setPhoneList(const VCard::PhoneList &p) -{ - d->phoneList = p; -} +void VCard::setPhoneList(const VCard::PhoneList &p) { d->phoneList = p; } -const VCard::EmailList &VCard::emailList() const -{ - return d->emailList; -} +const VCard::EmailList &VCard::emailList() const { return d->emailList; } -void VCard::setEmailList(const VCard::EmailList &e) -{ - d->emailList = e; -} +void VCard::setEmailList(const VCard::EmailList &e) { d->emailList = e; } -const QString &VCard::jid() const -{ - return d->jid; -} +const QString &VCard::jid() const { return d->jid; } -void VCard::setJid(const QString &j) -{ - d->jid = j; -} +void VCard::setJid(const QString &j) { d->jid = j; } -const QString &VCard::mailer() const -{ - return d->mailer; -} +const QString &VCard::mailer() const { return d->mailer; } -void VCard::setMailer(const QString &m) -{ - d->mailer = m; -} +void VCard::setMailer(const QString &m) { d->mailer = m; } -const QString &VCard::timezone() const -{ - return d->timezone; -} +const QString &VCard::timezone() const { return d->timezone; } -void VCard::setTimezone(const QString &t) -{ - d->timezone = t; -} +void VCard::setTimezone(const QString &t) { d->timezone = t; } -const VCard::Geo &VCard::geo() const -{ - return d->geo; -} +const VCard::Geo &VCard::geo() const { return d->geo; } -void VCard::setGeo(const VCard::Geo &g) -{ - d->geo = g; -} +void VCard::setGeo(const VCard::Geo &g) { d->geo = g; } -const QString &VCard::title() const -{ - return d->title; -} +const QString &VCard::title() const { return d->title; } -void VCard::setTitle(const QString &t) -{ - d->title = t; -} +void VCard::setTitle(const QString &t) { d->title = t; } -const QString &VCard::role() const -{ - return d->role; -} +const QString &VCard::role() const { return d->role; } -void VCard::setRole(const QString &r) -{ - d->role = r; -} +void VCard::setRole(const QString &r) { d->role = r; } -const QByteArray &VCard::logo() const -{ - return d->logo; -} +const QByteArray &VCard::logo() const { return d->logo; } -void VCard::setLogo(const QByteArray &i) -{ - d->logo = i; -} +void VCard::setLogo(const QByteArray &i) { d->logo = i; } -const QString &VCard::logoURI() const -{ - return d->logoURI; -} +const QString &VCard::logoURI() const { return d->logoURI; } -void VCard::setLogoURI(const QString &l) -{ - d->logoURI = l; -} +void VCard::setLogoURI(const QString &l) { d->logoURI = l; } VCard VCard::agent() const { @@ -1041,159 +845,68 @@ VCard VCard::agent() const void VCard::setAgent(const VCard &v) { - if ( !d->agent ) + if (!d->agent) d->agent = QSharedPointer(new VCard); *(d->agent) = v; } -const QString VCard::agentURI() const -{ - return d->agentURI; -} - -void VCard::setAgentURI(const QString &a) -{ - d->agentURI = a; -} +const QString VCard::agentURI() const { return d->agentURI; } -const VCard::Org &VCard::org() const -{ - return d->org; -} +void VCard::setAgentURI(const QString &a) { d->agentURI = a; } -void VCard::setOrg(const VCard::Org &o) -{ - d->org = o; -} +const VCard::Org &VCard::org() const { return d->org; } -const QStringList &VCard::categories() const -{ - return d->categories; -} +void VCard::setOrg(const VCard::Org &o) { d->org = o; } -void VCard::setCategories(const QStringList &c) -{ - d->categories = c; -} +const QStringList &VCard::categories() const { return d->categories; } -const QString &VCard::note() const -{ - return d->note; -} +void VCard::setCategories(const QStringList &c) { d->categories = c; } -void VCard::setNote(const QString &n) -{ - d->note = n; -} +const QString &VCard::note() const { return d->note; } -const QString &VCard::prodId() const -{ - return d->prodId; -} +void VCard::setNote(const QString &n) { d->note = n; } -void VCard::setProdId(const QString &p) -{ - d->prodId = p; -} +const QString &VCard::prodId() const { return d->prodId; } -const QString &VCard::rev() const -{ - return d->rev; -} +void VCard::setProdId(const QString &p) { d->prodId = p; } -void VCard::setRev(const QString &r) -{ - d->rev = r; -} +const QString &VCard::rev() const { return d->rev; } -const QString &VCard::sortString() const -{ - return d->sortString; -} +void VCard::setRev(const QString &r) { d->rev = r; } -void VCard::setSortString(const QString &s) -{ - d->sortString = s; -} +const QString &VCard::sortString() const { return d->sortString; } -const QByteArray &VCard::sound() const -{ - return d->sound; -} +void VCard::setSortString(const QString &s) { d->sortString = s; } -void VCard::setSound(const QByteArray &s) -{ - d->sound = s; -} +const QByteArray &VCard::sound() const { return d->sound; } -const QString &VCard::soundURI() const -{ - return d->soundURI; -} +void VCard::setSound(const QByteArray &s) { d->sound = s; } -void VCard::setSoundURI(const QString &s) -{ - d->soundURI = s; -} +const QString &VCard::soundURI() const { return d->soundURI; } -const QString &VCard::soundPhonetic() const -{ - return d->soundPhonetic; -} +void VCard::setSoundURI(const QString &s) { d->soundURI = s; } -void VCard::setSoundPhonetic(const QString &s) -{ - d->soundPhonetic = s; -} +const QString &VCard::soundPhonetic() const { return d->soundPhonetic; } -const QString &VCard::uid() const -{ - return d->uid; -} +void VCard::setSoundPhonetic(const QString &s) { d->soundPhonetic = s; } -void VCard::setUid(const QString &u) -{ - d->uid = u; -} +const QString &VCard::uid() const { return d->uid; } -const QString &VCard::url() const -{ - return d->url; -} +void VCard::setUid(const QString &u) { d->uid = u; } -void VCard::setUrl(const QString &u) -{ - d->url = u; -} +const QString &VCard::url() const { return d->url; } -const QString &VCard::desc() const -{ - return d->desc; -} +void VCard::setUrl(const QString &u) { d->url = u; } -void VCard::setDesc(const QString &desc) -{ - d->desc = desc; -} +const QString &VCard::desc() const { return d->desc; } -const VCard::PrivacyClass &VCard::privacyClass() const -{ - return d->privacyClass; -} +void VCard::setDesc(const QString &desc) { d->desc = desc; } -void VCard::setPrivacyClass(const VCard::PrivacyClass &c) -{ - d->privacyClass = c; -} +const VCard::PrivacyClass &VCard::privacyClass() const { return d->privacyClass; } -const QByteArray &VCard::key() const -{ - return d->key; -} +void VCard::setPrivacyClass(const VCard::PrivacyClass &c) { d->privacyClass = c; } -void VCard::setKey(const QByteArray &k) -{ - d->key = k; -} +const QByteArray &VCard::key() const { return d->key; } +void VCard::setKey(const QByteArray &k) { d->key = k; } } // namespace XMPP diff --git a/src/xmpp/xmpp-im/xmpp_vcard.h b/src/xmpp/xmpp-im/xmpp_vcard.h index 145c492f..5e442ade 100644 --- a/src/xmpp/xmpp-im/xmpp_vcard.h +++ b/src/xmpp/xmpp-im/xmpp_vcard.h @@ -17,274 +17,251 @@ * */ -#ifndef JABBER_VCARD_H -#define JABBER_VCARD_H +#ifndef XMPP_VCARD_H +#define XMPP_VCARD_H +#include #include #include #include - -#include #include class QDate; -namespace XMPP -{ - class VCardPrivate; - class VCard - { - public: - VCard(); - VCard(const VCard &); - VCard & operator=(const VCard &); - ~VCard(); - - QDomElement toXml(QDomDocument *) const; - static VCard fromXml(const QDomElement &); - bool isEmpty() const; - inline bool isNull() const { return !d; } - inline bool operator!() const { return !d; } - inline operator bool() const { return !!d; } - static VCard makeEmpty(); - - const QString &version() const; - void setVersion(const QString &); - - const QString &fullName() const; - void setFullName(const QString &); - - - const QString &familyName() const; - void setFamilyName(const QString &); - - const QString &givenName() const; - void setGivenName(const QString &); - - const QString &middleName() const; - void setMiddleName(const QString &); - - const QString &prefixName() const; - void setPrefixName(const QString &); - - const QString &suffixName() const; - void setSuffixName(const QString &); - - - const QString &nickName() const; - void setNickName(const QString &); - - - const QByteArray &photo() const; - void setPhoto(const QByteArray &); - - const QString &photoURI() const; - void setPhotoURI(const QString &); +namespace XMPP { +class VCardPrivate; +class VCard { +public: + VCard(); + VCard(const VCard &); + VCard &operator=(const VCard &); + ~VCard(); - const QDate bday() const; - void setBday(const QDate &); + QDomElement toXml(QDomDocument *) const; + static VCard fromXml(const QDomElement &); + bool isEmpty() const; + inline bool isNull() const { return !d; } + inline bool operator!() const { return !d; } + inline operator bool() const { return !!d; } + static VCard makeEmpty(); - const QString &bdayStr() const; - void setBdayStr(const QString &); + const QString &version() const; + void setVersion(const QString &); + const QString &fullName() const; + void setFullName(const QString &); - class Address { - public: - Address(); + const QString &familyName() const; + void setFamilyName(const QString &); - bool home; - bool work; - bool postal; - bool parcel; + const QString &givenName() const; + void setGivenName(const QString &); - bool dom; - bool intl; + const QString &middleName() const; + void setMiddleName(const QString &); - bool pref; + const QString &prefixName() const; + void setPrefixName(const QString &); - QString pobox; - QString extaddr; - QString street; - QString locality; - QString region; - QString pcode; - QString country; - }; - typedef QList
AddressList; - const AddressList &addressList() const; - void setAddressList(const AddressList &); + const QString &suffixName() const; + void setSuffixName(const QString &); - class Label { - public: - Label(); + const QString &nickName() const; + void setNickName(const QString &); - bool home; - bool work; - bool postal; - bool parcel; + const QByteArray &photo() const; + void setPhoto(const QByteArray &); - bool dom; - bool intl; + const QString &photoURI() const; + void setPhotoURI(const QString &); - bool pref; + const QDate bday() const; + void setBday(const QDate &); - QStringList lines; - }; - typedef QList