This commit is contained in:
Ho Sy Tan 2025-06-16 13:54:12 +07:00
commit 6466ec67ed
3046 changed files with 663075 additions and 0 deletions

32
.clang-format Normal file
View file

@ -0,0 +1,32 @@
# Flexisip, a flexible SIP proxy server with media capabilities.
# Copyright (C) 2010-2022 Belledonne Communications SARL, All rights reserved.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero 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 Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
---
Language: Cpp
BasedOnStyle: LLVM
AccessModifierOffset: -4
AllowShortFunctionsOnASingleLine: None
AllowShortIfStatementsOnASingleLine: AllIfsAndElse
AlwaysBreakTemplateDeclarations: Yes
BinPackParameters: false
ColumnLimit: 120
PointerAlignment: Left
IndentCaseLabels: true
IndentWidth: 4
Standard: c++14
TabWidth: 4
UseTab: ForIndentation
...

4
.clangd Normal file
View file

@ -0,0 +1,4 @@
Diagnostics:
UnusedIncludes: Strict
InlayHints:
Designators: No

81
.cproject Normal file
View file

@ -0,0 +1,81 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<?fileVersion 4.0.0?><cproject storage_type_id="org.eclipse.cdt.core.XmlProjectDescriptionStorage">
<storageModule moduleId="org.eclipse.cdt.core.settings">
<cconfiguration id="cdt.managedbuild.toolchain.gnu.base.1703525987">
<storageModule buildSystemId="org.eclipse.cdt.managedbuilder.core.configurationDataProvider" id="cdt.managedbuild.toolchain.gnu.base.1703525987" moduleId="org.eclipse.cdt.core.settings" name="Default">
<externalSettings/>
<extensions>
<extension id="org.eclipse.cdt.core.GNU_ELF" point="org.eclipse.cdt.core.BinaryParser"/>
<extension id="org.eclipse.cdt.core.GASErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
<extension id="org.eclipse.cdt.core.GmakeErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
<extension id="org.eclipse.cdt.core.GLDErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
<extension id="org.eclipse.cdt.core.CWDLocator" point="org.eclipse.cdt.core.ErrorParser"/>
<extension id="org.eclipse.cdt.core.GCCErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
</extensions>
</storageModule>
<storageModule moduleId="cdtBuildSystem" version="4.0.0">
<configuration buildProperties="" description="" id="cdt.managedbuild.toolchain.gnu.base.1703525987" name="Default" optionalBuildProperties="" parent="org.eclipse.cdt.build.core.emptycfg">
<folderInfo id="cdt.managedbuild.toolchain.gnu.base.1703525987.286272243" name="/" resourcePath="">
<toolChain id="cdt.managedbuild.toolchain.gnu.base.839081610" name="Linux GCC" superClass="cdt.managedbuild.toolchain.gnu.base">
<targetPlatform archList="all" binaryParser="org.eclipse.cdt.core.GNU_ELF" id="cdt.managedbuild.target.gnu.platform.base.16134557" name="Debug Platform" osList="linux,hpux,aix,qnx" superClass="cdt.managedbuild.target.gnu.platform.base"/>
<builder arguments="-j12" command="make" id="cdt.managedbuild.target.gnu.builder.base.285467957" keepEnvironmentInBuildfile="false" managedBuildOn="false" name="Gnu Make Builder" superClass="cdt.managedbuild.target.gnu.builder.base"/>
<tool id="cdt.managedbuild.tool.gnu.archiver.base.1258891957" name="GCC Archiver" superClass="cdt.managedbuild.tool.gnu.archiver.base"/>
<tool id="cdt.managedbuild.tool.gnu.cpp.compiler.base.1837414633" name="GCC C++ Compiler" superClass="cdt.managedbuild.tool.gnu.cpp.compiler.base">
<inputType id="cdt.managedbuild.tool.gnu.cpp.compiler.input.592090380" superClass="cdt.managedbuild.tool.gnu.cpp.compiler.input"/>
</tool>
<tool id="cdt.managedbuild.tool.gnu.c.compiler.base.1154097230" name="GCC C Compiler" superClass="cdt.managedbuild.tool.gnu.c.compiler.base">
<inputType id="cdt.managedbuild.tool.gnu.c.compiler.input.1955960919" superClass="cdt.managedbuild.tool.gnu.c.compiler.input"/>
</tool>
<tool id="cdt.managedbuild.tool.gnu.c.linker.base.1427539948" name="GCC C Linker" superClass="cdt.managedbuild.tool.gnu.c.linker.base"/>
<tool id="cdt.managedbuild.tool.gnu.cpp.linker.base.1374966898" name="GCC C++ Linker" superClass="cdt.managedbuild.tool.gnu.cpp.linker.base">
<inputType id="cdt.managedbuild.tool.gnu.cpp.linker.input.1235029049" superClass="cdt.managedbuild.tool.gnu.cpp.linker.input">
<additionalInput kind="additionalinputdependency" paths="$(USER_OBJS)"/>
<additionalInput kind="additionalinput" paths="$(LIBS)"/>
</inputType>
</tool>
<tool id="cdt.managedbuild.tool.gnu.assembler.base.1359322398" name="GCC Assembler" superClass="cdt.managedbuild.tool.gnu.assembler.base">
<inputType id="cdt.managedbuild.tool.gnu.assembler.input.1730089024" superClass="cdt.managedbuild.tool.gnu.assembler.input"/>
</tool>
</toolChain>
</folderInfo>
<sourceEntries>
<entry excluding="OUTPUT/|WORK/" flags="VALUE_WORKSPACE_PATH" kind="sourcePath" name=""/>
</sourceEntries>
</configuration>
</storageModule>
<storageModule moduleId="org.eclipse.cdt.core.externalSettings"/>
</cconfiguration>
</storageModule>
<storageModule moduleId="cdtBuildSystem" version="4.0.0">
<project id="flexisip.null.527299867" name="flexisip"/>
</storageModule>
<storageModule moduleId="org.eclipse.cdt.core.LanguageSettingsProviders"/>
<storageModule moduleId="refreshScope" versionNumber="2">
<configuration configurationName="Default">
<resource resourceType="PROJECT" workspacePath="/flexisip"/>
</configuration>
</storageModule>
<storageModule moduleId="org.eclipse.cdt.make.core.buildtargets"/>
<storageModule moduleId="scannerConfiguration">
<autodiscovery enabled="true" problemReportingEnabled="true" selectedProfileId=""/>
<scannerConfigBuildInfo instanceId="0.1713448483">
<autodiscovery enabled="true" problemReportingEnabled="true" selectedProfileId=""/>
</scannerConfigBuildInfo>
<scannerConfigBuildInfo instanceId="0.1420834493">
<autodiscovery enabled="true" problemReportingEnabled="true" selectedProfileId=""/>
</scannerConfigBuildInfo>
<scannerConfigBuildInfo instanceId="0.705448153">
<autodiscovery enabled="true" problemReportingEnabled="true" selectedProfileId="org.eclipse.cdt.make.core.GCCStandardMakePerProjectProfile"/>
<profile id="org.eclipse.cdt.make.core.GCCStandardMakePerProjectProfile">
<buildOutputProvider>
<openAction enabled="true" filePath=""/>
<parser enabled="false"/>
</buildOutputProvider>
<scannerInfoProvider id="specsFile">
<runAction arguments="-s -C scripts discovery" command="make" useDefault="true"/>
<parser enabled="true"/>
</scannerInfoProvider>
</profile>
</scannerConfigBuildInfo>
</storageModule>
</cproject>

6
.dockerignore Normal file
View file

@ -0,0 +1,6 @@
# Build/install
*build*/
*install*/
# Unused
test_deprecated/

1
.envrc Normal file
View file

@ -0,0 +1 @@
use flake

17
.gitignore vendored Normal file
View file

@ -0,0 +1,17 @@
### Any build directory ###
/build*
### IDE or editor files ###
*.kdev4
*.settings
*.kate*
.DS_Store
.vscode
/CMakeLists.txt.user
.idea/
### TEST ###
BCUnitAutomated-Results.xml
### Direnv ###
.direnv

View file

@ -0,0 +1,51 @@
# Flexisip continuous integration
The files contained in this directories are responsible for continuous integration. Each yaml file is included in the root `.gitlab-ci.yml` of the Flexisip project.
## Basic documentation of Gitlab-CI keywords
A basic documentation of the keywords is available in French on the [internal wiki](https://wiki.linphone.org/xwiki/bin/view/Engineering/Fonctionnement%20Gitlab-CI/).
It helps to understand the basics of Gitlab-CI.
## Flexisip tests
The Flexisip tests are run in the `tests-flexisip-mr` job of the `job-linux.yml` file. They rely on the current build from source of the Flexisip docker image done in the `docker-build-flexisip-src` job of the same file.
A Flexisip developper only needs to modify the following variables in `tests-flexisip-mr` for most usages :
- `LIBLINPHONE_DOCKER_TAG`
- `LIME_SERVER_VERSION`
- `ACCOUNT_MANAGER_VERSION`
- `FILE_TRANSFER_SERVER_VERSION`
You can find more information in the comments of this job.
What the test jobs does :
- It creates a workspace to store the logs of all components, and coredumps
- It launches docker-compose, running containers of Flexisip primary and auxiliary services. You can find all the services called in the docker-compose.yaml and docker-compose-standalone.yaml files of the [Flexisip-tester project](https://gitlab.linphone.org/BC/private/flexisip-tester).
- It prints logs of the liblinphone_tester in Gitlab-CI output.
- It uploads other logs in the artifacts.
- It displays the backtrace of liblinphone_tester and flexisip if coredumps were generated during the tests.
## Files structure
The files of this directory are organized by system type and by distribution.
Each file responsible for a Linux distribution has jobs inheriting from jobs located in `job-linux.yml` file.
Typically, this is a sample structure to illustrate :
- job-Linux
- build
- test
- package
- upload
- job-linux-centos7
- build-centos7 (extends `build`)
- test-centos7 (extends `test`)
- package-centos7 (extends `package`)
- upload-centos7 (extends `upload`)
- job-linux-ubuntu
- build-ubuntu (extends `build`)
- package-ubuntu (extends `package`)
- upload-ubuntu (extends `upload`)

55
.gitlab-ci-files/deploy.sh Executable file
View file

@ -0,0 +1,55 @@
#!/bin/bash
function print_usage {
prog=$(basename $0)
echo "syntax: $prog <dist>" 1>&2
exit 2
}
if [ -z "$1" ] || [ "${1:0:1}" = '-' ]; then
print_usage $0
fi
if [ $# -ne 1 ]; then
print_usage $0
fi
dist="$1"
id=$(cat /dev/urandom | env LC_ALL=C tr -dc 'a-zA-Z0-9' | fold -w 10 | head -n 1) || exit $?
tmpdir="$MAKE_REPO_TMP/tmp-$id"
rsync_dest="$DEPLOY_SERVER:$tmpdir/"
case "$dist" in
'centos')
make_repo_args="rpm $tmpdir $CENTOS_REPOSITORY"
rsync_src='build/*.rpm'
;;
'rockylinux')
make_repo_args="rpm $tmpdir $ROCKYLINUX_REPOSITORY"
rsync_src='build/*.rpm'
;;
'debian')
make_repo_args="deb $tmpdir $FREIGHT_PATH $RELEASE"
echo "make_repo_args=$make_repo_args"
rsync_src='build/*.deb build/*.ddeb'
;;
*)
echo "invalid distribution type: '$dist'. Only 'centos', 'rockylinux' and 'debian' are valid" 1>&2
exit 2
;;
esac
echo ">>> Pushing packages into '$rsync_dest'"
rsync -v $rsync_src $rsync_dest
echo ">>> Connecting on '$DEPLOY_SERVER'"
ssh $DEPLOY_SERVER "
echo '>>>> Making repository'
make_repo $make_repo_args || exit 1
echo \">>>> Removing '$tmpdir'\"
rm -r $tmpdir
"

View file

@ -0,0 +1,54 @@
.archlinux-image-variables:
image: gitlab.linphone.org:4567/bc/public/flexisip/bc-dev-archlinux:$ARCHLINUX_IMAGE_VERSION
variables:
CMAKE_OPTIONS: -DENABLE_UNIT_TESTS=ON -DENABLE_UNIT_TESTS_NGHTTP2ASIO=OFF
#################################################
# Makefile
#################################################
job-archlinux-makefile-gcc:
extends:
- .job-makefile-gcc
- .archlinux-image-variables
- .rules-never-run # ⚠ See `rules.yml`, override .job-makefile-gcc rules
job-archlinux-makefile-clang:
extends:
- .job-makefile-clang
- .archlinux-image-variables
- .rules-never-run # ⚠ See `rules.yml`, override .job-makefile-clang rules
#################################################
# Ninja
#################################################
job-archlinux-ninja-gcc:
extends:
- .job-ninja-gcc
- .archlinux-image-variables
# Uncomment when re-enabling tests
# - .tester-artifacts
rules:
- !reference [job-archlinux-ninja-clang, rules]
job-archlinux-ninja-clang:
extends:
- .job-ninja-clang
- .archlinux-image-variables
#################################################
# UNIT TESTS
#################################################
job-archlinux-unit-test:
extends:
- .unit-test
- .archlinux-image-variables
# 2023-07-06: The combination of an Ubuntu (22 or 23) host and an Archlinux image somehow breaks port assignments for yet unknown reasons.
# Symptoms: "Cannot assign requested address" errors from bctoolbox, sofia, and redis.
- .rules-never-run # ⚠ See `rules.yml`
needs:
- job-archlinux-ninja-gcc

View file

@ -0,0 +1,118 @@
.debian11-image:
image:
name: gitlab.linphone.org:4567/bc/public/flexisip/bc-dev-debian11:$DEBIAN_11_IMAGE_VERSION
#################################################
# Makefile
#################################################
job-debian11-makefile-gcc:
extends:
- .job-makefile-gcc
- .debian11-image
- .rules-never-run # ⚠ See `rules.yml`, override .job-makefile-gcc rules
job-debian11-makefile-clang:
extends:
- .job-makefile-clang
- .debian11-image
- .rules-never-run # ⚠ See `rules.yml`, override .job-makefile-clang rules
#################################################
# Ninja
#################################################
job-debian11-ninja-gcc:
extends:
- .job-ninja-gcc
- .debian11-image
job-debian11-ninja-clang:
extends:
- .job-ninja-clang
- .debian11-image
- .tester-artifacts
variables:
CMAKE_OPTIONS: -DENABLE_UNIT_TESTS=ON -DENABLE_COVERAGE=ON
#################################################
# UNIT TESTS
#################################################
job-debian11-unit-test:
extends:
- .unit-test
- .debian11-image
needs:
- job-debian11-ninja-clang
after_script:
- ls -lah #default.profraw should exist
- llvm-profdata merge -sparse default.profraw -o default.profdata
artifacts:
paths:
- default.profdata
- work/lib/libflexisip.so
debian11-coverage:
stage: coverage 📑
tags: [ "docker-test-flexisip" ]
extends:
- .rules-dev
- .debian11-image
needs:
- job-debian11-unit-test
script:
- export TERM=xterm-color
- echo "Full coverage of Flexisip library"
- llvm-cov show $PWD/work/lib/libflexisip.so -instr-profile=default.profdata include/flexisip src --ignore-filename-regex=src\/lib\/.* -use-color > flexisip_coverage_lines_full.txt
- echo "Display coverage by file of Flexisip library"
- llvm-cov report $PWD/work/lib/libflexisip.so -instr-profile=default.profdata include/flexisip src --ignore-filename-regex=src\/lib\/.* -use-color | tee flexisip_coverage_report_by_file.txt
- echo "filtering work of report to get global coverage %"
- cat flexisip_coverage_report_by_file.txt| grep -E "TOTAL" | grep -Po "\d+\.\d+\%" | sed -n '3p'
- echo "Export results in JSON format"
- llvm-cov export $PWD/work/lib/libflexisip.so -instr-profile=default.profdata -format=text include/flexisip src --ignore-filename-regex=src\/lib\/.* -use-color > flexisip_coverage_export.txt
- echo "Export results in Lcov format"
- llvm-cov export $PWD/work/lib/libflexisip.so -instr-profile=default.profdata -format=lcov include/flexisip src --ignore-filename-regex=src\/lib\/.* -use-color > flexisip_coverage_export.lcov
- lcov_cobertura flexisip_coverage_export.lcov
coverage: '/[0-9][0-9]\.[0-9]+\%/'
artifacts:
reports:
coverage_report:
coverage_format: cobertura
path: coverage.xml
paths:
- default.profdata
- flexisip_coverage_report_by_file.txt
- flexisip_coverage_lines_full.txt
- flexisip_coverage_export.lcov
- flexisip_coverage_export.txt
- coverage.xml
expire_in: 1 week
#################################################
# DEB
#################################################
job-debian11-deb:
extends:
- .job-linux-deb
- .debian11-image
needs:
- job: job-debian11-unit-test
optional: true
artifacts: false
- job: job-debian11-ninja-clang
optional: true
artifacts: false
job-debian11-deb-deploy:
extends: .job-debian-deb-deploy
variables:
RELEASE: bullseye
FREIGHT_PATH: $DEBIAN_FREIGHT_CONF_PATH
dependencies:
- job-debian11-deb

View file

@ -0,0 +1,95 @@
.debian12-image:
image:
name: gitlab.linphone.org:4567/bc/public/flexisip/bc-dev-debian12:$DEBIAN_12_IMAGE_VERSION
#################################################
# Makefile
#################################################
job-debian12-makefile-gcc:
extends:
- .job-makefile-gcc
- .debian12-image
- .rules-manual # ⚠ See `rules.yml`, override .job-makefile-gcc rules
job-debian12-makefile-clang:
extends:
- .job-makefile-clang
- .debian12-image
- .rules-manual # ⚠ See `rules.yml`, override .job-makefile-clang rules
#################################################
# Ninja
#################################################
job-debian12-ninja-gcc:
extends:
- .job-ninja-gcc
- .debian12-image
# Debian 12 packages GCC 12 (fitting!) for which our cmake has special-caseing,
# so let's override the .job-ninja-gcc rules to run it in dev pipelines
- .rules-dev # ⚠ See `rules.yml`
job-debian12-ninja-clang:
extends:
- .job-ninja-clang
- .debian12-image
- .tester-artifacts
variables:
CMAKE_OPTIONS: -DENABLE_UNIT_TESTS=ON
#################################################
# UNIT TESTS
#################################################
job-debian12-unit-test:
extends:
- .unit-test
- .debian12-image
needs:
- job-debian12-ninja-clang
artifacts:
paths:
- work/lib/libflexisip.so
#################################################
# DEB
#################################################
job-debian12-deb:
extends:
- .job-linux-deb
- .debian12-image
needs:
- job: job-debian12-unit-test
optional: true
artifacts: false
- job: job-debian12-ninja-clang
optional: true
artifacts: false
# Test installation of the DEB package and check its feature list
job-debian12-deb-check-features:
stage: check-package 📤
tags: [ "docker" ]
image: debian:12
rules:
- !reference [job-debian12-deb, rules]
needs:
- job: job-debian12-deb
variables:
GIT_STRATEGY: none
script:
- apt update
- apt install -y ./build/*.deb
- !reference [.script-check-features, script]
job-debian12-deb-deploy:
extends: .job-debian-deb-deploy
variables:
RELEASE: bookworm
FREIGHT_PATH: $DEBIAN_FREIGHT_CONF_PATH
dependencies:
- job-debian12-deb

View file

@ -0,0 +1,16 @@
.minimal-image:
image:
name: gitlab.linphone.org:4567/bc/public/flexisip/bc-dev-minimal:$MINIMAL_IMAGE_VERSION
#################################################
# Ninja
#################################################
job-minimal-ninja-gcc:
extends:
- .job-ninja-gcc
- .minimal-image
- .rules-dev # ⚠ See `rules.yml`
variables:
CMAKE_OPTIONS: -DENABLE_PRESENCE=OFF -DENABLE_REDIS=OFF -DENABLE_SNMP=OFF -DENABLE_SOCI=OFF -DENABLE_TRANSCODER=OFF -DENABLE_MDNS=OFF -DENABLE_EXTERNAL_AUTH_PLUGIN=OFF -DENABLE_JWE_AUTH_PLUGIN=OFF -DENABLE_CONFERENCE=OFF -DENABLE_SOCI_POSTGRESQL_BACKEND=OFF -DENABLE_B2BUA=OFF -DENABLE_UNIT_TESTS=OFF -DENABLE_FLEXIAPI=OFF

View file

@ -0,0 +1,92 @@
variables:
ROCKY8_CMAKE_OPTIONS: -DINTERNAL_LIBHIREDIS=ON
ROCKY8_CMAKE_OPTIONS_UNIT_TESTS: -DINTERNAL_LIBHIREDIS=ON -DENABLE_UNIT_TESTS=ON -DCMAKE_PREFIX_PATH=/usr/local -DCMAKE_INSTALL_RPATH_USE_LINK_PATH=ON
.rocky8-image-variables:
image:
name: gitlab.linphone.org:4567/bc/public/flexisip/bc-dev-rocky8:$ROCKY_8_IMAGE_VERSION
variables:
CMAKE_OPTIONS: ${ROCKY8_CMAKE_OPTIONS}
#################################################
# Makefile
#################################################
job-rocky8-makefile-gcc:
extends:
- .job-makefile-gcc
- .rocky8-image-variables
job-rocky8-makefile-clang:
extends:
- .job-makefile-clang
- .rocky8-image-variables
#################################################
# Ninja
#################################################
job-rocky8-ninja-gcc:
extends:
- .job-ninja-gcc
- .rocky8-image-variables
job-rocky8-ninja-clang:
extends:
- .job-ninja-clang
- .rocky8-image-variables
- .tester-artifacts
variables:
# -DCMAKE_PREFIX_PATH=/usr/local: Unit tests require libnghttp2_asio, which has been build and intalled into /usr/local
CMAKE_OPTIONS: ${ROCKY8_CMAKE_OPTIONS_UNIT_TESTS}
#################################################
# UNIT TESTS
#################################################
job-rocky8-unit-test:
extends:
- .unit-test
- .rocky8-image-variables
needs:
- job-rocky8-ninja-clang
#################################################
# RPM
#################################################
job-rocky8-rpm:
extends:
- .job-linux-rpm
- .rocky8-image-variables
needs:
- job: job-rocky8-unit-test
optional: true
artifacts: false
- job: job-rocky8-ninja-clang
optional: true
artifacts: false
# Test installation of the RPM package and check its feature list
job-rocky8-rpm-check-features:
stage: check-package 📤
tags: [ "docker" ]
extends:
- .rocky8-image-variables
rules:
- !reference [job-rocky8-rpm, rules]
needs:
- job: job-rocky8-rpm
variables:
GIT_STRATEGY: none
script:
- !reference [job-rocky9-rpm-check-features, script]
job-rocky8-rpm-deploy:
extends: .job-rpm-deploy
dependencies:
- job-rocky8-rpm
variables:
DISTRIB: rockylinux

View file

@ -0,0 +1,128 @@
variables:
ROCKY9_CMAKE_OPTIONS_UNIT_TESTS: -DENABLE_UNIT_TESTS=ON -DENABLE_UNIT_TESTS_MYSQL=ON -DCMAKE_PREFIX_PATH=/usr/local -DCMAKE_INSTALL_RPATH_USE_LINK_PATH=ON
.rocky9-image-variables:
image:
name: gitlab.linphone.org:4567/bc/public/flexisip/bc-dev-rocky9:$ROCKY_9_IMAGE_VERSION
#################################################
# Makefile
#################################################
job-rocky9-makefile-gcc:
extends:
- .job-makefile-gcc
- .rocky9-image-variables
job-rocky9-makefile-clang:
extends:
- .job-makefile-clang
- .rocky9-image-variables
#################################################
# Ninja
#################################################
job-rocky9-ninja-gcc:
extends:
- .job-ninja-gcc
- .rocky9-image-variables
job-rocky9-ninja-clang:
extends:
- .job-ninja-clang
- .rocky9-image-variables
- .tester-artifacts
variables:
# -DCMAKE_PREFIX_PATH=/usr/local: Unit tests require libnghttp2_asio, which has been build and intalled into /usr/local
CMAKE_OPTIONS: ${ROCKY9_CMAKE_OPTIONS_UNIT_TESTS}
job-rocky9-ninja-clang-nosoci:
extends:
- job-rocky9-ninja-clang
variables:
CMAKE_OPTIONS: ${ROCKY9_CMAKE_OPTIONS_UNIT_TESTS} -DENABLE_SOCI=OFF -DENABLE_B2BUA=ON -DENABLE_CONFERENCE=ON
#################################################
# UNIT TESTS
#################################################
job-rocky9-unit-test:
extends:
- .unit-test
- .rocky9-image-variables
needs:
- job-rocky9-ninja-clang
job-rocky9-unit-test-nosoci:
extends:
- job-rocky9-unit-test
needs:
- job-rocky9-ninja-clang-nosoci
#################################################
# XWiki reference documentation
#################################################
job-rocky9-xwiki-doc-deploy:
stage: deploy 🚀
tags: [ "docker" ]
extends:
- .rules-deploy # ⚠ See `rules.yml`
- .rocky9-image-variables
script:
- prefix=/opt/belledonne-communications
- builddir=wikiBuild
- sudo mkdir -p $prefix
- sudo chown bc:bc $prefix
- mkdir $builddir
- cmake -S . -B $builddir -G Ninja -DCMAKE_INSTALL_PREFIX=$prefix -DCMAKE_PREFIX_PATH=$prefix $DEFAULT_CMAKE_OPTIONS
- cmake --build $builddir
- /usr/bin/python3 ./doc/xw.py --flexisip-binary $builddir/bin/flexisip -H $XWIKI_HOSTNAME -u $XWIKI_USERNAME -p $XWIKI_PASSWORD
#################################################
# RPM
#################################################
job-rocky9-rpm:
extends:
- .job-linux-rpm
- .rocky9-image-variables
needs:
- job: job-rocky9-unit-test
optional: true
artifacts: false
- job: job-rocky9-unit-test-nosoci
optional: true
artifacts: false
- job: job-rocky9-ninja-clang
optional: true
artifacts: false
- job: job-rocky9-ninja-clang-nosoci
optional: true
artifacts: false
# Test installation of the RPM package and check its feature list
job-rocky9-rpm-check-features:
stage: check-package 📤
tags: [ "docker" ]
extends:
- .rocky9-image-variables
rules:
- !reference [job-rocky9-rpm, rules]
needs:
- job: job-rocky9-rpm
variables:
GIT_STRATEGY: none
script:
- sudo yum -y --nogpgcheck localinstall ./build/*.rpm
- !reference [.script-check-features, script]
job-rocky9-rpm-deploy:
extends: .job-rpm-deploy
dependencies:
- job-rocky9-rpm
variables:
DISTRIB: rockylinux

View file

@ -0,0 +1,77 @@
.ubuntu-22-04-image:
image:
name: gitlab.linphone.org:4567/bc/public/flexisip/bc-dev-ubuntu-22-04:$UBUNTU_22_04_IMAGE_VERSION
#################################################
# Makefile
#################################################
job-ubuntu-22-04-makefile-gcc:
extends:
- .job-makefile-gcc
- .ubuntu-22-04-image
- .rules-never-run # ⚠ See `rules.yml`, override .job-makefile-gcc rules
job-ubuntu-22-04-makefile-clang:
extends:
- .job-makefile-clang
- .ubuntu-22-04-image
- .rules-never-run # ⚠ See `rules.yml`, override .job-makefile-clang rules
#################################################
# Ninja
#################################################
job-ubuntu-22-04-ninja-gcc:
extends:
- .job-ninja-gcc
- .ubuntu-22-04-image
job-ubuntu-22-04-ninja-clang:
extends:
- .job-ninja-clang
- .ubuntu-22-04-image
- .tester-artifacts
variables:
CMAKE_OPTIONS: -DENABLE_UNIT_TESTS=ON
#################################################
# UNIT TESTS
#################################################
job-ubuntu-22-04-unit-test:
extends:
- .unit-test
- .ubuntu-22-04-image
needs:
- job-ubuntu-22-04-ninja-clang
#################################################
# DEB
#################################################
job-ubuntu-22-04-deb:
extends:
- .job-linux-deb
- .ubuntu-22-04-image
- .rules-manual-deploy # ⚠ See `rules.yml`, override .job-linux-deb rules
needs:
- job: job-ubuntu-22-04-unit-test
optional: true
artifacts: false
- job: job-ubuntu-22-04-ninja-clang
optional: true
artifacts: false
job-ubuntu-22-04-deb-deploy:
extends:
- .job-debian-deb-deploy
- .ubuntu-22-04-image
variables:
RELEASE: jammy
FREIGHT_PATH: $UBUNTU_FREIGHT_CONF_PATH
dependencies:
- job-ubuntu-22-04-deb

View file

@ -0,0 +1,77 @@
.ubuntu-24-04-image:
image:
name: gitlab.linphone.org:4567/bc/public/flexisip/bc-dev-ubuntu-24-04:$UBUNTU_24_04_IMAGE_VERSION
#################################################
# Makefile
#################################################
job-ubuntu-24-04-makefile-gcc:
extends:
- .job-makefile-gcc
- .ubuntu-24-04-image
- .rules-never-run # ⚠ See `rules.yml`, override .job-makefile-gcc rules
job-ubuntu-24-04-makefile-clang:
extends:
- .job-makefile-clang
- .ubuntu-24-04-image
- .rules-never-run # ⚠ See `rules.yml`, override .job-makefile-clang rules
#################################################
# Ninja
#################################################
job-ubuntu-24-04-ninja-gcc:
extends:
- .job-ninja-gcc
- .ubuntu-24-04-image
job-ubuntu-24-04-ninja-clang:
extends:
- .job-ninja-clang
- .ubuntu-24-04-image
- .tester-artifacts
variables:
CMAKE_OPTIONS: -DENABLE_UNIT_TESTS=ON
#################################################
# UNIT TESTS
#################################################
job-ubuntu-24-04-unit-test:
extends:
- .unit-test
- .ubuntu-24-04-image
needs:
- job-ubuntu-24-04-ninja-clang
#################################################
# DEB
#################################################
job-ubuntu-24-04-deb:
extends:
- .job-linux-deb
- .ubuntu-24-04-image
- .rules-manual-deploy # ⚠ See `rules.yml`, override .job-linux-deb rules
needs:
- job: job-ubuntu-24-04-unit-test
optional: true
artifacts: false
- job: job-ubuntu-24-04-ninja-clang
optional: true
artifacts: false
job-ubuntu-24-04-deb-deploy:
extends:
- .job-debian-deb-deploy
- .ubuntu-24-04-image
variables:
RELEASE: noble
FREIGHT_PATH: $UBUNTU_FREIGHT_CONF_PATH
dependencies:
- job-ubuntu-24-04-deb

View file

@ -0,0 +1,413 @@
#################################################
# Global
#################################################
.set-max-jobs: &set-max-jobs
- |
case "$CMAKE_GENERATOR" in
'Unix Makefiles')
export CMAKE_BUILD_PARALLEL_LEVEL=$MAKEFILE_JOBS ;;
'Ninja')
export CMAKE_BUILD_PARALLEL_LEVEL=$NINJA_JOBS ;;
*)
export CMAKE_BUILD_PARALLEL_LEVEL=1 ;;
esac
echo "CMAKE_BUILD_PARALLEL_LEVEL='$CMAKE_BUILD_PARALLEL_LEVEL'"
.linux-ccache:
cache:
key: $CI_JOB_NAME
paths:
- ccache/
before_script:
- git submodule foreach --recursive git fetch --tags --force
- "[[ -f /opt/rh/devtoolset-8/enable ]] && source /opt/rh/devtoolset-8/enable"
- mkdir -p ccache
- echo "max_size = $CCACHE_SIZE" > ccache/ccache.conf
- export CCACHE_BASEDIR=${PWD}
- export CCACHE_DIR=${PWD}/ccache
- ccache -s
after_script:
- export CCACHE_DIR=${PWD}/ccache
- ccache -s
.job-linux-build:
stage: build 🏗
tags: [ "docker" ]
extends: .linux-ccache
script:
- *set-max-jobs
- mkdir work && cd work
- cmake -G "$CMAKE_GENERATOR" $DEFAULT_CMAKE_OPTIONS -DCMAKE_INSTALL_PREFIX="$PWD/../OUTPUT" $CMAKE_OPTIONS ..
- cmake --build .
# Files required by the flexisip_tester bin
.tester-artifacts:
artifacts:
paths:
- work/bin/flexisip_tester
- work/lib{,64}/
- "**grammar"
- tester/cert
- tester/config
- tester/images
- tester/scripts
- tester/sounds
#################################################
# RPM
#################################################
.job-linux-rpm:
stage: package 📦
tags: [ "docker" ]
extends:
- .rules-default # ⚠ See `rules.yml`
variables:
CMAKE_GENERATOR: 'Ninja'
script:
- "[[ -f /opt/rh/devtoolset-8/enable ]] && source /opt/rh/devtoolset-8/enable"
- *set-max-jobs
- mkdir build && cd build
- cmake -G $CMAKE_GENERATOR $DEFAULT_PACKAGING_CMAKE_OPTIONS $CMAKE_OPTIONS -DCPACK_GENERATOR=RPM ..
- cmake --build . --target package
artifacts:
paths:
- build/*.rpm
- .gitlab-ci-files/deploy.sh
when: always
expire_in: 1 week
.job-rpm-deploy:
stage: deploy 🚀
tags: [ "deploy" ]
extends: .rules-deploy # ⚠ See `rules.yml`
variables:
# Do not clone, only use artifacts (TODO 'none' doesn't clean, we are using 'fetch' until we find a way)
GIT_STRATEGY: fetch
script: ./.gitlab-ci-files/deploy.sh ${DISTRIB}
#################################################
# DEB
#################################################
.job-linux-deb:
stage: package 📦
tags: [ "docker" ]
extends:
- .rules-default # ⚠ See `rules.yml`
variables:
CMAKE_GENERATOR: 'Ninja'
script:
- *set-max-jobs
- mkdir build && cd build
- cmake -G $CMAKE_GENERATOR $DEFAULT_PACKAGING_CMAKE_OPTIONS $CMAKE_OPTIONS -DCPACK_GENERATOR=DEB ..
- cmake --build . --target package
artifacts:
paths:
- build/*.deb
- build/*.ddeb
when: always
expire_in: 1 week
.job-debian-deb-deploy:
stage: deploy 🚀
tags: [ "deploy" ]
extends: .rules-deploy # ⚠ See `rules.yml`
variables:
# Do not clone, only use artifacts (TODO 'none' doesn't clean, we are using 'fetch' until we find a way)
GIT_STRATEGY: fetch
script: ./.gitlab-ci-files/deploy.sh debian
#################################################
# DOCKER
#################################################
.job-flexisip-image-deploy:
stage: image
tags: [ "linux-deploy" ]
extends: .rules-deploy # ⚠ See `rules.yml`
script:
- mkdir -p docker/DEBS && mv build/*.{deb,ddeb} docker/DEBS
- cd docker
- make flexisip-deb-build
- make flexisip-deb-push
# Enable this to use tests-flexisip-mr
#docker-build-flexisip-src:
# stage: build 🏗
# tags: [ "linux-nuc-build" ]
# extends: .rules-dev # ⚠ See `rules.yml`
# variables:
# DOCKER_BUILD_OPTIONS: "--no-cache --force-rm -t flexisip-from-src:$CI_COMMIT_REF_SLUG-$CI_COMMIT_SHA -f docker/flex-from-src --build-arg=njobs=12 --build-arg=build_type=RelWithDebInfo"
# script:
# - docker image ls
# - echo $DOCKER_BUILD_OPTIONS
# - docker build $DOCKER_BUILD_OPTIONS .
# - docker image ls
#################################################
# Nightly only
#################################################
.job-linux-build-nightly:
extends:
- .job-linux-build
- .rules-nightly # ⚠ See `rules.yml`
#################################################
# Build Makefile
#################################################
.job-makefile-gcc:
extends: .job-linux-build-nightly
variables:
CMAKE_GENERATOR: Unix Makefiles
CC: gcc
CXX: g++
.job-makefile-clang:
extends: .job-linux-build-nightly
variables:
CMAKE_GENERATOR: Unix Makefiles
CC: clang
CXX: clang++
#################################################
# Build Ninja
#################################################
.job-ninja-gcc:
extends:
- .job-linux-build
- .rules-manual # ⚠ See `rules.yml`
variables:
CMAKE_GENERATOR: Ninja
CC: gcc
CXX: g++
.job-ninja-clang:
extends:
- .job-linux-build
- .rules-dev # ⚠ See `rules.yml`
variables:
CMAKE_GENERATOR: Ninja
CC: clang
CXX: clang++
#################################################
# Tests
#################################################
.unit-test:
stage: test 🧪
tags: [ "docker-test-flexisip" ]
extends: .rules-dev # ⚠ See `rules.yml`
variables:
# Do not clone, only use artifacts (TODO 'none' doesn't clean, we are using 'fetch' until we find a way)
GIT_STRATEGY: fetch
script:
- export LD_LIBRARY_PATH=/usr/local/lib
- export LSAN_OPTIONS="suppressions=./sanitizer_ignore.txt"
- ./work/bin/flexisip_tester --verbose --xml || exit $?
artifacts:
reports:
junit:
- BCUnitAutomated-Results.xml
when: always
expire_in: 1 week
# Disabled because of too much random test errors for a valid result for now.
#
# tests-flexisip-mr:
# stage: test 🧪
# tags: [ "linux-nuc"]
# allow_failure: true
# extends: .rules-dev # ⚠ See `rules.yml`
# needs :
# - docker-build-flexisip-src
# variables:
# #Branch of the Flexisip-tester project to use. Could be modified to test fixes or features in the Flexisip-tester
# FLEXISIP_TESTER_BRANCH: "release/flexisip_2.2"
#
# #This value specifies the workspace where we from all tests outputs.
# workspace: "${CI_PROJECT_DIR}/liblinphone_tester_workspace"
#
# #Flexisip docker image is build from source in the build stage (job docker-build-flexisip-src)
# #No need to modify these value
# FLEXISIP_DOCKER_TAG: "$CI_COMMIT_REF_SLUG-$CI_COMMIT_SHA"
#
# # Docker run options are only used for coredump display if any
# # We are forced to copy the same string as "workspace" because we can't use nested variables in the same place (i.e. in variable declaration)
# docker_run_options: "-v ${CI_PROJECT_DIR}/liblinphone_tester_workspace:/home/bc/linphone-sdk-build/linphone-sdk/desktop/work"
# flexisip_docker_run_options: "-v ${CI_PROJECT_DIR}/liblinphone_tester_workspace:/root"
#
# script:
# # Retrieve the SDK commit sha used by Flexisip
# - cd linphone-sdk
# - export LIBLINPHONE_DOCKER_TAG=$(git rev-parse --verify HEAD)
# - echo "SDK commit sha " $LIBLINPHONE_DOCKER_TAG
# - cd ..
#
# # used to ensure there will be not network name conflict for parallel
# # docker-compose executions
# - export COMPOSE_PROJECT_NAME=$RANDOM
# - echo "Compose project name " $COMPOSE_PROJECT_NAME
#
# - export FLEXISIP_TESTER_IPV4_PREFIX="172.0.`expr $COMPOSE_PROJECT_NAME % 255`"
# - echo "Flexisip tester private ipv4 prefix " $FLEXISIP_TESTER_IPV4_PREFIX
#
# - export FLEXISIP_TESTER_IPV6_SUBNET="2001:3200:3200:`printf '%x\n' $COMPOSE_PROJECT_NAME`::/64"
# - echo "Flexisip tester private ipv6 subnet " $FLEXISIP_TESTER_IPV6_SUBNET
#
# - export FLEXISIP_TESTER_IPV6_GATEWAY="2001:3200:3200:`printf '%x\n' $COMPOSE_PROJECT_NAME`::1"
# - export FLEXISIP_TESTER_IPV6_PROBING_ADDR=$FLEXISIP_TESTER_IPV6_GATEWAY
# - echo "Flexisip tester private ipv6 gateway" $FLEXISIP_TESTER_IPV6_GATEWAY
#
# - echo "Liblinphone tester version:" $LIBLINPHONE_DOCKER_TAG
# - echo $workspace
#
# - git clone -b $FLEXISIP_TESTER_BRANCH --single-branch git@gitlab.linphone.org:BC/private/flexisip-tester --jobs 12
#
# #We make sure that the variables we set here can be accessed by the docker-compose scripts (in the flexisip-tester project).
# - export FLEXISIP_DOCKER_TAG="$CI_COMMIT_REF_SLUG-$CI_COMMIT_SHA"
# - export LIBLINPHONE_DOCKER_TAG=$LIBLINPHONE_DOCKER_TAG
#
# - |
# if [ -z $workspace ]; then
# echo "Error, $workspace should be set. Aborting to avoid unwanted rm"
# exit 1
# fi
#
# - mkdir -p $workspace
# - rm -rf $workspace/*
#
# - mkdir -p $workspace/{bin,flexiapi,ext,lime,lime-ext}
#
# # workaround for logs writing
# - chmod -R 777 $workspace
#
# # Handle multiple runs by unsetting variable
# # Overriding docker-compose.yaml values with docker-compose-standalone.yaml
# # in the ways specified with docker docs (either OR or AND, depending on key)
#
# - |
# docker_compose_options="--env-file ${CI_PROJECT_DIR}/flexisip-tester/docker/.env"
# for name in 'docker-compose.yaml' 'docker-compose-standalone.yaml'; do
# docker_compose_options="$docker_compose_options -f ${CI_PROJECT_DIR}/flexisip-tester/docker/$name"
# done
#
# #Tester's options like parallel mode are defined in the docker-compose-standalone.yaml file of the flexisip-tester project
#
# - export FLEXISIP_LOGS="$workspace"
# - export LIBLINPHONE_LOGS="$workspace"
#
# - export FLEXISIP_WORKSPACE="$workspace"
#
# - echo $docker_run_options
#
# - cd $workspace
#
# # Stop and remove containers, networks and volumes from previous tests.
# # Already done after tests, should do nothing except if previous job crashed.
# - docker-compose $docker_compose_options down --volumes --remove-orphans
#
# - echo "COMPOSE_PROJECT_NAME=$COMPOSE_PROJECT_NAME" > savedEnv
#
# # commented -d option to have debug logs on standard output in case of
# # buggy or silent log artifacts
# # We output only liblinphone_tester on stdout
#
# #-V, --renew-anon-volumes Recreate anonymous volumes instead of retrieving data from the previous containers.
# #--exit-code-from Exits with the return code from the specified container (implies --abort-on-container-exit)
# #In the grep we are searching only for the logs related to liblinphone_tester and docker build, so we remove output from all other services.
#
# - docker-compose $docker_compose_options up --build -V --exit-code-from liblinphone_tester |& tee logs_all | grep -vE 'redis_server[_-]1|flexisip[_-]1|flexisip-regevent[_-]1|flexisip-external[_-]1|flexisip-external-domain[_-]1|lime-server[_-]1|lime-server[_-]1|lime-external-server[_-]1|dbserver[_-]1|account-manager[_-]1|file-transfer-server[_-]1|http-proxy[_-]1|ldap-server[_-]1' || EXIT=$?
#
# #docker-compose log command isn't used anymore because of bad performance
# #the logs were converted by docker in json format, then reconverted to txt, which is useless (doing this made the job last for 20-30 minutes for 9 minutes of tests)
#
# - |
# for service in $(docker-compose $docker_compose_options ps --services); do
# if [[ "${service}" == "flexisip" ]]; then
# continue
# fi
# filename="${service}_stdout.log"
# echo -n "Writting ${filename}..."
# grep $service logs_all >> ${filename}.log
# echo " done"
# done
#
# #we wait for each grep to end before stopping containers
# - wait
#
# # The sleep command was removed because we now wait for the db socket to open (in compose file) before launching tests
#
# # Stop and remove containers, networks and volumes from previous tests
# - docker-compose $docker_compose_options down --volumes --remove-orphans || true
#
# - gzip *.log
#
# - gzip */*.log
#
# - exit $EXIT
#
# after_script:
#
# #content of $workspace (set like this because the variable can't be resolved here)
# - cd ${CI_PROJECT_DIR}/liblinphone_tester_workspace
# # we specify commands to launch for each coredump of liblinphone_tester
# - echo "set debug-file-directory ../lib64" | tee gdb_options
# - echo "thread apply all bt" | tee -a gdb_options
# # searching for core files and if there are some, launch gdb on all of it
# # xargs -L1 means that the command in argument will be executed for each
# # line (core dump) found in find output
# # The docker syntax is error proning : to override the entrypoint with
# # args, we enter the entrypoint first, then the name of the image, then the
# # args to the entrypoint command.
# # "|| true " is used here to continue the script even if the find fails
# - FIND_EXIT=0
# - find . -type f -name "core.liblin*" | grep . || FIND_EXIT=$?
# - echo $FIND_EXIT
# - if [[ $FIND_EXIT = 0 ]]; then find . -type f -name "core.liblin*" | xargs -L1 docker run $docker_run_options --entrypoint gdb "$LIBLINPHONE_DOCKER_IMAGE:$LIBLINPHONE_DOCKER_TAG" ../bin/liblinphone_tester -x gdb_options; fi || true
# - unset FIND_EXIT
# # we specify commands to launch for each coredump of flexisip_tester
# - echo "set debug-file-directory /opt/belledonne-communications/lib" | tee gdb_options
# - echo "thread apply all bt" | tee -a gdb_options
# - echo $flexisip_docker_run_options
# - find . -type f -name "core.flexisip*"
# - echo $FLEXISIP_DOCKER_IMAGE
# - echo $FLEXISIP_DOCKER_TAG
# - echo $flexisip_docker_run_options
# - FIND_EXIT=0
# - find . -type f -name "core.flexisip*" | grep . || FIND_EXIT=$?
# - echo $FIND_EXIT
# - if [[ $FIND_EXIT = 0 ]]; then find . -type f -name "core.flexisip*" | xargs -L1 docker run $flexisip_docker_run_options --entrypoint gdb "$FLEXISIP_DOCKER_IMAGE:$FLEXISIP_DOCKER_TAG" /opt/belledonne-communications/bin/flexisip -x gdb_options; fi || true
#
# # simplifing artifacts browsing
# # Moving artifacts to ease browsing from web view
# # initially, all the paths needed to be browsed entirely to see artifacts
# # now there is only the folder "results" to check
# - mkdir -p ${CI_PROJECT_DIR}/results/ext
# - chmod 777 ${CI_PROJECT_DIR}/results
# - cp -r BCUnitAutomated* ${CI_PROJECT_DIR}/results
# - cp -r *.log.gz ${CI_PROJECT_DIR}/results
# - cp -r ext/*.log* ${CI_PROJECT_DIR}/results/ext
#
# # Remove network (useful in case of crash or cancel), loading the random generated project name saved during execution
# - source savedEnv
# - docker network rm "$COMPOSE_PROJECT_NAME"_default "$COMPOSE_PROJECT_NAME"_flexisip_internal_private || true
#
# artifacts:
# paths:
# - results/*
# when: always
# reports:
# junit:
# - liblinphone_tester_workspace/BCUnitAutomated-Results.xml
# expire_in: 4 week

View file

@ -0,0 +1,55 @@
.job-macosx:
stage: build 🏗
tags: [ "macosx-min-xcode12.2" ]
# Temporarily allows macosx jobs to fail because the machine that
# was executing the macOS jobs is broken. Remove when it is
# fixed.
allow_failure: true
script:
- ccache -s
- export OPENSSL_ROOT_DIR=/usr/local/opt/openssl
- export MYSQL_DIR=/usr/local/opt/mysql-client
- export CMAKE_BUILD_PARALLEL_LEVEL=$NJOBS
- mkdir work
- cmake -S . -B work -G "$CMAKE_GENERATOR" $DEFAULT_MACOS_CMAKE_OPTIONS $CMAKE_OPTIONS -DCMAKE_PREFIX_PATH=/usr/local/opt/mbedtls@2 -DENABLE_SOCI_POSTGRESQL_BACKEND=OFF -DENABLE_UNIT_TESTS=ON -DENABLE_UNIT_TESTS_NGHTTP2ASIO=OFF
- cmake --build work -- $ADDITIONAL_BUILD_OPTIONS
- ccache -s
#################################################
# Makefile
#################################################
job-macosx-makefile:
extends:
- .job-macosx
- .rules-nightly # ⚠ See `rules.yml`
variables:
CMAKE_GENERATOR: Unix Makefiles
NJOBS: $MAKEFILE_JOBS
#################################################
# Ninja
#################################################
job-macosx-ninja:
extends:
- .job-macosx
- .rules-dev # ⚠ See `rules.yml`
variables:
CMAKE_GENERATOR: Ninja
NJOBS: $NINJA_JOBS
#################################################
# Xcode
#################################################
job-macosx-xcode:
extends:
- .job-macosx
- .rules-nightly # ⚠ See `rules.yml`
variables:
CMAKE_GENERATOR: Xcode
NJOBS: $MAX_NUMBER_TASK_XCODE

View file

@ -0,0 +1,83 @@
# https://docs.gitlab.com/ee/ci/yaml/index.html#rules
#
# All `rules` should be gathered in this file.
#
# Gitlab lets us merge job definitions through inheritance with the `extends` section.
# However, if two jobs merged in this fashion define the same section (e.g. `rules`) the sections themselves will not be
# merged: the last will override the first. In the case of arrays (like `scripts` or `rules`) this is a problem,
# because we usually would like to merge them, but there is no mechanism to do so.
#
# A workaround is to use YAML anchors (`&` and `*`) to reuse previously defined `rules` items, and ease refactoring.
# However, YAML anchors only work within the same file (so not across Gitlab `include` boundaries).
# Hence: this file.
#
# Every time you need a new set of `rules` for a job, you SHOULD define it here, reusing other `rules` items as
# building blocks, then use `extends` to apply it to your job.
# (This is to ease maintenance, as rules can then be changed quickly in one place.)
#
# You SHOULD NOT write `rules` outside this file. (This is to ease debugging, because, as explained above, `rules` can
# override one another in an inheritance tree. This is not a strict rule however, and can be bypassed when justified.)
#
# This file MUST only contain hidden jobs (beginning with `.`) and these jobs MUST only contain a `rules` section
# Jobs that always run with the default gitlab policy
.rules-default:
rules:
- &manual-override
if: $MANUAL_OVERRIDE
when: manual
allow_failure: true
- &otherwise-run # Default gitlab policy that we have to explicitely add back
when: on_success
# Jobs that always run, except for DEPLOY_ONLY pipelines.
.rules-dev:
rules:
- *manual-override
- &exclude-from-deploy
if: $DEPLOY_ONLY
when: never
- *otherwise-run
# Jobs that only run in NIGHTLY pipelines
.rules-nightly:
rules:
- *manual-override
- if: '$NIGHTLY == null'
when: never
- *exclude-from-deploy
- *otherwise-run
# Jobs that run in NIGHTLY pipelines, are added in manual mode to all pipelines, except for DEPLOY_ONLY ones.
.rules-manual:
rules:
- *exclude-from-deploy
- if: '$NIGHTLY != null'
when: on_success
- when: manual
allow_failure: true # This allows pipeline to continue without blocking on only manual jobs.
# Jobs that run in NIGHTLY pipelines, are added in manual mode to all pipelines.
.rules-manual-deploy:
rules:
- if: '$NIGHTLY != null'
when: on_success
- when: manual
allow_failure: true # This allows pipeline to continue without blocking on only manual jobs.
# Jobs that run only for DEPLOY or DEPLOY_ONLY pipelines.
.rules-deploy:
rules:
- *manual-override
- if: $DEPLOY
when: on_success
- if: $DEPLOY_ONLY
when: on_success
- when: never
# Use this rule when you don't want a job to be run but still want to make it easily available by removing this rule.
# e.g. when you are adding a new platform or upgrading an existing one, and you want to run thorough tests
.rules-never-run:
rules:
- *manual-override
- when: never

View file

@ -0,0 +1,16 @@
# This file MUST only contain hidden jobs (beginning with `.`)
# Check available features of an installed flexisip binary
.script-check-features:
script:
- FLEXISIP_BIN=/opt/belledonne-communications/bin/flexisip
- VERSION_STRING=$($FLEXISIP_BIN --version)
- echo $VERSION_STRING | grep B2BUA
- echo $VERSION_STRING | grep Conference
- echo $VERSION_STRING | grep Presence
- echo $VERSION_STRING | grep Redis
- echo $VERSION_STRING | grep RegEvent
- echo $VERSION_STRING | grep Transcoder
- echo $VERSION_STRING | grep Soci
# No graphical dependencies
- "! ldd $FLEXISIP_BIN | grep --extended-regexp 'X|GL'"

67
.gitlab-ci.yml Normal file
View file

@ -0,0 +1,67 @@
#################################################
# Base configuration
#################################################
variables:
GIT_SUBMODULE_STRATEGY: recursive
GIT_SUBMODULE_UPDATE_FLAGS: --jobs 8
CCACHE_SIZE: 2G
# For build and test on Linux
DEFAULT_CMAKE_OPTIONS: '-DCMAKE_BUILD_TYPE=Debug'
# For build on MacOS
DEFAULT_MACOS_CMAKE_OPTIONS: ''
# For packaging, deb and rpm
DEFAULT_PACKAGING_CMAKE_OPTIONS: '-DCMAKE_BUILD_TYPE=RelWithDebInfo -DCMAKE_INSTALL_PREFIX=/opt/belledonne-communications -DSYSCONF_INSTALL_DIR=/etc -DFLEXISIP_SYSTEMD_INSTALL_DIR=/usr/lib/systemd/system'
# Docker image version
ARCHLINUX_IMAGE_VERSION: 20250127_update_clang
ROCKY_8_IMAGE_VERSION: 20240911_remove_protobuf
ROCKY_9_IMAGE_VERSION: 20240911_remove_protobuf
DEBIAN_10_IMAGE_VERSION: 220240911_remove_protobuf
DEBIAN_11_IMAGE_VERSION: 20240911_remove_protobuf
DEBIAN_12_IMAGE_VERSION: 20240911_remove_protobuf
MINIMAL_IMAGE_VERSION: 20240911_remove_protobuf
UBUNTU_22_04_IMAGE_VERSION: 20240911_remove_protobuf
UBUNTU_24_04_IMAGE_VERSION: 20240911_remove_protobuf
#https://docs.gitlab.com/ee/ci/yaml/index.html#workflow
workflow:
rules:
# Allow merge request pipelines
- if: $CI_PIPELINE_SOURCE == "merge_request_event" && $CI_MERGE_REQUEST_TITLE !~ /^Draft:.*/
# Allow scheduled pipelines
- if: $CI_PIPELINE_SOURCE == "schedule"
before_script:
- |
if [ "$GIT_STRATEGY" != "none" ]; then
git submodule foreach --recursive git fetch --tags --force
fi
#################################################
# Platforms to test
#################################################
include:
- '.gitlab-ci-files/rules.yml'
- '.gitlab-ci-files/scripts.yml'
- '.gitlab-ci-files/job-linux.yml'
- '.gitlab-ci-files/job-linux-archlinux.yml'
- '.gitlab-ci-files/job-linux-rocky8.yml'
- '.gitlab-ci-files/job-linux-rocky9.yml'
- '.gitlab-ci-files/job-linux-debian11.yml'
- '.gitlab-ci-files/job-linux-debian12.yml'
- '.gitlab-ci-files/job-linux-minimal.yml'
- '.gitlab-ci-files/job-linux-ubuntu-22-04.yml'
- '.gitlab-ci-files/job-linux-ubuntu-24-04.yml'
- '.gitlab-ci-files/job-macosx.yml'
stages:
- build 🏗
- test 🧪
- coverage 📑
- package 📦
- check-package 📤
- deploy 🚀

15
.gitmodules vendored Normal file
View file

@ -0,0 +1,15 @@
[submodule "submodules/externals/sofia-sip"]
path = submodules/externals/sofia-sip
url = https://gitlab.linphone.org/BC/public/external/sofia-sip.git
[submodule "submodules/externals/hiredis"]
path = submodules/externals/hiredis
url = https://gitlab.linphone.org/BC/public/external/hiredis.git
[submodule "submodules/externals/jansson"]
path = submodules/externals/jansson
url = https://gitlab.linphone.org/BC/public/external/jansson.git
[submodule "submodules/externals/jose"]
path = submodules/externals/jose
url = https://gitlab.linphone.org/BC/public/external/jose.git
[submodule "linphone-sdk"]
path = linphone-sdk
url = https://gitlab.linphone.org/BC/public/linphone-sdk.git

27
.project Normal file
View file

@ -0,0 +1,27 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>flexisip</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.eclipse.cdt.managedbuilder.core.genmakebuilder</name>
<triggers>clean,full,incremental,</triggers>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder</name>
<triggers>full,incremental,</triggers>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.eclipse.cdt.core.cnature</nature>
<nature>org.eclipse.cdt.core.ccnature</nature>
<nature>org.eclipse.cdt.managedbuilder.core.managedBuildNature</nature>
<nature>org.eclipse.cdt.managedbuilder.core.ScannerConfigNature</nature>
</natures>
</projectDescription>

12
AUTHORS Normal file
View file

@ -0,0 +1,12 @@
Copyright Belledonne Communications SARL, all rights reserved.
Authors:
Simon Morlat
Guillaume Beraudo
Jehan Monnier
Guillaume Bienkoswki
Sylvain Berfini
Margaux Clerc
Ghislain Mary
Gautier Pelloux-Prayer
Yann Diorcet

664
CHANGELOG.md Normal file
View file

@ -0,0 +1,664 @@
# Change Log
All notable changes to this project will be documented in this file.
Group changes to describe their impact on the project, as follows:
| **Group name** | **Description** |
|----------------|-----------------------------------------------------------------------------|
| Added | New features |
| Changed | Changes in existing functionality |
| Deprecated | Once-stable features removed in upcoming releases |
| Removed | Deprecated features removed in this release |
| Fixed | Any bug fixes |
| Security | To invite users to upgrade in case of vulnerabilities |
| Known Issues | Issues whose fix has not been tested and cannot be included in this release |
## [2.4.1]
### [Added]
- **B2BUA, Conference, Presence, Proxy, RegEvent:** periodically log the server memory usage (on Linux and with debug
log level).
### [Fixed]
- **B2BUA, Conference, Presence, Proxy, RegEvent:** error in Flexisip startup phase (daemon mode) caused the watchdog process to freeze
- **RegEvent:** server did not support several subscriptions to the same record key
- **RegEvent:** not disabling call-logs and zrtp-secrets DBs caused crash at init
- **Sofia-SIP:** idle-timeout was not set in TLS connections if no message was received
- **Proxy:** invalid P-Preferred-Identity could lead to crash
- **B2BUA, Conference, Presence, Proxy, RegEvent:** watchdog logs could not be printed into journald
- **Conference server:** compatibility issue between conference server using linphone-SDK 5.3 and clients using linphone-SDK 5.4.
- **Proxy:** drastically improved performances when retrieving undelivered chat messages from the database at startup.
## [2.4.0] - 2025-01-30
### [Added]
- **B2BUA server/SIP Bridge:**
- Now supports bridging **incoming** (external) calls.
I.e. calls from clients registered on third-party domains/proxies to clients registered on the local domain/proxies.
This requires a corresponding external account for each user.
Please refer to [the SIP Bridge documentation] for details.
- [`one-connection-per-account`](https://wiki.linphone.org/xwiki/wiki/public/view/Flexisip/A.%20Configuration%20Reference%20Guide/2.4.0/b2bua-server#HB2buaserver):
Let the B2BUA use a separate connection (port) for each (external) account it manages. This can be used to work around DoS protection and rate-limiting systems on external proxies.
- **Proxy/Router:** new parameter `module::Router/static-targets` that lists sip addresses which will always be added to
the list of contacts fetched from the registrar database when routing INVITE and MESSAGE requests.
- **Proxy/NatHelper:** new strategy to route requests through NATs called
"flow-token" ([RFC5626](https://datatracker.ietf.org/doc/html/rfc5626))
- new parameter `module::NatHelper/nat-traversal-strategy` to indicate the strategy to use for routing requests
through NATs (`contact-correction` or `flow-token`).
- new parameter `module::NatHelper/force-flow-token` to force the use of flow-token under specific conditions (
boolean expression).
- **Configuration:** you can now indicate a unit (ms, s, min, h, d, m, y) along with the value for a duration
parameter in the configuration file.
See [File Syntax](https://wiki.linphone.org/xwiki/wiki/public/view/Flexisip/Configuration/#HFilesyntax) for more
information.
- **Registrar:** Keep-alive Redis requests for subscription connections.
Flexisip can now be more robust to middle-ware aggressively dropping idle connections.
- **Conference and B2BUA servers:** new
parameters `b2bua-server/audio-port`, `b2bua-server/video-port`, `conference-server/audio-port`
and `conference-server/video-port` to specify which port (or range of ports) to use for RTP and RTCP traffic.
- **Conference server:**
- new parameter `conference-server/call-timeout` to kill all incoming calls that last longer than
the defined value.
- compatibility with clients using Linphone SDK 5.4.
- **Build:**
- Support for Ubuntu 24.04.
- Support for Clang 18.
- Support for GCC 14.
- **Pusher:**
- Clearer message when Apple Push Notification Service (APNS) certificates are not read accessible.
- `--call-id` option to customise the content of the push notification payload
### [Fixed]
- **B2BUA, Conference, Linphone Daemon, Presence, Proxy, RegEvent:** All associated SystemD services now properly wait for the network to be online before starting.
(Fixes interface binding issues on boot.)
- **Proxy:**
- **NatHelper:** `module::NatHelper/contact-correction-param` is now properly removed from the `Contact` URI by the last hop in the proxy chain.
- **RegistrarDb:** Failing to subscribe to a Redis Pub/Sub channel is now properly handled and logged, to help troubleshoot ACL issues in Redis' config.
(Pub/Sub channels are used internally to trigger push notifications when devices register.)
- **ForkCallContext:** Ringing devices now receive the appropriate "Accepted Elsewhere" status via push notifications when another device accepts the call in a multi-proxy configuration.
- **Conference server:** Group chats no longer see their title overwritten to "ICE processing concluded" when the conference server's connection to Redis is slow or non-existant.
- **RPM:** no longer breaks the Flexisip Account Manager (FAM) if it had been installed first. The SELinux `var_log_t` label is now properly applied to Flexisip's log files only.
- **B2BUA server:** bridged calls put on hold (paused) using `a=inactive` can now be properly resumed.
- **Sofia SIP:** Incoming messages exceeding the maximum acceptable size are now answered with a 400, and can no longer cause a congestion blocking a socket.
- **CLI:** `REGISTRAR_DELETE` now properly deletes contacts identified by a `+sip.instance=` URI parameter.
### [Changed]
- **B2BUA:**
- The schema of the `b2bua-server::sip-bridge/providers` JSON configuration file has been overhauled to accommodate for the new incoming call bridging feature, and now offers many more configuration options.
Please refer to [the SIP Bridge documentation] for details.
- Custom header `flexisip-b2bua` is renamed to `X-Flexisip-B2BUA`.
(This header is no longer used by the proxy which instead relies on the `User-Agent` header. However the B2BUA server still adds it to its messages for backwards compatibility.)
- `b2bua-server/user-agent` can now include an optional `{version}` placeholder that will be replaced with the currently running Flexisip version.
- **Build:** refactor of the build system to meet new CMake standards.
- **Configuration:**
- Flexisip will now refuse to launch if duplicated keys are found in the configuration file.
(An explanatory message will be logged.)
- Configuration values (anything to the right of an `=` sign in the config file) can now be 10x larger (up to 20KiB = 20480 ASCII characters), allowing for e.g. long and complex filter expressions.
- **Proxy/PushNotification:** Invite/Cancel feature is now only used for Apple voip push notifications.
- **Proxy/NatHelper:** parameter `module::NatHelper/contact-verified-param` is
renamed `module::NatHelper/contact-correction-param`.
- **Proxy/MediaRelay:** In early media mode, the ringing device that answered last is now the one sending audio/video.
(Each new early media response takes over send capability.)
- **Internal:**
- Refactored software architecture (removed singletons) so you can now run several flexisip instances on
the same machine.
- Flexisip tester can now be used without the need for installation.
- **EventLogs:** Event IDs are now generated with the SHA 256 algorithm to ensure reproducibility. (In lieu of C++'s `std::hash<string>`.)
- **Logs:** Log messages from the Sofia SIP library are now only displayed if Flexisip is configured in `debug` logging level.
A new config option `global/sofia-level`, has been added to tweak which messages are shown in that case.
(This new option can be adjusted on a running instance via [the `CONFIG_SET` CLI command](https://wiki.linphone.org/xwiki/wiki/public/view/Flexisip/3.%20Operate/Examining%20run-time%20statistics/#HAccessstatisticsandconfiguration))
- **RPM:** The package now depends on `policycoreutils`, `policycoreutils-python-utils`, and `selinux-policy-targeted` to ensure its ability to set SELinux labels.
- **CLI:** Improved messages around `REGISTRAR_*` commands.
- **Pusher:** Added "Flexisip" to default push notification infos to make the origin of the PN clearer.
### [Deprecated]
- **Windows push notifications:** these push notifications are not handled anymore.
- parameter `module::PushNotification/windowsphone` no longer has any effect.
- parameter `module::PushNotification/windowsphone-package-sid` no longer has any effect.
- parameter `module::PushNotification/windowsphone-application-secret` no longer has any effect.
- **Proxy/registrar:** parameter `module::Registrar/redis-server-timeout` no longer has any effect. This parameter is not
really deprecated. It is not used for the moment but may be used in the future.
- **Plugin:** The JweAuth plugin will be removed in Flexisip 2.5.
### [Removed]
- **Ubuntu 18.04:** support discontinued as distribution has reached end-of-life (2023-05-31).
- **CentOS 7:** support discontinued as distribution has reached end-of-life (2024-06-30).
- **Debian 10:** support discontinued as distribution has reached end-of-life (2024-06-30).
- **Proxy/Registrar:** `module::Registrar/redis-record-serializer` (deprecated in 2.0.0)
- **Build:** `ENABLE_PROTOBUF` CMake option.
This option only enabled a Protobuf backend for the serialization of records in Redis. It could no longer be used since the deprecation of the `redis-record-serializer` config option (see above).
### [Known Issues]
- **Presence server:** Intermittent crash when updating the list of subscribers
- **Sofia-SIP:**
- Exponential memory usage when parsing data from a TCP socket.
(A 5MB socket buffer can lead to 26GB of parsing buffers.)
- Memory usage spike when the system clock jumps forward in time.
(As can happen when NTP connection is (re-)established, leading to OOM in the worst case where e.g. the system clock inits at epoch (1970-01-01) because of a dead battery.)
- **RPM:** Rocky 8 may refuse to install the package because it detects a conflict with the SystemD package.
- **Proxy:** No "Missing call" notification will be sent if a call is cancelled after being unanswered for more than 30 seconds.
[the SIP Bridge documentation]: https://wiki.linphone.org/xwiki/wiki/public/view/Flexisip/Configuration/Back-to-back%20User%20Agent%20(b2bua)/SIP%20Bridge
## [2.3.4] - 2024-07-24
### [Added]
- **Proxy/Registrar:** add 'max-contacts-per-registration' to reject REGISTER requests containing more Contact headers
than the specified limit.
- **Dependencies:** added `python3-google-auth` and `python3-requests` to package dependencies.
- **Build:** added CMake variable `FLEXISIP_VERSION` to enable build without GIT repository.
### [Changed]
- **Build:** disabled default configuration file generation when crosscompiling.
- **flexisip_cli.py:** API change, REGISTRAR_DELETE now returns an empty record instead of "Error 404" when deleting
the last contact of a Record, OR when attempting to delete a contact from a non-existent Record.
### [Fixed]
- **Proxy:** fix a crash when parsing an invalid "Contact" header value.
- **Proxy/NatHelper:** fix wrong "Contact" header correction in response when the proxy is first and last hop.
- **Configuration:** fix a crash when configuring an invalid boolean expression.
- **Proxy/PushNotification:** fix missing "to-tag" parameter on "110 Push Sent" when `module::PushNotification` is
enabled and filter parameter `module::PushNotification/add-to-tag-filter` evaluates to true.
- **Proxy/Router:** fix no call is routed to callee when `module::Router/fork-late`
and `module::Router/message-fork-late` are enabled.
- **Proxy/PushNotification:** fix several issues on the new Firebase V1 push notifications client.
- **Proxy/EventLogs:** fix wrong computation of event id. Previous method was sensitive to an inversion of "From"
and "To" header values.
- **B2BUA server:** fix behavior of the B2BUA. It was erroneously trying to resume a call that was paused with
a=inactive in SDP.
- **Proxy/Forward:** fix missing contact paths processing for mid-dialog requests intended to GRUU addresses. Fetched
paths from database were not translated into Route headers before forwarding the request.
- **Proxy/Router:** fix Proxy does not send terminal response in case of an early cancelled call
when `module::Router/fork-late` is on. This case could happen when a client had an offline device and/or did not have
the time to answer to the INVITE request before the CANCELED request (from the caller) arrived to the Proxy.
### [Deprecated]
- **Proxy/EventLog:** parameter `event-logs/flexiapi-token` is renamed `event-logs/flexiapi-api-key`. It still works but
deprecated, please use the new name.
## [2.3.3] - 2023-12-14
### [Added]
- **Presence:** last activity date is sent with long term presence.
- **B2BUA server:** you can now force the usage of a specific video codec. See the `video-codec` config for more
information.
- **B2BUA server:** add a configurable time limit on calls and fix a bug where calls where limited to 30 minutes.
See the `max-call-duration` config for more information.
- **B2BUA server:** allow to use other transport protocol than TCP.
- **B2BUA server:** you can now choose the User-Agent header for outgoing B2BUA request with the `user-agent` config.
- **Proxy/push-notification:** you can now choose between a HTTP/1 or a HTTP/2 client for the generic push-notification
service. See the `external-push-protocol` config for more information.
- **[Experimental]** **Proxy/push-notification:** you can choose to use the new Firebase v1 API to send Android push
notifications. See `firebase-service-accounts` config for more information. Use with caution, this feature is
experimental.
### [Changed]
- **Proxy/logs:** improve Redis request logging in case of errors.
- **Proxy/router:** add a configuration parameter to choose database connection pool size for message persistence.
See the `message-database-pool-size` config for more information.
- **Packaging:** Flexisip will create default configurations files on first install.
### [Fixed]
- **B2BUA server:** fix a bug where Trenscrypter mode placed outgoing calls using the request address instead of
the `To` header from the incoming call.
- **Packaging:** fix Systemctl warning and service restart on Rocky Linux 9 after package update.
- **Proxy:** fix a server hangup that occurred on TLS connection timeout.
- **Proxy/push-notification:** fix crashes around HTTP/2 client that occurred on iOS push notification sending.
- **Proxy/http/2 client:** fix a problem where frames where not sent directly after an HTTP/2 window size update.
- **Proxy/registrar:** fix a race condition on rapid consecutive REGISTERs that may lead to a crash.
- **Proxy/router:** fix bug where a 5xx response was preferred over a 6xx response for some INVITEs.
- **Proxy/forward:** fix a bug where stateless CANCEL could be forwarded without the Reason header.
## [2.3.2] - 2023-09-07
### [Fixed]
- **Proxy/media-relay:** fix `candidates` media attributes being wiped out of all INVITE responses. This buggy behaviour
was introduced in 2.3.1 while attempting to handle a response **with** ICE candidates to an INVITE **without** ICE
candidates.
- **Proxy/registrar:** fix a regression in a domain-registration scenario with "relay-reg-to-domains" enabled, where the
backend server fails to route to the intermediate proxy.
## [2.3.1] - 2023-08-30
### [Added]
- **B2BUA server:** add the 'no-rtp-timeout' parameter that allows to set the delay before the call is automatically
hung up because no RTP data is received.
### [Fixed]
- **Proxy/authentication:** fix behavior differences of 'soci-password-request' according to which Soci backend
is used. With SQlite backend the :authid placeholder was mandatory, which is not conform with parameter docstring,
whereas it was optional with MySQL backend. It is now optional whatever the backend in use.
- **Proxy/media-relay:** fix bad behavior when the MediaRelay forwards an INVITE without ICE candidate and the
callee send back a response with ICE candidates. In this case, the media relay didn't masquerade the connection
address of the response.
- **Proxy/push-notification:** add support of 'google' legacy pn-type.
- **Conference & B2BUA servers:** remove liblinphone debug messages from standard output when '-d' command-line
option isn't used.
## [2.3.0] - 2023-08-21
### [Added]
- **Flexisip proxy:** add `global/tport-message-queue-size` parameter to set the max number of SIP messages to be
queued for writing when a socket is full.
- **Flexisip proxy:** add support for REGISTER requests with several Contact headers.
- **Flexisip proxy:** reply to OPTIONS requests with “200 Ok”. Useful to keep a connection alive by using OPTIONS
requests.
- **Flexisip proxy:** add `module::Registrar/redis-auth-user` parameter to allow authentication to Redis servers via
user/password.
- **Conference server:** add audio/video conferencing capability.
- **B2BUA:** forwarding of [RFC2833](https://datatracker.ietf.org/doc/html/rfc2833) and SIP INFO DTMFs.
- **flexisip_cli.py:** add `REGISTRAR_UPSERT` command that allows to modify or insert any registrar binding for a given
Address of Record.
- **External authentication plugin:** add the SNI header in order to establish TLS connections with HTTPS virtual hosts.
- Packaging for Rocky Linux 9 and Debian 12.
- **[Experimental]** New EventLog backend based on an HTTP REST API.
### [Changed]
- **Flexisip proxy:** enforce compliance with [RFC3261](https://datatracker.ietf.org/doc/html/rfc3261) when processing
REGISTER requests. The Call-ID is no longer used as unique-id when no `+sip-instance` parameter has been set in the
Contact-URI; the Contact-URI is used instead by using URI comparison logic as described
in [RFC3261 Section 10.2.4](https://datatracker.ietf.org/doc/html/rfc3261#section-10.2.4). The CSeq value is now
used to avoid replay attacks or SIP race conditions.
## [2.2.5] - 2023-08-02
### [Added]
- **Presence server:** add timestamp of last activity to the presence notification when the status of the user is 'away'
or their client is no longer active.
### [Fixed]
- **Proxy:** fix system file descriptor limit detection bug that was eventually causing Flexisip to run out of file
descriptors to handle all of its connections on some OS.
- **Proxy ContactRouteInserter:** increase the max size of the 'CtRtxxxxx' parameter to 512 bytes to ensure that a
full domain name can be stored.
- **Proxy ExternalPushNotification:** fix bad behavior when an iOS client uses legacy push parameters while
registering and the 'app-id' parameter doesn't end with '.prod' or '.dev'. It caused the '$app-id' placeholder to be
replaced by a truncated 'app-id'. The fix makes Flexisip assume the 'app-id' ends with '.prod' if the user agent
hasn't specified the last component.
## [2.2.4] - 2023-04-20
### [Fixed]
- Bug in SofiaSip that causes the proxy to choose a not fully established TCP connection when it needs to send a SIP
message to a user agent. That causes some SIP message losses.
- Make the proxy to answer “200 Ok” to OPTIONS requests that are directly addressed to itself.
- Crash when the “Generic Push Notifications” feature is enabled (`module::PushNotification/external-push-uri`) but no
Firebase API key has been put in `firebase-projects-api-keys` parameter.
- Fix a bug that causes some PUBLISH requests that was not related to presence information to be forwarded to the
presence server.
## [2.2.3] - 2023-04-11
### [Fixed]
- CLI: print a more explicit message when the CLI cannot connect to the server socket due to permissions.
- Pusher: allow to set a custom payload for Firebase push notifications requests, as it is for Apple.
- Presence server: ensure that capabilities of each device of a user are concatenated by union while sending a NOTIFY
request to the subscriber.
- Proxy server: make the generic pusher to replace the $app-id parameter by the right value.
## [2.2.2] - 2023-02-24
### [Fixed]
- Issue in packaging and deployment scripts.
## [2.2.1] - 2023-02-24
### [Added]
- 'global/tport-message-queue-size' parameter in flexisip.conf. Allows to set the size of the message queue which is
used when a SIP message cannot be sent because the socket is full.
### [Changed]
- Format of `--key` option of `./flexisip_pusher` tool. The option only takes the Firebase authentication token now.
### [Fixed]
- Bug that caused the number of contacts for a given AoR to grow indefinitely when there was no '+sip.instance'
parameter in the Contact-URI.
- Push notification was not sent to the second device when two devices had the same 'pn-prid' but distinct '
pn-provider'.
- Messages were not forwarded with the same order as when they were received, should 'save-fork-late-message-in-db'
feature have been enabled.
- 6xx responses were not prioritized on 4xx responses when the proxy had to forward a final response to the caller.
- Compilation with `ENABLE_SOCI=OFF` was broken.
- Crash when the “Periodic Binding Refresh” mechanism (rfc8599) was
enabled (module::PushNotification/register-wakeup-interval>=0)
- The MediaRelay let the video stream pass in one direction only when the call was in early-media.
- Flexisip depended of useless runtime libraries such as libGLEW, libX11, etc.
- The ExternalAuthentication module didn't set the SNI header when it connected on the HTTPS server.
## [2.2.0] - 2022-10-28
### [Added]
- [Back-to-back user agent service](https://wiki.linphone.org/xwiki/wiki/public/view/Flexisip/C.%20Features/3.%20Back-to-back%20User%20Agent%20server)
- `module::Router/message-database-enabled` parameter: allow to store the chat messages that are waiting for delivery
in a SQL database instead of memory (experimental).
Associated parameters: `message-database-backend`, `message-database-connection-string`.
- [Filter syntax](https://wiki.linphone.org/xwiki/wiki/public/view/Flexisip/Configuration/Filter%20syntax/) getter
additions:
- `contact.uri.{user,domain,params}`: get parts of the Contact-URI of the current SIP message;
- `content-type`: get the full Content-type of the body as string.
- `module::ExternalAuth/trusted-hosts` parameter: allow to let requests coming from given IP addresses pass the
ExternalAuth module. This module is provided by 'libexternal-auth' plugin.
- Make the proxy to answer to double-CRLF ping sequence (RFC5626 §4.4.1).
- Use double-CRLF ping sequence (RFC5626 §4.4.1) to maintain connections made by the domain registration feature.
- Packaging for Rocky Linux 8, Debian 11, Ubuntu 22.04 LTS.
### [Changed]
- Improve the usage of iOS remote push notifications for notifying incoming calls. This is useful for home-automation
applications that haven't any VoIP push notification token. In such a case, Flexisip will send several alert PNs to
the application until one called device accept the call, and then, send a final alert PN to all the other devices
telling that the call has been answered elsewhere. Furthermore, all the called devices receive a final PN should
the caller cancel the call. Related parameters: `module::PushNotification/call-remote-push-interval`.
- The body of the SIP messages are hidden by default except the body of type `application/sdp`. This behaviour may be
customized by `global/show-body-for` parameter that allows to modify the condition that discriminates which request
should have their body displayed.
### [Fixed]
- Make the MediaRelay module to handle UPDATE requests.
- Issue where a 200 Ok for REGISTER coming from an upstream server is discarded instead of being routed back the
originator of the REGISTER. Only concerns user of `module::Registrar/reg-on-response` feature.
- Avoid push notification sending while forwarding INVITE requests with `Replace` header.
- Issue with domain registration digest authentication which failed because of the selection of a different SRV node
between first and authenticated request.
- Add a mechanism to ensure that all devices receive an INVITE followed by a CANCEL when a caller cancel a call
invitation. This is useful for iOS devices that are ringing before receiving the INVITE request because they
are notified by a VoIP push notification. Thus, such devices need to receive a CANCEL request to stop ringing.
### [Deprecated]
- `conference-server/enable-one-to-one-chat-room` parameter. Will force to `true` in further versions.
- Package for Debian 9.
## [2.1.6] - 2023-09-22
### [Fixed]
- Backport of several fixes concerning our HTTP/2 client code.
## [2.1.5] - 2022-06-09
### [Fixed]
- `reg-on-response` parameter no longer worked since Flexisip 2.1.0
## [2.1.4] - 2022-05-19
### [Fixed]
- Fix warning about failing SQL request on conference server starting.
- Make Flexisip to require Hiredis >= 0.14.
- Remove Sofia-SIP implementation of some functions that must be found on system.
## [2.1.3] - 2022-03-18
### [Fixed]
- ExternalPusher: the response to each HTTP request systematically has a delay of a few seconds when using a TCP
connection instead of TLS.
- Race condition around Redis SUBSCRIBEs/UNSUBSCRIBEs that causes Flexisip to wrongly thinks that it is subscribed to
some fork contexts. Finally, that causes to have end-users' device receiving push notifications for a message but no
message is delivered by Flexisip once the application registers again.
- Weakness in the module replacement algorithm that causes some modules coming
from plugins to be inserted in bad position in the modules list.
## [2.1.2] - 2021-12-22
### [Added]
- `rtp_bind_address` configuration parameter, which allow to choose the listening address of the media relay.
- Allow boolean expression filter to access the Contact header of the request.
### [Fixed]
- Have the CMake script to install flexisip-version.h header and embed it in the RPM/DEB package.
- Crash of the proxy when using `REGISTERAR_DELETE` command in flexisip_cli.
- Fix problems in migration of old protobuf-encoded Registrar entries.
## [2.1.1] - 2021-10-25
### [Fixed]
- Fix an issue in the CPack script that caused the name of CentOS packages to not conform with CentOS format, because
the distribution tag (el7, el8, etc.) was missing.
## [2.1.0] - 2021-10-20
### [Added]
- New Flexisip service, 'RegEvent server', available through flexisip-regevent SystemD service.
The RegEvent server is in charge of responding to SIP SUBSCRIBEs for the 'reg' event as defined by
[RFC3680 - A Session Initiation Protocol (SIP) Event Package for Registrations](https://tools.ietf.org/html/rfc3680).
To generate the outgoing NOTIFY, it relies upon the registrar database, as setup in module::Registrar section.
- **Proxy** New transport URI parameter: `tls-allow-missing-client-certificate=<true/false>`. This allows to accept TLS
connections from clients which haven't any X.509 certificate even if `tls-verify-incoming` has been enabled. Valid for
SIPS transport only.
- **Proxy** Add `module::DoSProtection/white-list` parameter in flexisip.conf to allow packets from given IP addresses
to bypass the DoS protection system.
- **Proxy** Add `module::Authentication/realm` parameter that allows to force the realm offered by the proxy to user
agents during authentication (401/407 responses).
- **Conference server** Several factory URIs can be handled by the server.
- **Push notifications** New option `--custom-payload` for flexisip_pusher utility that allows to manually set the
payload sent to push notificaiton server (Apple push only).
- **Flexisip CLI** Add `REGISTRAR_DUMP` CLI command to dump all addresses of record registered locally.
- **Packaging** Support of CentOS 8 and Debian 10 GNU/Linux distributions.
### [Changed]
- **Proxy** `regex` operator of filter expressions in flexisip.conf now
uses [ECMAScript grammar](https://en.cppreference.com/w/cpp/regex/ecmascript) from C++ specification.
- **Proxy** Firebase push notifications are now sent by using HTTP/2 protocol.
- **Presence server** Moving `soci-user-with-phone-request` and `soci-users-with-phones-request` parameters
from _[module::Authenticaiton]_ section to _[presence-server]_.
- **Conference server** Conformance to 1.1 specification.
- **Packaging** Packaging process has entirely been reworked in order to embed Flexisip and Linphone SDK inside a single
package. Thus, a given version of Flexisip is strongly bound to a specific version of Linphone SDK.
### [Deprecated]
- **Presence server** Setting `module::Authentication/soci-user-with-phone-request` and
`module::Authentication/soci-users-with-phones-request` parameters still works but will raise a warning.
### [Removed]
- **Proxy/Push notifications** `pn-silent` push parameter has no more effect.
- **Proxy/Push notifications** Remove legacy `form-uri` key-value from Firebase push notification body.
## [2.0.9] - 2021-08-10
### [Fixed]
- **Proxy** Reverts the previous fix which prevents that two contacts with the same push parameters be registered for
the same user. Side effects which caused some users to not receive messages or calls have been observed in production.
## [2.0.8] - 2021-08-09
### [Added]
- **Proxy** Adding 'fallback-route-filter' parameter in 'module::Router' section. This parameter allows to prevent some
SIP requests to be forwarded to the fallback route when all the forked transactions have failed. The parameter expects
a boolean expression as the filter parameter at the beggining of each module::\* sections. The fallback route is used
when the boolean expression is evaluated to _true_.
### [Fixed]
- **Proxy** Prevent SIP client to registers two distinct contacts (distinct UID) which would have the same push
notification parameters. That often happens when Linphone is uninstalled and installed again on an iOS device, causing
the instance UID to be generated again but keeping the same push notification tokens. That causes the device to
receives several push notifications for each SIP request because Flexisip assumes that each contact URI matches a
distinct device. To avoid this scenario, Flexisip automatically removes the old contact URI to ensure the unicity
of the push notification parameters.
## [2.0.7] - 2021-07-09
### [Fixed]
- **Proxy** Fix a bug that caused the fallback route to be used even if the forked request had succeeded.
## [2.0.6] - 2021-07-07
### [Fixed]
- **Proxy** INVITE requests was systematically forked to the fallback route (if set) independently of the status of each
received response. Furthermore, the fallback destination was called alongside the real contact addresses of the called
identity.
## [2.0.5] - 2021-06-09
### [Added]
- **Flexisip CLI** Add three new counters: count-basic-forks, count-call-forks and count-message-forks.
### [Fixed]
- **Apple push notifications** Set the 'apns-push-type' header.
- **Apple push notifications** Correctly set the 'apns-expiration' header, basing on some parameters of module::Router
(call-fork-timeout and message-delivery-timeout).
- **Apple push notifications** Prevent the TLS connection from blocking the main thread for more than one second while
connecting.
- **Android push notifications** Fix typo in the name of one key in the PNR payload. ('form-uri' -> 'from-uri'). The old
key will be supported until Flexisip 2.1.
- **External Authentication plugin** Correctly print the HTTP response from the authentication server in the log.
- **External Authentication plugin** Fix bug that caused the HTTP response to be matched with the bad request when
several request was sent simultaneously.
- **Filter parameter** Fix crash on evaluation when 'contains' operator has no left-hand operand. Makes Flexisip to
abort on starting otherwise.
- **Flexisip CLI** Fix crash with Python3 < 3.7.
- **Memory usage** Fix several memory leaks.
- **XWiki doc generator** Fix bad output syntax when bullet points are used in parameter descriptions.
- **XWiki doc generator** Generate documentation for the experimental modules.
## [2.0.4] - 2021-03-01
### [Fixed]
- **Authentication** Prevent password mismatch error when hashed passwords are in upper case in the user database.
- **Push Notifications** Prevent the PushNotification module from sending an out-of-dialog "180 Ringing" reply when an
in-dialog 180 reply has already been forwarded back by the Router module.
- **Apple push notifications** The new HTTP/2 client now automatically close the connection with the APNS after one
minute of inactivity to prevent the connection to be silently destroyed by aggressive routers. That improve PNR
sending reliability.
- **Android push notifications** Use timeouts that has been set in the Router module settings to fill the TTL with the
push notification request.
See [Flexisip's specification around push notifications](https://wiki.linphone.org/xwiki/wiki/public/view/Flexisip/D.%20Specifications/Push%20notifications/#HContentofthepushnotificationssentbyFlexisip)
for more information about the involved parameters.
- **Media relay** Fix an issue while processing a SDP without ICE containing an IPv6 connection address, and Flexisip
has no IPv6 address available.
Previously, an empty connection address was set by the MediaRelay module, causing a blank call. Now, the IPv4 address
will be used as fallback, which will work if the network provides NAT64 service.
- **Proxy server** Fix several huge memory leaks. No more memory leaks issues are known on the proxy component today.
- **Conference server** The transport address now allows to restrict the listening interface. Before, the conference was
listening on all interfaces independently of the transport host.
### [Removed]
- 'pn-silent' custom Contact parameter for push notifications.
## [2.0.3] - 2020-11-13
### [Fixed]
- Apple push notification client: the body of HTTP/2 GOAWAY frames wasn't printed in log, which doesn't allow to know
the disconnection reason.
- Fix a regression that causes to have an empty pub-gruu parameter in the Contact header of forwarded REGISTERs.
- Fix potential crash or at least memory corruption when both "route" and "default-transport" are set in the
ForwardModule. The default-transport will not be applied when route is used.
- MediaRelay: fix ICE restart not being detected or notified on the offered side. This causes relay candidates to be not
added in the 200 Ok, which can break RTP communication.
## [2.0.2] - 2020-10-14
### [Fixed]
- Fix a crash that occures when module::Registrar/reg-on-response feature is enabled. It happens when the “200
Registration successful” response is received from the backend server.
## [2.0.1] - 2020-10-13
### [Changed]
- Usage of HTTP2 protocol to send Apple push notification requests. No change in PushNotification module configuration
required.
### [Fixed]
- Crash when trying to fetch domain records from registrar DB.
- Avoid MediaRelay's channel to continuously swap between IPv6 and IPv4 during ICE connectivity checks. Indeed, this
causes some connectivity checks to fail because some stun requests sent over IPv6 are answered over IPv4 and vice
versa. The workaround implemented consists in locking the destination chosen by the MediaRelay's channels (when
receiving a packet) for a minimum of 5 seconds. The switch to a new destination is allowed only if the previous
destination has been unused over the last 5 seconds.
## [2.0.0] 2020-07-31
### [Added]
**New settings**
- `global/contextual-log-filter` ([Contextual log feature](https://wiki.linphone.org/xwiki/wiki/public/view/Flexisip/C.%20Features/Contextual%20logs/))
- `global/contextual-log-level` ([Contextual log feature](https://wiki.linphone.org/xwiki/wiki/public/view/Flexisip/C.%20Features/Contextual%20logs/))
- `global/log-filename`: allows to choose the name of the log file.
- `module::Authentication/realm-regex`: allows to choose how the authentication module deduces the realm from the "From"
header.
- `module::PushNotification/retransmission-count` (PNR retransmission feature)
- `module::PushNotification/retransmission-interval` (PNR retransmission feature)
- `module::PushNotification/display-from-uri`: controls whether the "From" URI is print in PN payloads.
- `module::MediaRelay/force-public-ip-for-sdp-masquerading`: force the MediaRelay module to put the public IP address of
the proxy while modifying the SDP body of INVITE requests. Only useful when the server is behind a NAT router.
- `conference-server/check-capabalities` (
see [Reference Documentation](https://wiki.linphone.org/xwiki/wiki/public/view/Flexisip/A.%20Configuration%20Reference%20Guide/2.0.0/conference-server))
**Proxy**
- [Contextual log feature](https://wiki.linphone.org/xwiki/wiki/public/view/Flexisip/C.%20Features/Contextual%20logs/)
- External authentication plugin.
- Push Notification Request (PNR) retransmission feature. Allow to send PNR several time when no response for the first
PNR has been received from the push server.
- Add support for loc-key and loc-args to Firebase, in order to be compatible with apps implementing the same logic as
for iOS when handling push notifications coming from Flexisip.
- EventLog: log the value of 'Priority' header for each request event.
- Support of [RFC 8599](https://tools.ietf.org/html/rfc8599) for the transmission of the PushNotification information
through REGISTER requests.
**Presence**
- Support
of [“Server known resource lists” feature](https://wiki.linphone.org/xwiki/wiki/public/view/Flexisip/C.%20Features/Presence%20server/#HServerknownresourcelists).
**Miscellaneous**
- Add an option (`--rewrite-config`) to Flexisip command-line interface to dump a new configuration file with up-to-date
doc strings but keeping the setting that have been set explicitly by the user.
### [Changed]
**Settings**
- Default value changes:
- `global/enable-snmp`: `true` -> `false`
- `gloabl/dump-cores`: `true` -> `false`
- `module::Router/message-delivery-timeout`: 1w
- `module::Router/message-accept-timeout`: 5s
- `module::Forward/params-to-remove`: adding `pn-provider`, `pn-prid`, `pn-param`
- `presence-server/max-thread`: `200` -> `50`
- `presence-server/max-thread-queue-size`: `200` -> `50`
- Parameter renaming:
- `event-logs/dir` -> `event-logs/filesystem-directory`
- `module::Registrar/datasource` -> `module::Registrar/file-path`
- `module::Registrar/name-message-expires` -> `module::Registrar/message-expires-param-name`
- `presence-server/soci-connection-string` -> `presence-server/rls-database-connection`
- `presence-server/external-list-subscription-request` -> `presence-server/rls-database-request`
- `presence-server/max-thread` -> `presence-server/rls-database-max-thread`
- `presence-server/max-thread-queue-size` -> `presence-server/rls-database-max-thread-queue-size`
- `[monitor]` section marked as experimental.
- `[module::Presence]` section is no more marked as experimental.
**Proxy**
- `REGISTRAR_CLEAR` sub-command of `flexisip_cli` can now clear registration of a given SIP identity.
- Improvement of the performance of the boolean expression engine used by module filters.
- Breaking of the event log database schema.
- [Push Notifications] The "From" URI is no more printed in the PN payload as first element of loc-args list.
Use `module::PushNotification/display-from-uri` setting to restore this behaviour.
**Miscellaneous**
- Log files are now named flexisip-proxy.log, flexisip-conference.log flexisip-presence.log by default.
- Log rotation is fully handled by Logrotate script (
see [“Logging” documentation page](https://wiki.linphone.org/xwiki/wiki/public/view/Flexisip/Configuration/Logs/#HLogrotation)).
- `--dump-all-default` option dumps a configuration file with all the parameters commented out.
- `--dump-default` allow to dump default settings for non-module sections.
- Generation of plugins default settings and documentation by '--dump-all-default' option when they have been loaded
using `--set global/plugins=<plugin-list>`.
### [Deprecated]
**New deprecated settings**
- `global/use-maddr`
- `global/max-log-size`
- `module::Registrar/redis-record-serializer`
- `module::Router/fork`
- `module::Router/stateful`
### [Removed]
**Removed settings**
- `global/debug`
- `module::Authentication/enable-test-accounts-creation`
- `module::Authentication/hashed-password`
- `module::Router/generated-contact-route`
- `module::Router/generated-contact-expected-realm`
- `module::Router/generate-contact-even-on-filled-aor`
- `module::Router/preroute`
- `module::PushNotification/google`
- `module::PushNotification/google-*`
### [Fixed]
**Proxy**
- Aborted calls not logged in the event log.
- Missing line-feed in filesystem event logs.
- Prevent loops due to fallback routes, when two Flexisip servers have a fallback route to each other.
- Abort server start if `module::Presence/presence-server` setting has an invalid SIP URI.
- Don't set tag in “110 Push sent” responses. It makes no sense as a proxy doesn't have to create dialogs.
- Prevent “110 Push sent” response from being sent after “180 Ringing”.
- Prevent sending of multiple “110 Push sent” responses when a call is forked into several legs.
- Prevent server abort on registration with an invalid Address-of-Record.
- Crash when processing a REGISTER with an invalid Contact URI.
- Bad behaviour when receiving a REGISTER request which contains a '@' in its CallID.
- Failing authentication when the user part of the "From" URI has escaped sequences.
- Improve Firebase's push notification resilience against broken sockets.
- Remove empty 'pub-gruu' params from contact headers of OK response when `module::Registrar/reg-on-response` is on.
- SystemD service not restarted on package update.
- Fix MediaRelay ICE processing when the server has both IPv6 and IPv6 addresses. Previously, only ICE relay candidates
with the "preferred" connectivity was offered. However, the way the "preferred" connectivity is guessed is not
reliable, especially when sending the INVITE to the callee, and it can change during a call, for example when one of
the parties moves from an IPv6-only LTE network to an IPv4-only network. For these reasons, it is preferable that ICE
relay candidates are added for both IPv4 and IPv6.
**Conference**
- Fix becoming admin again after leaving and reentering a chat room.

310
CMakeLists.txt Normal file
View file

@ -0,0 +1,310 @@
############################################################################
# CMakeLists.txt
# Copyright (C) 2010-2024 Belledonne Communications, Grenoble France
#
############################################################################
#
# 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, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
############################################################################
cmake_minimum_required(VERSION 3.22)
# CMP0076 is required to use relative path in target_sources.
cmake_policy(SET CMP0076 NEW)
# CMP0077 is required to correctly force the value of subprojects' cache variables.
cmake_policy(SET CMP0077 NEW)
# Require C++17
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
# -O0 disables optimizations. Any level of optimization (higher than 0) will throw off debuggers while stepping through source code.
# With sanitizers enabled, fortifying source requires some optimizations. This is unwanted in Debug builds.
set(CMAKE_C_FLAGS_DEBUG_INIT "-g -O0 -U_FORTIFY_SOURCE -fdiagnostics-color=always")
set(CMAKE_CXX_FLAGS_DEBUG_INIT ${CMAKE_C_FLAGS_DEBUG_INIT})
include("./linphone-sdk/bctoolbox/cmake/BCToolboxCMakeUtils.cmake")
if(FLEXISIP_VERSION)
message(WARNING "Ignoring git version, using provided \"${FLEXISIP_VERSION}\" instead")
set(FLEXISIP_FULL_VERSION ${FLEXISIP_VERSION})
else()
# Set project version by using the Git describe
bc_compute_full_version(FLEXISIP_FULL_VERSION)
endif()
bc_parse_full_version("${FLEXISIP_FULL_VERSION}" major minor patch)
project(flexisip VERSION "${major}.${minor}.${patch}" LANGUAGES C CXX)
unset(major)
unset(minor)
unset(patch)
include(CMakePushCheckState)
include(CMakeDependentOption)
include(CheckSymbolExists)
include(CheckFunctionExists)
include(FeatureSummary)
include(CheckCXXSourceCompiles)
include(GNUInstallDirs)
include("cmake/FlexisipUtils.cmake")
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
set(IS_DEBUG TRUE)
else()
set(IS_DEBUG FALSE)
endif()
option(ENABLE_SANITIZERS "Turn on sanitizers, like the LSAN memory leak detector" ${IS_DEBUG})
option(ENABLE_STRICT "Pass strict flags to the compiler" ON)
option(ENABLE_STRICT_LINPHONESDK "Pass strict flags to the compiler for all Linphone SDK submodules" OFF)
option(ENABLE_DATEHANDLER "Build DateHandler module" OFF)
option(ENABLE_PDFDOC "Build PDF documentation" OFF)
option(ENABLE_MONOTONIC_CLOCK_REGISTRATIONS "Enable monotonic clock for registrations" OFF)
option(ENABLE_PRESENCE "Build with presence server support" ON)
option(ENABLE_REDIS "Build with Redis support" ON)
option(ENABLE_SNMP "Build with SNMP support" ON)
option(ENABLE_SOCI "Build with SOCI support" ON)
option(ENABLE_TRANSCODER "Build with transcoder support" ON)
option(ENABLE_MDNS "Build with multicast DNS support" OFF)
option(ENABLE_EXTERNAL_AUTH_PLUGIN "Enable ExternalAuth plugin support" ON)
option(ENABLE_JWE_AUTH_PLUGIN "[Deprecated] Enable JweAuth plugin support" ON)
option(ENABLE_UNIT_TESTS "Enable Flexisip unit tests (low level tests)" OFF)
add_ccache_option(ON)
option(ENABLE_COVERAGE "Enable flexisip clang test coverage reports (add instrumentation)" OFF)
option(ENABLE_MSGPACK "[Deprecated] Build with support for MessagePack for Record serializing" OFF)
option(ENABLE_FLEXIAPI "Support for sending usage statistics (messages, calls, conferences) to the Flexisip Account Manager" ON)
option(INTERNAL_LIBSRTP2 "Build SRTP2 source code present as linphone-sdk submodule instead of searching it in system libraries" ON)
option(INTERNAL_JSONCPP "Build and use vendored Jsoncpp source code instead of searching for it in system libraries" OFF)
cmake_dependent_option(INTERNAL_LIBHIREDIS "Build libhiredis source code present as Flexisip submodule instead of searching it in system libraries" OFF "ENABLE_REDIS" OFF)
cmake_dependent_option(ENABLE_CONFERENCE "Build conference support" ON "ENABLE_SOCI" OFF)
cmake_dependent_option(ENABLE_SOCI_POSTGRESQL_BACKEND "Build with SOCI Postgre sql backend support" ON "ENABLE_SOCI" OFF)
cmake_dependent_option(ENABLE_B2BUA "Enable Back2back user agent support" ON "ENABLE_SOCI" OFF)
cmake_dependent_option(ENABLE_UNIT_TESTS_NGHTTP2ASIO "Enable unit tests requiring libnghttp2_asio" ON "ENABLE_UNIT_TESTS" ON)
cmake_dependent_option(ENABLE_SPECIFIC_FEATURES "Enable media relay specific features" OFF "ENABLE_TRANSCODER" OFF)
set(CPACK_GENERATOR "" CACHE STRING "Generator to use for making package. Supported values: 'RPM', 'DEB'")
set(SYSCONF_INSTALL_DIR "" CACHE STRING
"Configuration directory, the place where Flexisip expects its flexisip.conf file to reside. Always equal to '${CMAKE_INSTALL_FULL_SYSCONFDIR}' if empty."
)
set(FLEXISIP_SYSTEMD_INSTALL_DIR "" CACHE STRING
"Where to install the SystemD units. Always equal to '${CMAKE_INSTALL_FULL_DATAROOTDIR}/systemd/system' if empty."
)
if(ENABLE_CONFERENCE OR ENABLE_B2BUA)
set(LIBLINPHONE_REQUIRED ON)
set(HAVE_LIBLINPHONE YES)
set(HAVE_LIBLINPHONECXX YES)
elseif(ENABLE_UNIT_TESTS)
set(LIBLINPHONE_REQUIRED ON)
else()
set(LIBLINPHONE_REQUIRED OFF)
endif()
if(ENABLE_SOCI OR LIBLINPHONE_REQUIRED)
set(SOCI_REQUIRED ON)
else()
set(SOCI_REQUIRED OFF)
endif()
# It seems -fsanitize=address and -Wuninitialized don't play well together
if(CMAKE_BUILD_TYPE STREQUAL "Sanitizer"
AND CMAKE_CXX_COMPILER_ID STREQUAL "GNU"
)
# https://gcc.gnu.org/bugzilla/show_bug.cgi?id=105616
if(CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 13 AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS_EQUAL 13.2.1)
set(HAS_GCC_BUG_105616 ON)
# https://gcc.gnu.org/bugzilla/show_bug.cgi?id=105562
elseif(CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 12 AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS_EQUAL 12.3.0)
set(HAS_GCC_BUG_105562 ON)
endif()
endif()
# Place the built libraries and executables in top level directories 'lib' and 'bin' in the build tree.
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib" CACHE PATH "Archive output dir.")
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib" CACHE PATH "Library output dir.")
set(CMAKE_PDB_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin" CACHE PATH "PDB (MSVC debug symbol)output dir.")
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin" CACHE PATH "Executable/dll output dir.")
# Advanced options (i.e. hidden to the user by default)
option(ENABLE_LIBLINPHONE_TESTER "Build liblinphone_tester executable." OFF)
mark_as_advanced(ENABLE_LIBLINPHONE_TESTER)
# Handle the default value of installation paths. That ensures that they are
# always relative to the install prefix when the user hasn't set them explicitly.
if(SYSCONF_INSTALL_DIR STREQUAL "")
set(SYSCONF_INSTALL_DIR "${CMAKE_INSTALL_FULL_SYSCONFDIR}")
endif()
if(FLEXISIP_SYSTEMD_INSTALL_DIR STREQUAL "")
set(FLEXISIP_SYSTEMD_INSTALL_DIR "${CMAKE_INSTALL_FULL_DATAROOTDIR}/systemd/system")
endif()
# Build libflexisip and all its dependencies as shared libraries
set(BUILD_SHARED_LIBS ON)
if(NOT CMAKE_INSTALL_RPATH AND CMAKE_INSTALL_PREFIX)
set(CMAKE_INSTALL_RPATH ${CMAKE_INSTALL_FULL_LIBDIR})
message(STATUS "Setting install rpath to ${CMAKE_INSTALL_RPATH}")
endif()
set(CONFIG_DIR "${SYSCONF_INSTALL_DIR}/flexisip")
message(STATUS "Config dir: ${CONFIG_DIR}")
set(INSTALL_PREFIX ${CMAKE_INSTALL_PREFIX})
function(FIND_PROGRAM_REQUIRED varname progname)
find_program(${varname} NAMES "${progname}")
if(NOT ${varname})
message(FATAL_ERROR "Program '${progname}' is required but could not be found")
endif()
endfunction()
list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake)
set(INTERNAL_MBEDTLS ON)
include("cmake/ExternalDependencies.cmake")
include("cmake/LinphoneSDK.cmake")
# Required packages
find_package(LibNgHttp2 REQUIRED)
find_package(Threads)
find_package(XercesC)
# Dummy executable used by the type-safe abstractions over POSIX processes
find_program(DUMMY_EXEC NAMES true REQUIRED)
check_function_exists(arc4random HAVE_ARC4RANDOM)
find_file(HAVE_SYS_PRCTL_H NAMES sys/prctl.h)
set(CMAKE_REQUIRED_LIBRARIES)
# Enable std::filesystem on old implementations (GNU <9.1, LLVM <9.0)
link_libraries("stdc++fs")
# Options
if(ENABLE_SNMP)
# todo: Not quite ready
FIND_PROGRAM_REQUIRED(NET_SNMP_PROG net-snmp-config)
find_path(NET_SNMP_INCLUDE_DIRS NAMES net-snmp/net-snmp-config.h)
if(NOT NET_SNMP_INCLUDE_DIRS)
message(FATAL_ERROR "SNMP header files not found")
endif()
execute_process(COMMAND "${NET_SNMP_PROG}" "--agent-libs" OUTPUT_VARIABLE NET_SNMP_LIBRARIES OUTPUT_STRIP_TRAILING_WHITESPACE)
endif()
if(ENABLE_SPECIFIC_FEATURES)
set(MEDIARELAY_SPECIFIC_FEATURES_ENABLED ON)
endif()
if(ENABLE_MONOTONIC_CLOCK_REGISTRATIONS)
set(MONOTONIC_CLOCK_REGISTRATIONS ON)
endif()
if(ENABLE_DATEHANDLER)
set(HAVE_DATEHANDLER ON)
endif()
if(ENABLE_REDIS AND NOT INTERNAL_LIBHIREDIS)
find_package(Hiredis 0.14 REQUIRED)
endif()
if(ENABLE_PDFDOC)
FIND_PROGRAM_REQUIRED(PDFLATEX_PROG pdflatex)
endif()
if(ENABLE_MSGPACK)
find_path(MSGPACK_INCLUDE_DIRS NAMES msgpack.hpp HINTS /usr/local/include REQUIRED)
add_definitions("-DENABLE_MSGPACK")
endif()
# Allow to use SLOGD and LOGD macros.
add_definitions("-DBCTBX_DEBUG_MODE")
find_package(OpenSSL 0.9.8 REQUIRED)
feature_summary(WHAT ALL INCLUDE_QUIET_PACKAGES FATAL_ON_MISSING_REQUIRED_PACKAGES)
include_directories(
"include"
"src"
"src/plugin"
"src/presence"
"${CMAKE_CURRENT_BINARY_DIR}"
"${CMAKE_CURRENT_BINARY_DIR}/include"
"${CMAKE_CURRENT_BINARY_DIR}/src"
)
set(BELR_GRAMMARS_DIR "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_DATADIR}/belr/grammars")
configure_file(cmake/flexisip-config.h.in flexisip-config.h)
set_source_files_properties(${PROJECT_BINARY_DIR}/flexisip-config.h PROPERTIES GENERATED ON)
add_compile_definitions("HAVE_CONFIG_H")
# Compute and set compilation options
bc_init_compilation_flags(CPP_BUILD_FLAGS C_BUILD_FLAGS CXX_BUILD_FLAGS ENABLE_STRICT)
if(ENABLE_SANITIZERS)
set(SANITIZERS_FLAG "-fsanitize=address,undefined")
add_compile_options(
${SANITIZERS_FLAG}
"-fno-omit-frame-pointer"
"-fno-optimize-sibling-calls"
)
add_link_options(${SANITIZERS_FLAG})
endif()
if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
if(ENABLE_STRICT)
list(APPEND CXX_BUILD_FLAGS
"-Werror=unused-result" # Packaging (on Ubuntu) has this on, so better catch it sooner
"-Werror=maybe-uninitialized" # GCC on CentOS 7 treats this as an error
)
endif()
# -Werror=varargs seems to do false positives with GCC 4.9.x
if(CMAKE_CXX_COMPILER_VERSION MATCHES "^4\\.9\\.[0-9]+$")
list(APPEND CXX_BUILD_FLAGS "-Wno-error=varargs")
endif()
if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS 7)
# Old compiler (gcc6 on Debian 9 Stretch) are giving us some toubles...
list(APPEND CXX_BUILD_FLAGS "-Wno-error=unused-variable" "-Wno-error=attributes")
else()
# GCC on CentOS 7 treats this as an error
list(APPEND CXX_BUILD_FLAGS "-Werror=format-truncation=1")
endif()
endif()
if(CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
# Old compiler (clang3 on Debian 9 Stretch) are giving us some toubles...
if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS 4)
list(APPEND CXX_BUILD_FLAGS "-Wno-error=unused-variable" "-Wno-error=unknown-attributes")
endif()
endif()
add_compile_options(${CPP_BUILD_FLAGS} ${CXX_BUILD_FLAGS})
add_subdirectory(include)
add_subdirectory(src)
add_subdirectory(scripts)
add_subdirectory(share)
if(ENABLE_UNIT_TESTS)
add_subdirectory(tester)
endif()
# Packaging
add_subdirectory(packaging)

661
COPYING Normal file
View file

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

179
README.md Normal file
View file

@ -0,0 +1,179 @@
# Flexisip
Flexisip is a comprehensive, modular and scalable SIP server suite written in C++17. It offers a wide range of
functionalities, including:
* **Proxy Server**: acts as a central hub for routing SIP messages.
* **Push Notification Service**: delivers SIP notifications (in-calls, messages) to mobile devices even when the app
is not actively running.
* **Presence Server**: enables users to see the online status of others and their availability for calls.
* **Conference Server**: enables group voice and video calls.
* **Back-to-Back User Agent (B2BUA) Server**: enables caller identity translation, media-level transcoding and SIP
trunking.
* **RegEvent Server**: notify tier domains of user registration
## Deployment and Applications:
* **Server-based VoIP Service**: Flexisip can be deployed on server machines to run a full-fledged SIP VoIP service.
This is exemplified by the free linphone.org service, which has been powered by Flexisip since 2011. Users can create
SIP accounts on this service to connect with each other.
* **Embedded Solutions**: Flexisip can also be embedded and run seamlessly on smaller hardware systems, making it
suitable for various embedded applications.
# License
Copyright © Belledonne Communications
Flexisip is dual licensed, and can be licensed and distributed:
- under a GNU Affero GPLv3 license for free (see COPYING file for details)
- under a proprietary license, for closed source projects. Contact Belledonne Communications for any question about
[costs and services](https://www.linphone.org/en/flexisip-sip-server/#flexisip-license).
# Documentation
- [Supported features and RFCs](https://www.linphone.org/en/flexisip-sip-server/#flexisip-software)
- [Flexisip documentation](https://www.linphone.org/en/flexisip-sip-server/#flexisip-documentation)
# Dependencies
| Dependency | Description | Mandatory | Enabled by default |
|:----------------|:--------------------------------------------------------------------------------------------------------------------------------------|:---------:|:------------------:|
| OpenSSL | TLS stack. | X | |
| LibNgHttp2 | HTTP2 stack. | X | |
| libsrtp2 | Secure RTP (SRTP) and UST Reference Implementations | X | |
| SQLite3 | Library for handling SQlite3 file | X | |
| libmysql-client | Client library for MySQL database. | X | |
| Hiredis | Redis DB client library, used for Registrar DB and communications between Flexisip instances of a same cluster. (-DENABLE\_REDIS=YES) | | X |
| NetSNMP | SNMP library, used for SNMP support. (-DENABME\_SNMP=YES) | | X |
| XercesC | XML parser. (-DENABLE\_PRESENCE=YES) | | X |
| jsoncpp | JSON parsing and writing (-DENABLE\_B2BUA=YES) | | X |
# Compilation
## Required build tools
- C and C++ compiler. GCC and Clang are supported *as long as they are recent enough for building C++17 code*. On
Redhat/CentOS 7, we recommend installing gcc-7 from https://www.softwarecollections.org/en/scls/rhscl/devtoolset-7/ .
The default gcc-4.8 is not sufficient.
- CMake >= 3.13
- make or Ninja
- Python >= 3
- Doxygen
- Git
## Building Flexisip with CMake
Create a build directory and configure the project:
### From cloned GIT repository
```bash
mkdir ./build
cmake -S . -B ./build
make -C ./build -j<njobs>
```
### Custom
When built outside a git repository, you have to manually mention Flexisip and Linphone-SDK versions.
```bash
mkdir ./build
cmake -S . -B ./build -DFLEXISIP_VERSION=<version> -DLINPHONESDK_VERSION=<version>
make -C ./build -j<njobs>
```
### Some tips
Check *CMakeLists.txt* to know the list of the available options and their default value. To change an option, invoke
*CMake* again and specify the option you need to change.
For instance, here is how to disable the presence server feature:
```bash
cmake ./build -DENABLE_PRESENCE=OFF
make -C ./build -j<njobs>
```
You may also use *ccmake* or *cmake-gui* utilities to interactively configure the project:
```bash
ccmake ./build
make -C ./build -j<njobs>
```
## Building RPM or DEB packages
This procedure will help you generate a unique RPM package containing Flexisip, all its dependencies and the
corresponding package for debug symbols.
The following options are relevant for packaging:
| Option | Description |
|:-----------------------|:---------------------------------------------------------------------------|
| `CMAKE_INSTALL_PREFIX` | Prefix path where the package will install the files. |
| `SYSCONF_INSTALL_DIR` | Directory where Flexisip expects to find its default configuration. |
| `CMAKE_BUILD_TYPE` | Set it to “RelWithDebInfo” to have debug symbols in the debuginfo package. |
| `CPACK_GENERATOR` | Package type: “RPM” or “DEB”. |
```bash
cmake ./build -DCMAKE_INSTALL_PREFIX=/opt/belledonne-communications -DCMAKE_BUILD_TYPE=RelWithDebInfo -DSYSCONF_INSTALL_DIR=/etc -DCPACK_GENERATOR=RPM
make -C ./build -j<njobs> package
```
Packages are now available in the `./build` directory.
[More info on RPM packaging](./packaging/rpm/README.md)
## Docker
A docker image can be built from sources using the following command:
```bash
docker build -t flexisip --build-arg='njobs=<njobs>' -f docker/flex-from-src .
```
## Nix ❄️
Flexisip can also be compiled with [Nix]. From the root of the repository, you can obtain a development shell using:
```sh
nix-shell
```
Or with Flakes enabled:
```sh
nix develop
```
Nix makes it easier to have a reproducible development environment on any Linux distribution, and doesn't interfere with
other installed tooling. It is just an additional, **optional** way to build flexisip.
### Example build commands:
```sh
CC=gcc CXX=g++ BUILD_DIR_NAME="build" cmake -DCMAKE_EXPORT_COMPILE_COMMANDS=1 -S . -B ./$BUILD_DIR_NAME -G "Ninja" -DCMAKE_BUILD_TYPE=Debug -DCMAKE_INSTALL_PREFIX="$PWD/$BUILD_DIR_NAME/install" -DENABLE_UNIT_TESTS=ON -DENABLE_STRICT_LINPHONESDK=OFF -DINTERNAL_JSONCPP=OFF
cd build
clear && cmake --build . --target install && LSAN_OPTIONS="suppressions=../sanitizer_ignore.txt" bin/flexisip_tester --resource-dir "../tester/" --verbose
```
### Note to maintainers
At the exception to [`shell.nix`](./shell.nix), `.nix` files should live inside the [`nix/`](./nix/) folder.
All `.nix` files should be formatted with `nixpkgs-fmt`.
[Nix]: https://nixos.org/
# Configuration
Flexisip needs a configuration file to run correctly.
Use `./flexisip --dump-all-default > flexisip.conf` to generate a documented default configuration file.
# Developer notes
With sofia-sip, you can choose between `msg_dup()` and `msg_copy()`, `sip_from_dup()` and `sip_from_copy()`, _etc_.
The difference isn't well documented in the sofia-sip documentation, but it is important to understand that:
- `*_dup()` makes a copy of the structure plus all included strings inside (deep copy).
- `*_copy()` just makes a copy of the structure, not the strings pointed by it (shallow copy). **These functions are
dangerous**; use `*_dup()` versions in doubt.

View file

@ -0,0 +1,51 @@
############################################################################
# ExternalDependencies.cmake
# Copyright (C) 2010-2023 Belledonne Communications, Grenoble France
#
############################################################################
#
# 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, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
############################################################################
############################################################################
# Add some external dependencies as subproject
############################################################################
# Configure and add SofiaSip
function(add_sofiasip) # Use function of override variable without propagating the change afterwards
set(ENABLE_UNIT_TESTS OFF)
add_subdirectory("submodules/externals/sofia-sip")
endfunction()
add_sofiasip()
# Add libhiredis
if(ENABLE_REDIS AND INTERNAL_LIBHIREDIS)
function(add_hiredis)
set(CMAKE_POLICY_DEFAULT_CMP0077 NEW) # Prevent project from overriding the options set at this level
add_subdirectory("submodules/externals/hiredis")
target_compile_definitions(hiredis INTERFACE "INTERNAL_LIBHIREDIS")
endfunction()
add_hiredis()
endif()
# Configure and add Jose
if(ENABLE_JWE_AUTH_PLUGIN)
function(add_jose)
add_subdirectory("submodules/externals/jose")
endfunction()
add_jose()
endif()

85
cmake/FindHiredis.cmake Normal file
View file

@ -0,0 +1,85 @@
############################################################################
# FindHiredis.cmake
# Copyright (C) 2017 Belledonne Communications, Grenoble France
#
############################################################################
#
# 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, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
############################################################################
#
# - Find the libhiredis library
#
# HIREDIS_FOUND - system has libhiredis
# HIREDIS_INCLUDE_DIRS - the libhiredis include directory
# HIREDIS_LIBRARIES - The libraries needed to use libhiredis
# HIREDIS_ASYNC_ENABLED - The found libhiredis library supports async commands
# Find out the include directory
find_path(HIREDIS_INCLUDE_DIRS
NAMES hiredis/hiredis.h
PATH_SUFFIXES include
)
# Find out the path to the library
find_library(HIREDIS_LIBRARIES
NAMES hiredis
)
if(HIREDIS_INCLUDE_DIRS AND HIREDIS_LIBRARIES)
# Extract the version number from the header files
function(extract_version version_header range output_var)
string(TOUPPER ${range} range)
set(define_line_regex "^#define HIREDIS_${range} ([0-9]+)$")
file(STRINGS ${version_header} define_line REGEX "${define_line_regex}")
if(NOT define_line)
message(FATAL_ERROR "HIREDIS_${range} isn't defined in '${version_header}'")
endif()
string(REGEX REPLACE ${define_line_regex} "\\1" version_range_value "${define_line}")
set(${output_var} ${version_range_value} PARENT_SCOPE)
endfunction()
set(version_header "${HIREDIS_INCLUDE_DIRS}/hiredis/hiredis.h")
extract_version(${version_header} major HIREDIS_VERSION_MAJOR)
extract_version(${version_header} minor HIREDIS_VERSION_MINOR)
extract_version(${version_header} patch HIREDIS_VERSION_PATCH)
set(HIREDIS_VERSION "${HIREDIS_VERSION_MAJOR}.${HIREDIS_VERSION_MINOR}.${HIREDIS_VERSION_PATCH}")
# Check that the async mode is supported
cmake_push_check_state(RESET)
list(APPEND CMAKE_REQUIRED_INCLUDES ${HIREDIS_INCLUDE_DIRS})
list(APPEND CMAKE_REQUIRED_LIBRARIES ${HIREDIS_LIBRARIES})
check_symbol_exists("redisAsyncCommand" "hiredis/async.h" HIREDIS_ASYNC_ENABLED)
cmake_pop_check_state()
if(NOT HIREDIS_ASYNC_ENABLED)
message(FATAL_ERROR "redisAsyncCommand() not found")
endif()
endif()
# Define HIREDIS_FOUND output variable and define the 'hiredis' imported target
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(Hiredis
REQUIRED_VARS HIREDIS_INCLUDE_DIRS HIREDIS_LIBRARIES
VERSION_VAR HIREDIS_VERSION
)
if (HIREDIS_FOUND)
add_library(hiredis SHARED IMPORTED)
set_target_properties(hiredis PROPERTIES
INTERFACE_INCLUDE_DIRECTORIES "${HIREDIS_INCLUDE_DIR}"
IMPORTED_LOCATION "${HIREDIS_LIBRARIES}"
)
endif()
mark_as_advanced(HIREDIS_INCLUDE_DIRS HIREDIS_LIBRARIES HIREDIS_ASYNC_ENABLED)

44
cmake/FindJansson.cmake Normal file
View file

@ -0,0 +1,44 @@
############################################################################
# FindJansson.cmake
# Copyright (C) 2018 Belledonne Communications, Grenoble France
#
############################################################################
#
# 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, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
############################################################################
#
# - Find Jansson include files and library
#
# JANSSON_FOUND - System has jansson
# JANSSON_INCLUDE_DIRS - The jansson include directories
# JANSSON_LIBRARIES - The libraries needed to use jansson
find_package(PkgConfig QUIET)
pkg_check_modules(PC_JANSSON QUIET jansson)
find_path(JANSSON_INCLUDE_DIRS
NAMES jansson.h
HINTS ${PC_JANSSON_INCLUDE_DIRS}
)
find_library(JANSSON_LIBRARIES
NAMES jansson
HINTS ${PC_JANSSON_LIBRARY_DIRS}
)
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(Jansson REQUIRED_VARS JANSSON_INCLUDE_DIRS JANSSON_LIBRARIES)
mark_as_advanced(JANSSON_INCLUDE_DIRS JANSSON_LIBRARIES)

44
cmake/FindJose.cmake Normal file
View file

@ -0,0 +1,44 @@
############################################################################
# FindJose.cmake
# Copyright (C) 2018 Belledonne Communications, Grenoble France
#
############################################################################
#
# 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, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
############################################################################
#
# - Find Jose include files and library
#
# JOSE_FOUND - System has jose
# JOSE_INCLUDE_DIRS - The jose include directories
# JOSE_LIBRARIES - The libraries needed to use jose
find_package(PkgConfig QUIET)
pkg_check_modules(PC_JOSE QUIET jose)
find_path(JOSE_INCLUDE_DIRS
NAMES jose/jose.h
HINTS ${PC_JOSE_INCLUDE_DIRS}
)
find_library(JOSE_LIBRARIES
NAMES jose
HINTS ${PC_JOSE_LIBRARY_DIRS}
)
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(Jose REQUIRED_VARS JOSE_INCLUDE_DIRS JOSE_LIBRARIES)
mark_as_advanced(JOSE_INCLUDE_DIRS JOSE_LIBRARIES)

View file

@ -0,0 +1,44 @@
############################################################################
# FindNgHttp2.cmake
# Copyright (C) 2010-2020 Belledonne Communications, Grenoble France
#
############################################################################
#
# 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, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
############################################################################
#
# Find libnghttp2 and defined the associated target.
#
# Target name: LibNgHttp2
include(FindPackageHandleStandardArgs)
find_path(LIBNGHTTP2_INCLUDE_DIR nghttp2/nghttp2.h)
find_library(LIBNGHTTP2_LIBRARY nghttp2)
find_package_handle_standard_args(LibNgHttp2 REQUIRED_VARS LIBNGHTTP2_INCLUDE_DIR LIBNGHTTP2_LIBRARY)
if (LIBNGHTTP2_FOUND)
add_library(LibNgHttp2 SHARED IMPORTED)
set_target_properties(LibNgHttp2 PROPERTIES
INTERFACE_INCLUDE_DIRECTORIES "${LIBNGHTTP2_INCLUDE_DIR}"
IMPORTED_LOCATION "${LIBNGHTTP2_LIBRARY}"
)
endif()
unset(NGHTTP2_INCLUDE_DIR)
unset(NGHTTP2_LIBRARY)

View file

@ -0,0 +1,44 @@
############################################################################
# FindLibNgHttp2Asio.cmake
# Copyright (C) 2010-2021 Belledonne Communications, Grenoble France
#
############################################################################
#
# 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, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
############################################################################
#
# Find libnghttp2_asio and defined the associated target.
#
# Target name: LibNgHttp2Asio
include(FindPackageHandleStandardArgs)
find_path(LIBNGHTTP2ASIO_INCLUDE_DIR nghttp2/asio_http2.h)
find_library(LIBNGHTTP2ASIO_LIBRARY nghttp2_asio)
find_package_handle_standard_args(LibNgHttp2Asio REQUIRED_VARS LIBNGHTTP2ASIO_INCLUDE_DIR LIBNGHTTP2_LIBRARY)
if (LIBNGHTTP2ASIO_FOUND)
add_library(LibNgHttp2Asio SHARED IMPORTED)
set_target_properties(LibNgHttp2Asio PROPERTIES
INTERFACE_INCLUDE_DIRECTORIES "${LIBNGHTTP2ASIO_INCLUDE_DIR}"
IMPORTED_LOCATION "${LIBNGHTTP2ASIO_LIBRARY}"
)
target_link_libraries(LibNgHttp2Asio INTERFACE LibNgHttp2)
endif()
unset(NGHTTP2_ASIO_INCLUDE_DIR)
unset(NGHTTP2_ASIO_LIBRARY)

111
cmake/FindODB.cmake Normal file
View file

@ -0,0 +1,111 @@
#
# This module defines the following variables:
#
# ODB_USE_FILE - Path to the UseODB.cmake file. Use it to include the ODB use file.
# The use file defines the needed functionality to compile and use
# odb generated headers.
#
# ODB_FOUND - All required components and the core library were found
# ODB_INCLUDR_DIRS - Combined list of all components include dirs
# ODB_LIBRARIES - Combined list of all componenets libraries
#
# ODB_LIBODB_FOUND - Libodb core library was found
# ODB_LIBODB_INCLUDE_DIRS - Include dirs for libodb core library
# ODB_LIBODB_LIBRARIES - Libraries for libodb core library
#
# For each requested component the following variables are defined:
#
# ODB_<component>_FOUND - The component was found
# ODB_<component>_INCLUDE_DIRS - The components include dirs
# ODB_<component>_LIBRARIES - The components libraries
#
# <component> is the original or uppercase name of the component
#
# The component names relate directly to the odb module names.
# So for the libodb-mysql.so library, the component is named mysql,
# for the libodb-qt.so module it's qt, and so on.
#
set(ODB_USE_FILE "${CMAKE_CURRENT_LIST_DIR}/UseODB.cmake")
find_package(PkgConfig QUIET)
function(find_odb_api component)
string(TOUPPER "${component}" component_u)
set(ODB_${component_u}_FOUND FALSE PARENT_SCOPE)
pkg_check_modules(PC_ODB_${component} QUIET "libodb-${component}")
find_path(ODB_${component}_INCLUDE_DIR
NAMES odb/${component}/version.hxx
HINTS
${ODB_LIBODB_INCLUDE_DIRS}
${PC_ODB_${component}_INCLUDE_DIRS})
find_library(ODB_${component}_LIBRARY
NAMES odb-${component} libodb-${component}
HINTS
${ODB_LIBRARY_PATH}
${PC_ODB_${component}_LIBRARY_DIRS})
set(ODB_${component_u}_INCLUDE_DIRS ${ODB_${component}_INCLUDE_DIR} CACHE STRING "ODB ${component} include dirs")
set(ODB_${component_u}_LIBRARIES ${ODB_${component}_LIBRARY} CACHE STRING "ODB ${component} libraries")
mark_as_advanced(ODB_${component}_INCLUDE_DIR ODB_${component}_LIBRARY)
if(ODB_${component_u}_INCLUDE_DIRS AND ODB_${component_u}_LIBRARIES)
set(ODB_${component_u}_FOUND TRUE PARENT_SCOPE)
set(ODB_${component}_FOUND TRUE PARENT_SCOPE)
list(APPEND ODB_INCLUDE_DIRS ${ODB_${component_u}_INCLUDE_DIRS})
list(REMOVE_DUPLICATES ODB_INCLUDE_DIRS)
set(ODB_INCLUDE_DIRS ${ODB_INCLUDE_DIRS} PARENT_SCOPE)
list(APPEND ODB_LIBRARIES ${ODB_${component_u}_LIBRARIES})
list(REMOVE_DUPLICATES ODB_LIBRARIES)
set(ODB_LIBRARIES ${ODB_LIBRARIES} PARENT_SCOPE)
endif()
endfunction()
pkg_check_modules(PC_LIBODB QUIET "libodb")
set(ODB_LIBRARY_PATH "" CACHE STRING "Common library search hint for all ODB libs")
find_path(libodb_INCLUDE_DIR
NAMES odb/version.hxx
HINTS
${PC_LIBODB_INCLUDE_DIRS})
find_library(libodb_LIBRARY
NAMES odb libodb
HINTS
${ODB_LIBRARY_PATH}
${PC_LIBODB_LIBRARY_DIRS})
find_program(odb_BIN
NAMES odb
HINTS
${libodb_INCLUDE_DIR}/../bin)
set(ODB_LIBODB_INCLUDE_DIRS ${libodb_INCLUDE_DIR} CACHE STRING "ODB libodb include dirs")
set(ODB_LIBODB_LIBRARIES ${libodb_LIBRARY} CACHE STRING "ODB libodb library")
set(ODB_EXECUTABLE ${odb_BIN} CACHE STRING "ODB executable")
mark_as_advanced(libodb_INCLUDE_DIR libodb_LIBRARY odb_BIN)
if(ODB_LIBODB_INCLUDE_DIRS AND ODB_LIBODB_LIBRARIES)
set(ODB_LIBODB_FOUND TRUE)
endif()
set(ODB_INCLUDE_DIRS ${ODB_LIBODB_INCLUDE_DIRS})
set(ODB_LIBRARIES ${ODB_LIBODB_LIBRARIES})
foreach(component ${ODB_FIND_COMPONENTS})
find_odb_api(${component})
endforeach()
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(ODB
FOUND_VAR ODB_FOUND
REQUIRED_VARS ODB_EXECUTABLE ODB_LIBODB_FOUND
HANDLE_COMPONENTS)

View file

@ -0,0 +1,79 @@
############################################################################
# FindSofiaSipUa.txt
# Copyright (C) 2014 Belledonne Communications, Grenoble France
#
############################################################################
#
# 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, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
############################################################################
#
# - Find the sofia-sip include file and library
#
# SOFIASIPUA_FOUND - system has sofia-sip
# SOFIASIPUA_INCLUDE_DIRS - the sofia-sip include directory
# SOFIASIPUA_LIBRARIES - The libraries needed to use sofia-sip
# SOFIASIPUA_CPPFLAGS - The cflags needed to use sofia-sip
set(_SOFIASIPUA_ROOT_PATHS
${WITH_SOFIASIPUA}
${CMAKE_INSTALL_PREFIX}
)
find_path(SOFIASIPUA_INCLUDE_DIRS
NAMES sofia-sip/sip.h
HINTS _SOFIASIPUA_ROOT_PATHS
PATH_SUFFIXES include/sofia-sip-1.13 include/sofia-sip-1.12
)
if(SOFIASIPUA_INCLUDE_DIRS)
set(HAVE_SOFIASIPUA_SOFIASIPUA_H 1)
file(STRINGS "${SOFIASIPUA_INCLUDE_DIRS}/sofia-sip/sofia_features.h" SOFIASIPUA_VERSION_STR
REGEX "^#define[\t ]+SOFIA_SIP_VERSION[\t ]+\"([0-9a-zA-Z\.])+\"$")
string(REGEX REPLACE "^.*SOFIA_SIP_VERSION[\t ]+\"([0-9a-zA-Z\.]+)\"$"
"\\1" SOFIASIPUA_VERSION "${SOFIASIPUA_VERSION_STR}")
endif()
find_library(SOFIASIPUA_LIBRARIES
NAMES sofia-sip-ua
HINTS ${_SOFIASIPUA_ROOT_PATHS}
PATH_SUFFIXES bin lib
)
if(WIN32)
list(APPEND SOFIASIPUA_LIBRARIES ws2_32 delayimp Winmm Qwave)
endif(WIN32)
list(REMOVE_DUPLICATES SOFIASIPUA_INCLUDE_DIRS)
list(REMOVE_DUPLICATES SOFIASIPUA_LIBRARIES)
set(SOFIASIPUA_CPPFLAGS "")
include(FindPackageHandleStandardArgs)
if (SOFIASIPUA_VERSION)
find_package_handle_standard_args(SofiaSipUa
REQUIRED_VARS SOFIASIPUA_INCLUDE_DIRS SOFIASIPUA_LIBRARIES
VERSION_VAR SOFIASIPUA_VERSION
)
else()
find_package_handle_standard_args(SofiaSipUa
REQUIRED_VARS SOFIASIPUA_INCLUDE_DIRS SOFIASIPUA_LIBRARIES
)
endif()
mark_as_advanced(SOFIASIPUA_INCLUDE_DIRS SOFIASIPUA_LIBRARIES SOFIASIPUA_CPPFLAGS)

61
cmake/FindZlib.cmake Normal file
View file

@ -0,0 +1,61 @@
############################################################################
# FindZlib.cmake
# Copyright (C) 2015-2018 Belledonne Communications, Grenoble France
#
############################################################################
#
# 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, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
############################################################################
#
# - Find the zlib include file and library
#
# ZLIB_FOUND - system has zlib
# ZLIB_INCLUDE_DIRS - the zlib include directory
# ZLIB_LIBRARIES - The libraries needed to use zlib
if(ZLIB_HINTS)
set(ZLIB_LIBRARIES_HINTS "${ZLIB_HINTS}/lib")
endif()
find_path(ZLIB_INCLUDE_DIRS
NAMES zlib.h
HINTS "${ZLIB_HINTS}"
PATH_SUFFIXES include
)
if(ZLIB_INCLUDE_DIRS)
set(HAVE_ZLIB_H 1)
endif()
if(ENABLE_STATIC)
find_library(ZLIB_LIBRARIES
NAMES zstatic zlibstatic zlibstaticd z
HINTS "${ZLIB_LIBRARIES_HINTS}"
)
else()
find_library(ZLIB_LIBRARIES
NAMES z zlib zlibd
HINTS "${ZLIB_LIBRARIES_HINTS}"
)
endif()
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(Zlib
DEFAULT_MSG
ZLIB_INCLUDE_DIRS ZLIB_LIBRARIES HAVE_ZLIB_H
)
mark_as_advanced(ZLIB_INCLUDE_DIRS ZLIB_LIBRARIES HAVE_ZLIB_H)

77
cmake/Findjsoncpp.cmake Normal file
View file

@ -0,0 +1,77 @@
############################################################################
# Findjsoncpp.cmake
# Copyright (C) 2010-2022 Belledonne Communications, Grenoble France
#
############################################################################
#
# 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, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
############################################################################
include(FindPackageHandleStandardArgs)
if(INTERNAL_JSONCPP)
if(NOT TARGET jsoncpp_lib)
add_subdirectory("${PROJECT_SOURCE_DIR}/linphone-sdk/external/jsoncpp" "${PROJECT_BINARY_DIR}/linphone-sdk/external/jsoncpp")
endif()
find_package_handle_standard_args(jsoncpp DEFAULT_MSG)
else()
find_package(jsoncpp CONFIG)
if(jsoncpp_FOUND)
get_target_property(JSONCPP_INCLUDE_DIR jsoncpp_lib INTERFACE_INCLUDE_DIRECTORIES)
find_file(JSON_HEADER_FOUND NAMES "json.h" PATHS "${JSONCPP_INCLUDE_DIR}/json/" NO_CACHE)
if(NOT JSON_HEADER_FOUND)
# CentOS 7-8 & RockyLinux
message(STATUS "Invalid jsoncpp include directory detected [${JSONCPP_INCLUDE_DIR}]. Trying '${JSONCPP_INCLUDE_DIR}/jsoncpp'")
string(APPEND JSONCPP_INCLUDE_DIR "/jsoncpp")
find_file(JSON_HEADER_FOUND NAMES "json.h" PATHS "${JSONCPP_INCLUDE_DIR}/json/" NO_CACHE)
if(NOT JSON_HEADER_FOUND)
message(FATAL_ERROR "CMake config file for 'jsoncpp' library is invalid. Try -DINTERNAL_JSONCPP=ON")
endif()
set_target_properties(jsoncpp_lib PROPERTIES INTERFACE_INCLUDE_DIRECTORIES "${JSONCPP_INCLUDE_DIR}")
endif()
else()
message(STATUS "Searching for jsoncpp in module mode")
find_path(jsoncpp_INCLUDE_DIR "json/json.h" PATH_SUFFIXES "jsoncpp")
find_library(jsoncpp_LIBRARIES jsoncpp)
# Get version number
if (jsoncpp_INCLUDE_DIR)
set(version_def_line_regex "^[ \\t]*#[ \\t]*define[ \\t]+JSONCPP_VERSION_STRING[ \\t]+\"([0-9]+\\.[0-9]+\\.[0-9]+)\"[ \\t]*$")
file(STRINGS "${jsoncpp_INCLUDE_DIR}/json/version.h" version_def_line REGEX "${version_def_line_regex}")
if (NOT version_def_line)
message(FATAL_ERROR "Cannot find version number of jsoncpp")
endif()
string(REGEX REPLACE "${version_def_line_regex}" "\\1" version "${version_def_line}")
endif()
find_package_handle_standard_args(jsoncpp
REQUIRED_VARS jsoncpp_INCLUDE_DIR jsoncpp_LIBRARIES
VERSION_VAR version
)
mark_as_advanced(jsoncpp_LIBRARIES jsoncpp_INCLUDE_DIR)
if(jsoncpp_FOUND)
add_library(jsoncpp_lib SHARED IMPORTED)
set_target_properties(jsoncpp_lib
PROPERTIES
IMPORTED_LOCATION ${jsoncpp_LIBRARIES}
INTERFACE_INCLUDE_DIRECTORIES ${jsoncpp_INCLUDE_DIR}
)
endif()
endif()
endif()

39
cmake/FlexisipUtils.cmake Normal file
View file

@ -0,0 +1,39 @@
############################################################################
# FlexisipUtils.cmake
# Copyright (C) 2010-2021 Belledonne Communications, Grenoble France
#
############################################################################
#
# 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, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
############################################################################
# Add the 'ENABLE_CCACHE' option that allows to find the ccache
# executable on the system to use it as launcher for the C/C++
# compiler.
#
# Parameter:
# * default[in]: default value of ENABLE_CCACHE option. Must be a boolean.
macro(add_ccache_option default)
option(ENABLE_CCACHE "Use CCache to accelerate the build" ${default})
if(ENABLE_CCACHE)
find_program(CCACHE_EXECUTABLE "ccache")
if(CCACHE_EXECUTABLE)
message(STATUS "Using '${CCACHE_EXECUTABLE}' as C/C++ compiler launcher")
set(CMAKE_C_COMPILER_LAUNCHER "${CCACHE_EXECUTABLE}")
set(CMAKE_CXX_COMPILER_LAUNCHER "${CCACHE_EXECUTABLE}")
endif()
endif()
endmacro()

185
cmake/LinphoneSDK.cmake Normal file
View file

@ -0,0 +1,185 @@
############################################################################
# LinphoneSDK.cmake
# Copyright (C) 2010-2023 Belledonne Communications, Grenoble France
#
############################################################################
#
# 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, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
############################################################################
############################################################################
# Add Linphone SDK project as subproject
############################################################################
function(add_linphonesdk)
if(ENABLE_TRANSCODER OR ENABLE_CONFERENCE OR ENABLE_B2BUA)
set(BUILD_MEDIASTREAMER2 ON)
set(ENABLE_ZRTP ON)
else()
set(BUILD_MEDIASTREAMER2 OFF)
set(ENABLE_ZRTP OFF)
endif()
set(BUILD_SOCI ${SOCI_REQUIRED})
if(BUILD_SOCI)
set(BUILD_SOCI_BACKENDS "mysql;sqlite3")
if(ENABLE_SOCI_POSTGRESQL_BACKEND)
string(APPEND BUILD_SOCI_BACKENDS ";postgresql")
endif()
endif()
if(ENABLE_PRESENCE OR ENABLE_MDNS OR ENABLE_CONFERENCE OR ENABLE_UNIT_TESTS)
set(BUILD_BELLESIP ON)
endif()
set(BUILD_LIBLINPHONE ${LIBLINPHONE_REQUIRED})
# # Global SDK config
set(ENABLE_DOC OFF)
set(ENABLE_PACKAGE_SOURCE OFF)
set(ENABLE_STRICT ${ENABLE_STRICT_LINPHONESDK})
set(ENABLE_TESTS_COMPONENT ON)
set(ENABLE_TOOLS OFF)
set(ENABLE_UNIT_TESTS ${ENABLE_LIBLINPHONE_TESTER})
set(ENABLE_SANITIZER ${ENABLE_SANITIZERS})
# Global features activation
set(BUILD_GSM OFF)
set(BUILD_JSONCPP ${INTERNAL_JSONCPP})
set(BUILD_AOM OFF)
set(BUILD_DAV1D OFF)
set(BUILD_LIBVPX OFF)
set(BUILD_LIBXML2 OFF)
set(BUILD_MBEDTLS_WITH_FATAL_WARNINGS OFF)
set(BUILD_OPUS OFF)
set(BUILD_SPEEX OFF)
set(BUILD_SQLITE3 OFF)
set(BUILD_XERCESC OFF)
set(BUILD_ZLIB OFF)
set(ENABLE_ADVANCED_IM ON)
set(ENABLE_AMRNB OFF)
set(ENABLE_AMRWB OFF)
set(ENABLE_ASSETS OFF)
set(ENABLE_BV16 OFF)
set(ENABLE_CODEC2 OFF)
set(ENABLE_CSHARP_WRAPPER OFF)
set(ENABLE_CXX_WRAPPER ${BUILD_LIBLINPHONE})
set(ENABLE_DB_STORAGE ON)
set(ENABLE_DECAF ON)
set(ENABLE_FFMPEG OFF)
set(ENABLE_FLEXIAPI OFF)
set(ENABLE_G726 OFF)
set(ENABLE_G729 OFF) # Disable for license conformity
set(ENABLE_G729B_CNG OFF) # Disable for license conformity
set(ENABLE_GSM ON)
set(ENABLE_ILBC OFF)
set(ENABLE_ISAC OFF)
set(ENABLE_JAVA_WRAPPER OFF)
set(ENABLE_JAZZY_DOC OFF)
set(ENABLE_JPEG OFF)
set(ENABLE_LDAP OFF)
set(ENABLE_LIBYUV OFF)
set(ENABLE_LIME OFF)
set(ENABLE_LIME_X3DH ${BUILD_LIBLINPHONE})
# ENABLE_MBEDTLS must be a cache variable because this option is declared by
# libsrtp2 project as cache variable instead of using option() command. That avoid Flexisip
# to masquerade this variable by using CMP0077 new behavior.
set(ENABLE_MBEDTLS ON CACHE BOOL "Enable MbedTLS support." FORCE)
mark_as_advanced(ENABLE_MBEDTLS)
set(ENABLE_MKV OFF)
set(ENABLE_NON_FREE_FEATURES OFF)
set(ENABLE_OPENH264 OFF)
set(ENABLE_OPUS ON)
set(ENABLE_PQCRYPTO OFF)
set(ENABLE_QRCODE OFF)
set(ENABLE_RELATIVE_PREFIX OFF)
set(ENABLE_SILK OFF)
set(ENABLE_SPEEX_CODEC ON)
set(ENABLE_SPEEX_DSP ON)
set(ENABLE_SRTP ON)
set(ENABLE_SWIFT_WRAPPER OFF)
set(ENABLE_SWIFT_WRAPPER_COMPILATION OFF)
set(ENABLE_THEORA OFF)
set(ENABLE_TUNNEL OFF)
set(ENABLE_VCARD OFF)
set(ENABLE_VIDEO ON)
set(ENABLE_VPX ${BUILD_MEDIASTREAMER2})
set(ENABLE_AV1 OFF)
set(ENABLE_WEBRTC_AEC OFF)
set(ENABLE_WEBRTC_VAD OFF)
set(BUILD_LIBSRTP2 ${INTERNAL_LIBSRTP2})
set(BUILD_MBEDTLS ${INTERNAL_MBEDTLS})
# BcToolbox specific config
set(ENABLE_DEFAULT_LOG_HANDLER OFF)
# BZRTP specific config
set(ENABLE_ZIDCACHE ${ENABLE_ZRTP})
set(ENABLE_EXPORTEDKEY_V1_0_RETROCOMPATIBILITY OFF)
set(ENABLE_GOCLEAR ON)
set(ENABLE_PQCRYPTO OFF)
if(BUILD_MEDIASTREAMER2)
# Mediastreamer specific config
set(ENABLE_FIXED_POINT OFF)
set(ENABLE_PCAP OFF)
set(ENABLE_SOUND OFF) # Disable all sound card backends.
set(ENABLE_V4L OFF) # Disable video capture
# Disable video rendering
set(ENABLE_GL OFF)
set(ENABLE_GLX OFF)
set(ENABLE_SDL OFF)
set(ENABLE_X11 OFF)
set(ENABLE_XV OFF)
set(ENABLE_RESAMPLE ON)
endif()
if(BUILD_BELLESIP)
# Belle-sip specific config
set(ENABLE_RTP_MAP_ALWAYS_IN_SDP OFF)
endif()
if(ENABLE_LIME_X3DH)
# Lime specific config
set(ENABLE_CURVE25519 YES)
set(ENABLE_CURVE448 YES)
set(ENABLE_PROFILING NO)
set(ENABLE_C_INTERFACE NO)
set(ENABLE_JNI NO)
endif()
if(BUILD_LIBLINPHONE)
# Liblinphone specific config
set(ENABLE_CONSOLE_UI OFF)
set(ENABLE_DATE OFF)
set(ENABLE_JAVADOC OFF)
set(ENABLE_TUTORIALS OFF)
set(ENABLE_UPDATE_CHECK OFF)
if(APPLE)
set(ENABLE_DAEMON OFF)
else()
set(ENABLE_DAEMON ON)
endif()
endif()
set(LINPHONESDK_BUILD_TYPE "Flexisip")
add_subdirectory("linphone-sdk")
endfunction()
add_linphonesdk()

167
cmake/UseODB.cmake Normal file
View file

@ -0,0 +1,167 @@
set(ODB_COMPILE_DEBUG FALSE)
set(ODB_COMPILE_OUTPUT_DIR "${CMAKE_CURRENT_BINARY_DIR}/odb_gen")
set(ODB_COMPILE_HEADER_SUFFIX ".h")
set(ODB_COMPILE_INLINE_SUFFIX "_inline.h")
set(ODB_COMPILE_SOURCE_SUFFIX ".cpp")
set(ODB_COMPILE_FILE_SUFFIX "_odb")
set(CMAKE_INCLUDE_CURRENT_DIR TRUE)
function(odb_compile outvar)
if(NOT ODB_EXECUTABLE)
message(FATAL_ERROR "odb compiler executable not found")
endif()
set(options GENERATE_QUERY GENERATE_SESSION GENERATE_SCHEMA GENERATE_PREPARED)
set(oneValueParams SCHEMA_FORMAT SCHEMA_NAME TABLE_PREFIX
STANDARD SLOC_LIMIT
HEADER_PROLOGUE INLINE_PROLOGUE SOURCE_PROLOGUE
HEADER_EPILOGUE INLINE_EPILOGUE SOURCE_EPILOGUE
MULTI_DATABASE
PROFILE)
set(multiValueParams FILES INCLUDE DB)
cmake_parse_arguments(PARAM "${options}" "${oneValueParams}" "${multiValueParams}" ${ARGN})
if(PARAM_UNPARSED_ARGUMENTS)
message(FATAL_ERROR "invalid arguments passed to odb_wrap_cpp: ${PARAM_UNPARSED_ARGUMENTS}")
endif()
if(NOT PARAM_FILES)
message(FATAL_ERROR: "no input files to odb_compile")
endif()
set(ODB_ARGS)
if(PARAM_MULTI_DATABASE)
list(APPEND ODB_ARGS --multi-database "${PARAM_MULTI_DATABASE}")
list(APPEND PARAM_DB common)
endif()
foreach(db ${PARAM_DB})
list(APPEND ODB_ARGS -d "${db}")
endforeach()
if(PARAM_GENERATE_QUERY)
list(APPEND ODB_ARGS --generate-query)
endif()
if(PARAM_GENERATE_SESSION)
list(APPEND ODB_ARGS --generate-session)
endif()
if(PARAM_GENERATE_SCHEMA)
list(APPEND ODB_ARGS --generate-schema)
endif()
if(PARAM_GENERATE_PREPARED)
list(APPEND ODB_ARGS --generate-prepared)
endif()
if(PARAM_SCHEMA_FORMAT)
list(APPEND ODB_ARGS --schema-format "${PARAM_SCHEMA_FORMAT}")
endif()
if(PARAM_SCHEMA_NAME)
list(APPEND ODB_ARGS --schema-name "${PARAM_SCHEMA_NAME}")
endif()
if(PARAM_TABLE_PREFIX)
list(APPEND ODB_ARGS --table-prefix "${PARAM_TABLE_PREFIX}")
endif()
if(PARAM_STANDARD)
list(APPEND ODB_ARGS --std "${PARAM_STANDARD}")
endif()
if(PARAM_SLOC_LIMIT)
list(APPEND ODB_ARGS --sloc-limit "${PARAM_SLOC_LIMIT}")
endif()
if(PARAM_HEADER_PROLOGUE)
list(APPEND ODB_ARGS --hxx-prologue-file "${PARAM_HEADER_PROLOGUE}")
endif()
if(PARAM_INLINE_PROLOGUE)
list(APPEND ODB_ARGS --ixx-prologue-file "${PARAM_INLINE_PROLOGUE}")
endif()
if(PARAM_SOURCE_PROLOGUE)
list(APPEND ODB_ARGS --cxx-prologue-file "${PARAM_SOURCE_PROLOGUE}")
endif()
if(PARAM_HEADER_EPILOGUE)
list(APPEND ODB_ARGS --hxx-epilogue-file "${PARAM_HEADER_EPILOGUE}")
endif()
if(PARAM_INLINE_EPILOGUE)
list(APPEND ODB_ARGS --ixx-epilogue-file "${PARAM_INLINE_EPILOGUE}")
endif()
if(PARAM_SOURCE_EPILOGUE)
list(APPEND ODB_ARGS --cxx-epilogue-file "${PARAM_SOURCE_EPILOGUE}")
endif()
if(PARAM_PROFILE)
list(APPEND ODB_ARGS --profile "${PARAM_PROFILE}")
endif()
list(APPEND ODB_ARGS --output-dir "${ODB_COMPILE_OUTPUT_DIR}")
list(APPEND ODB_ARGS --hxx-suffix "${ODB_COMPILE_HEADER_SUFFIX}")
list(APPEND ODB_ARGS --ixx-suffix "${ODB_COMPILE_INLINE_SUFFIX}")
list(APPEND ODB_ARGS --cxx-suffix "${ODB_COMPILE_SOURCE_SUFFIX}")
if(PARAM_MULTI_DATABASE AND NOT "${ODB_COMPILE_FILE_SUFFIX}" MATCHES ".+:.+")
set(osuffix "${ODB_COMPILE_FILE_SUFFIX}")
set(ODB_COMPILE_FILE_SUFFIX)
foreach(db ${PARAM_DB})
if("${db}" MATCHES "common")
list(APPEND ODB_COMPILE_FILE_SUFFIX "${db}:${osuffix}")
else()
list(APPEND ODB_COMPILE_FILE_SUFFIX "${db}:${osuffix}_${db}")
endif()
endforeach()
endif()
foreach(sfx ${ODB_COMPILE_FILE_SUFFIX})
list(APPEND ODB_ARGS --odb-file-suffix "${sfx}")
endforeach()
foreach(dir ${PARAM_INCLUDE} ${ODB_INCLUDE_DIRS})
list(APPEND ODB_ARGS "-I${dir}")
endforeach()
file(REMOVE_RECURSE "${ODB_COMPILE_OUTPUT_DIR}")
file(MAKE_DIRECTORY "${ODB_COMPILE_OUTPUT_DIR}")
foreach(input ${PARAM_FILES})
get_filename_component(fname "${input}" NAME_WE)
set(outputs)
foreach(sfx ${ODB_COMPILE_FILE_SUFFIX})
string(REGEX REPLACE ":.*$" "" pfx "${sfx}")
string(REGEX REPLACE "^.*:" "" sfx "${sfx}")
if(NOT "${PARAM_MULTI_DATABASE}" MATCHES "static" OR NOT "${pfx}" MATCHES "common")
set(output "${ODB_COMPILE_OUTPUT_DIR}/${fname}${sfx}${ODB_COMPILE_SOURCE_SUFFIX}")
list(APPEND ${outvar} "${output}")
list(APPEND outputs "${output}")
endif()
endforeach()
if(ODB_COMPILE_DEBUG)
set(_msg "${ODB_EXECUTABLE} ${ODB_ARGS} ${input}")
string(REPLACE ";" " " _msg "${_msg}")
message(STATUS "${_msg}")
endif()
add_custom_command(OUTPUT ${outputs}
COMMAND ${ODB_EXECUTABLE} ${ODB_ARGS} "${input}"
DEPENDS "${input}"
WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
VERBATIM)
endforeach()
set(${outvar} ${${outvar}} PARENT_SCOPE)
endfunction()

View file

@ -0,0 +1,74 @@
/*
Flexisip, a flexible SIP proxy server with media capabilities.
Copyright (C) 2010-2024 Belledonne Communications SARL, All rights reserved.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero 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 Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#cmakedefine CONFIG_DIR "${CONFIG_DIR}"
#cmakedefine INSTALL_PREFIX "${CMAKE_INSTALL_PREFIX}"
#cmakedefine ENABLE_SNMP 1
#cmakedefine ENABLE_LIBODB_MYSQL 1
#cmakedefine ENABLE_TRANSCODER 1
#cmakedefine ENABLE_PRESENCE 1
#cmakedefine ENABLE_CONFERENCE 1
#cmakedefine ENABLE_B2BUA 1
#cmakedefine ENABLE_ODBC 1
#cmakedefine ENABLE_REDIS 1
#cmakedefine ENABLE_SOCI 1
#cmakedefine ENABLE_FLEXIAPI 1
#cmakedefine ENABLE_MDNS 1
#cmakedefine ENABLE_UNIT_TESTS 1
#cmakedefine ENABLE_UNIT_TESTS_NGHTTP2ASIO 1
#cmakedefine HAVE_DATEHANDLER 1
#cmakedefine HAVE_ARC4RANDOM 1
#cmakedefine HAVE_SYS_PRCTL_H 1
/* Whether the liblinphone library has been linked. */
#cmakedefine HAVE_LIBLINPHONE 1
/* Whether the liblinphone++ library has been linked. */
#cmakedefine HAVE_LIBLINPHONECXX 1
#cmakedefine MEDIARELAY_SPECIFIC_FEATURES_ENABLED 1
#cmakedefine MONOTONIC_CLOCK_REGISTRATIONS 1
// Anything that exits successfully without fuss
#cmakedefine DUMMY_EXEC "${DUMMY_EXEC}"
#define SNMP_COMPANY_OID 10000
/* oRTP ABI version */
#define ORTP_ABI_VERSION 9
/* oRTP rtp_session_set_reuseaddr availabled */
#define ORTP_HAS_REUSEADDR 1
/* Default lib directory */
#define DEFAULT_LIB_DIR "@CMAKE_INSTALL_FULL_LOCALSTATEDIR@/lib/flexisip"
/* Default log directory */
#define DEFAULT_LOG_DIR "@CMAKE_INSTALL_FULL_LOCALSTATEDIR@/log/flexisip"
/* Default b2bua data dir */
#define DEFAULT_B2BUA_DATA_DIR "@CMAKE_INSTALL_FULL_LOCALSTATEDIR@/flexisip/b2b"
/* Default directory for Flexisip's plugins */
#define DEFAULT_PLUGINS_DIR "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}/flexisip/plugins"
#define BELR_GRAMMARS_DIR "${BELR_GRAMMARS_DIR}"
#define FIREBASE_GET_ACCESS_TOKEN_SCRIPT_PATH "${CMAKE_INSTALL_FULL_DATADIR}/flexisip/firebase_v1_get_access_token.py"

257
doc/xw.py Executable file
View file

@ -0,0 +1,257 @@
#!/bin/python
import sys
if sys.version_info.major < 3:
print('ERROR: current Python version is {0}.{1}.{2} whereas Python 3 is required.'.format(
sys.version_info[0], sys.version_info[1], sys.version_info[2]
))
sys.exit(1)
import argparse
import base64
import os
import re
import subprocess
import urllib.request
class Version:
def __init__(self, major=0, minor=0, patch=0, branch=None, ncommit=0, _hash=None, fromStr=None):
if fromStr is not None:
m = re.match('^([0-9]+)\\.([0-9]+)\\.([0-9]+)(-alpha|-beta|-pre)?((-[0-9]+)(-g[0-9a-f]+))?$', fromStr)
if m is None:
raise ValueError("'{0}' isn't a valid git describe string".format(fromStr))
major = m.group(1)
minor = m.group(2)
patch = m.group(3)
if m.group(4) is not None:
branch = m.group(4)[1:]
if m.group(5) is not None:
ncommit = int(m.group(6)[1:])
_hash = m.group(7)[2:]
if ncommit > 0 and _hash is None:
raise ValueError('missing hash')
if ncommit == 0 and _hash is not None:
raise ValueError('missing or null commit number')
self.major = major
self.minor = minor
self.patch = patch
self.branch = branch
self.ncommit = ncommit
self.hash = _hash
@property
def short_version(self):
return '{0}.{1}.{2}'.format(self.major, self.minor, self.patch)
@property
def git_version(self):
res = self.short_version
if self.branch is not None:
res += '-{0}'.format(self.branch)
if self.ncommit > 0:
res += '-{0}-g{1}'.format(self.ncommit, self.hash)
return res
class FlexisipProxy:
def __init__(self, binaryPath):
self.path = binaryPath
self._version = None
@property
def section_list(self):
p = subprocess.Popen([self.path, '--list-sections'], stdout=subprocess.PIPE , stderr=subprocess.PIPE)
out, err = p.communicate()
return str(out, encoding='utf-8').rstrip('\n').split('\n')
@property
def version(self):
if self._version is None:
_version = self._get_version()
return _version
def dump_section_doc(self, moduleName):
p = subprocess.Popen([self.path, '--dump-format', 'xwiki', '--show-experimental', '--dump-default', moduleName], stdout=subprocess.PIPE , stderr=subprocess.PIPE)
out, err = p.communicate()
out = str(out, encoding='utf-8')
# replace all the -- in the doc with {{{--}}} to escape xwiki autoformatting -- into striken
return re.sub("--", "{{{--}}}", out)
def _get_version(self):
p = subprocess.Popen([self.path, '-v'], stdout=subprocess.PIPE , stderr=subprocess.PIPE)
out, err = p.communicate()
out = str(out, encoding='utf-8')
m = re.search('version: ([.a-z0-9-]+)', out)
if m is None:
raise RuntimeError("unexpected output of 'flexisip -v': [{0}]".format(out))
version = m.group(1)
try:
return Version(fromStr=version)
except ValueError:
raise RuntimeError("invalid version string in output of 'flexisip -v' [{0}]".format(version))
class XWikiProxy:
class Credential:
def __init__(self, user, password):
self.user = user
self.password = password
def to_base64(self):
return base64.b64encode(bytes('{0}:{1}'.format(self.user, self.password), encoding='utf-8'))
def __init__(self, host, wikiname, credentials=None, cafile=None):
m = re.fullmatch('(?:(http[s]?)\\://)?(\\S+)', host)
if m is None:
raise ValueError('invalid host [{0}]'.format(host))
self.scheme = m.group(1) if m.group(1) is not None else 'http'
self.host = m.group(2)
self.wikiname = wikiname
self.credentials = credentials
self.cafile = cafile
def update_page(self, path, content):
uri = self._forge_page_uri(path)
request = self._forge_http_request(uri, 'PUT', content)
response = urllib.request.urlopen(request, cafile=self.cafile)
if response.status not in (201, 202):
raise RuntimeError('page creation or modification has failed' if response.status == 304 \
else 'unexpected status code ({0})'.format(response.status))
def _forge_page_uri(self, path):
uri = self._forge_root_uri()
scopepath = os.path.dirname(path)
scopepath = scopepath.split('/')
if scopepath[0] == '':
del scopepath[0]
for scopename in scopepath:
uri += ('/spaces/' + self._escape(scopename))
pagename = os.path.basename(path)
uri += ('/pages/' + self._escape(pagename))
return uri
def _escape(self, string):
return string.translate({0x20 : '%20'})
def _forge_root_uri(self):
return self.scheme + '://' + self.host + XWikiProxy._apipath + '/wikis/' + self.wikiname
_apipath = '/xwiki/rest'
def _forge_http_request(self, uri, method, body):
headers = { 'Content-Type': 'text/plain' }
if self.credentials is not None:
headers['Authorization'] = ('Basic ' + str(self.credentials.to_base64(), encoding='ascii'))
return urllib.request.Request(uri, data=bytes(body, encoding='utf-8'), headers=headers, method=method)
class DocWriter:
def __init__(self, wikiProxy, fProxy):
self.proxy = wikiProxy
self.fProxy = fProxy
self.documentRoot = '/Flexisip/A. Configuration Reference Guide'
def write_and_push(self):
fProxy = FlexisipProxy(args.flexisip_binary)
childrenMacro = '{{children/}}'
wiki.update_page(os.path.join(self.documentRoot, 'WebHome'), childrenMacro)
wiki.update_page(os.path.join(self._get_version_page_path(), 'WebHome'), childrenMacro)
wiki.update_page(os.path.join(self._get_version_page_path(), 'module/WebHome'), childrenMacro)
for section in fProxy.section_list:
out = fProxy.dump_section_doc(section)
#add commit version on top of the file
message = "// Documentation based on repostory git version commit {0} //\n\n".format(fProxy.version.git_version)
out = message + out
path = self._section_name_to_page_path(section)
print("Updating page '{0}'".format(path))
wiki.update_page(path, out)
def _section_name_to_page_path(self, module_name):
return os.path.join(self._get_version_page_path(), *tuple(module_name.split('::')))
def _get_version_page_path(self):
if fProxy.version.branch == 'alpha':
version = 'master'
elif fProxy.version.branch is None or fProxy.version.branch == 'beta':
version = fProxy.version.short_version
else:
raise RuntimeError("Reference documentation isn't authorized to be pushed for this version of Flexisip [{0}]".format(fProxy.version.git_version))
return os.path.join(self.documentRoot, version)
class Settings:
def __init__(self):
self.section_name = 'main'
self.host = ''
self.wikiname = ''
self.user = ''
self.password = ''
def load(self, filename):
import configparser
config = configparser.ConfigParser()
config.read(config_file)
self.host = config.get(self.section_name, 'host', fallback=self.host)
self.wikiname = config.get(self.section_name, 'wiki', fallback=self.wikiname)
self.user = config.get(self.section_name, 'username')
self.password = config.get(self.section_name, 'password')
def dump_example(self):
return """[{section}]
host=example.com
wiki=public
username=titi
password=toto""".format(self.section_name)
if __name__ == '__main__':
# parse cli arguments
parser = argparse.ArgumentParser(description='Send the Flexisip documentation to the Wiki. All options passed override the config file.')
parser.add_argument('-H', '--host' , help='the host to which we should send the documentation', default='wiki.linphone.org:8080')
parser.add_argument('-w', '--wiki' , help='name of the wiki', default='public', dest='wikiname')
parser.add_argument('-u', '--user' , help='the user to authenticate to the server', dest='config_user')
parser.add_argument('-p', '--password' , help='the password to authenticate to the server', dest='config_password')
parser.add_argument('--cafile' , help='file containing a set of trusted certificates', default=None)
parser.add_argument('--flexisip-binary', help='location of the Flexisip executable to run', default='../OUTPUT/bin/flexisip')
args = parser.parse_args()
# read from a configuration file for user/pass/host. This allows for out-of-cli specification of these parameters.
settings = Settings()
config_file = os.path.expanduser('~/.flexiwiki.x.cfg')
if os.access(config_file, os.R_OK):
settings.load(config_file)
# require a password for REST
if args.config_password is not None:
settings.password = args.config_password
if args.config_user is not None:
settings.user = args.config_user
if args.host is not None:
settings.host = args.host
if args.wikiname is not None:
settings.wikiname = args.wikiname
if settings.password == '':
print("Please define a password using " + config_file + " or using the --password option")
print("Example of " + config_file + ":")
print(settings.dump_example())
sys.exit(1)
credentials = XWikiProxy.Credential(settings.user, settings.password)
wiki = XWikiProxy(settings.host, settings.wikiname, credentials=credentials, cafile=args.cafile)
fProxy = FlexisipProxy(args.flexisip_binary)
docWriter = DocWriter(wiki, fProxy)
docWriter.write_and_push()

6
docker/Belledonne.repo Normal file
View file

@ -0,0 +1,6 @@
[Belledonne]
name=Belledonne
baseurl=https://linphone.org/snapshots/centos7
enabled=1
gpgcheck=0

37
docker/Dockerfile Normal file
View file

@ -0,0 +1,37 @@
FROM centos:7
MAINTAINER Jehan Monnier <jehan.monnier@linphone.org>
# Prepare the Belledonne's repository
COPY docker/Belledonne.repo /etc/yum.repos.d/Belledonne.repo
RUN yum -y install epel-release yum-downloadonly gdb
RUN yum update -y
# Download rpm to be able to skip systemd's scripts
RUN yum install -y --downloadonly --downloaddir=/opt bc-flexisip bc-flexisip-debuginfo bc-flexisip-jwe-auth-plugin
RUN mv /opt/bc-flexisip*.rpm /tmp
RUN rpm -i /opt/*.rpm
RUN rpm -i --noscripts /tmp/bc-flexisip*.rpm
#RUN echo '/tmp/core' > /proc/sys/kernel/core_pattern
RUN rm /opt/*.rpm
# Add it to the default path
ENV PATH=$PATH:/opt/belledonne-communications/bin
WORKDIR /opt/belledonne-communications
# Generate a default configuration
RUN flexisip --dump-default all > /etc/flexisip/flexisip.conf
VOLUME /etc/flexisip
COPY docker/flexisip-entrypoint.sh /
COPY docker/backtrace.gdb /
RUN chmod a+x /flexisip-entrypoint.sh
# Script to wait db before launch flexisip [Licence Apache2]
ADD https://github.com/ufoscout/docker-compose-wait/releases/download/2.2.1/wait /wait
RUN chmod +x /wait
RUN yum clean all
ENTRYPOINT ["/flexisip-entrypoint.sh"]
CMD flexisip

37
docker/Makefile Normal file
View file

@ -0,0 +1,37 @@
BASE_NAME=gitlab.linphone.org:4567/bc/public/flexisip
$(eval GIT_DESCRIBE = $(shell sh -c "git describe"))
DOCKER_TAG=$(BASE_NAME):$(GIT_DESCRIBE)
DOCKER_FILE=flex-from-src
# We cannot use dockerfile's COPY outside build context
# we use then flexisip directory as context for flex-from-src instead of docker directory
ifeq ($(DOCKER_FILE), flex-from-src)
CONTEXT=..
else
CONTEXT=.
endif
flexisip-build:
docker build -f $(DOCKER_FILE) --pull --no-cache -t $(DOCKER_TAG) --rm $(CONTEXT)
flexisip-push:
docker push $(DOCKER_TAG)
flexisip-clean:
docker image rm $(DOCKER_TAG)
flexisip-deb-before:
$(eval DOCKER_FILE = flex-from-deb)
# forcing context to .
# at the moment of the condition above being executed, $DOCKER_FILE doesn't have the right value
$(eval CONTEXT = .)
$(eval DOCKER_TAG = $(DOCKER_TAG)-deb)
flexisip-deb-build: flexisip-deb-before flexisip-build
flexisip-deb-push: flexisip-deb-before flexisip-push
flexisip-deb-clean: flexisip-deb-before flexisip-clean
.PHONY: flexisip-build

3
docker/backtrace.gdb Normal file
View file

@ -0,0 +1,3 @@
set debug-file-directory /opt/belledonne-communications/lib
thread apply all bt full
quit

103
docker/bc-dev-archlinux Normal file
View file

@ -0,0 +1,103 @@
###############################################################################
# Dockerfile used to make gitlab.linphone.org:4567/bc/public/flexisip/bc-dev-archlinux:20250127_update_clang
###############################################################################
FROM archlinux:base
LABEL org.opencontainers.image.authors="Belledonne Communications <flexisip@belledonne-communications.com>"
# Configure locale
RUN echo "en_US.UTF-8 UTF-8" > /etc/locale.gen && locale-gen
ENV LANG='en_US.UTF-8' LANGUAGE='en_US:en' LC_ALL='en_US.UTF-8'
ENV SHELL=/bin/bash
# Update the system
RUN pacman --noconfirm --noprogressbar --sync --refresh --sysupgrade \
# Install development tools
&& pacman --noconfirm --noprogressbar --sync \
ccache \
clang \
cmake \
doxygen \
gcc \
git \
make \
nasm \
ninja \
python-pip \
python-pystache \
python-six \
sudo \
locate \
yasm \
# Install Flexisip dependencies
gsm \
hiredis \
jansson \
jsoncpp \
libsrtp \
libxml2 \
mariadb \
mbedtls \
net-snmp \
openssl \
opus \
postgresql \
speex \
speexdsp \
sqlite \
xerces-c \
# Dependencies of the B2BUA (and video tests)
libvpx \
# Test dependencies
boost \
redis \
# Clean package cache
&& pacman --noconfirm -Scc
# Configure user bc
RUN useradd -m -s /bin/bash bc && \
echo 'bc:cotcot' | chpasswd && \
echo 'bc ALL=(ALL) NOPASSWD:ALL' >> /etc/sudoers
USER bc
WORKDIR /home/bc
ENTRYPOINT /bin/bash
CMD "--login"
RUN sudo pacman --noconfirm --noprogressbar --sync \
# Build-time dependencies of AUR packages
fakeroot \
debugedit \
# Install AUR packages
&& aurinstall() { \
# https://wiki.archlinux.org/title/AUR_helpers
local AURL="https://aur.archlinux.org/cgit/aur.git/snapshot/${1}.tar.gz"; \
local WDIR="$(mktemp --directory)"; \
cd "${WDIR}"; \
curl --remote-name --fail --silent "${AURL}"; \
tar --extract --file "${1}.tar.gz"; \
cd "${WDIR}/${1}"; \
makepkg --noconfirm --syncdeps --install --clean; \
rm -rf "${WDIR}"; \
} \
# OpenID Connect dependency
&& aurinstall cpp-jwt \
&& sudo pacman --noconfirm --remove --nosave --recursive \
fakeroot \
debugedit \
# Clean package cache
&& sudo pacman --noconfirm -Scc
# CVE-2022-24765, from git 2.35.2 onward
RUN git config --global --add safe.directory *
# Example build commands
#
# export CC=gcc
# export CXX=g++
# export BUILD_DIR_NAME="build.archlinux"
# cmake -S . -B ./$BUILD_DIR_NAME -G "Ninja" -DCMAKE_BUILD_TYPE=Debug -DCMAKE_INSTALL_PREFIX="$PWD/$BUILD_DIR_NAME/install" -DENABLE_UNIT_TESTS=ON -DENABLE_UNIT_TESTS_NGHTTP2ASIO=OFF
# cmake --build . --target install

98
docker/bc-dev-debian11 Normal file
View file

@ -0,0 +1,98 @@
###############################################################################
# Dockerfile used to make gitlab.linphone.org:4567/bc/public/flexisip/bc-dev-debian11:20240911_remove_protobuf
###############################################################################
FROM debian:bullseye
MAINTAINER Anthony Gauchy <anthony.gauchy@belledonne-communications.com>
# Update
RUN apt-get -y update \
# Install common tools
&& apt-get -y install sudo \
vim \
wget \
file \
# Install development tools
ccache \
clang \
cmake \
doxygen \
g++ \
gdb \
git \
make \
ninja-build \
python3 \
python3-pip \
yasm \
# Install all dependencies needed for Flexisip
libssl-dev \
libboost-dev \
libboost-system-dev \
libboost-thread-dev \
libhiredis-dev \
libjansson-dev \
libjsoncpp-dev \
libsqlite3-dev \
libpq-dev \
libmariadb-dev \
libmariadb-dev-compat \
libnghttp2-dev \
libsnmp-dev \
libxerces-c-dev \
libsrtp2-dev \
libgsm1-dev \
libopus-dev \
libmbedtls-dev \
libspeex-dev \
libspeexdsp-dev \
libxml2-dev \
redis-server \
# Dependencies of the B2BUA
libvpx-dev \
# Dependencies of the tester
mariadb-server \
# Dependencies of clang test coverage
llvm \
# Clean
&& apt-get -y autoremove \
&& apt-get -y clean
# Install CMake 3.22.6
COPY cmake_install.sh .
RUN ./cmake_install.sh 3.22.6 \
&& rm cmake_install.sh \
&& apt-get -y remove cmake
# Install libnghttp2_asio
RUN wget https://github.com/nghttp2/nghttp2/releases/download/v1.43.0/nghttp2-1.43.0.tar.bz2 && \
tar xf nghttp2-1.43.0.tar.bz2 && \
cd nghttp2-1.43.0 && \
./configure --prefix=/usr/local --disable-examples --disable-python-bindings --enable-lib-only --enable-asio-lib && \
make && \
sudo make -C src install && \
cd - && \
rm -rf nghttp2-1.43.0.tar.bz2 nghttp2-1.43.0
# Dependency of clang test coverage (converts lcov format to cobertura so Gitlab can display code coverage for files in diff view)
RUN pip install lcov_cobertura
# Configure user bc
RUN useradd -ms /bin/bash bc && \
echo 'bc:cotcot' | chpasswd && \
echo 'bc ALL=(ALL) NOPASSWD:ALL' >> /etc/sudoers
# Switch for 'bc' user
USER bc
WORKDIR /home/bc
# Install python3 modules.
# This must be done as 'bc' user because some python modules are installed into /usr/local/lib when pip is invoked
# as root, and rpmbuild prevent python from seaching its modules in this prefix. Using 'bc' user make the python
# modules to be installed into /home/bc/.local/bin.
RUN python3 -m pip install --user pystache six
ENV PATH=$PATH:/home/bc/.local/bin
# CVE-2022-24765, from git 2.35.2 onward
RUN git config --global --add safe.directory *

98
docker/bc-dev-debian12 Normal file
View file

@ -0,0 +1,98 @@
###############################################################################
# Dockerfile used to make gitlab.linphone.org:4567/bc/public/flexisip/bc-dev-debian12:20240911_remove_protobuf
###############################################################################
FROM debian:12
MAINTAINER Thibault Lemaire <thibault.lemaire@belledonne-communications.com>
# Update
RUN apt-get -y update \
&& apt-get -y install \
# Install common tools
sudo \
vim \
wget \
file \
# Install development tools
ccache \
clang \
cmake \
doxygen \
g++ \
gdb \
git \
make \
ninja-build \
python3 \
python3-pystache \
python3-six \
yasm \
# Install all dependencies needed for Flexisip
libssl-dev \
libboost-dev \
libboost-system-dev \
libboost-thread-dev \
libhiredis-dev \
libjansson-dev \
libjsoncpp-dev \
libnghttp2-dev \
libsqlite3-dev \
libpq-dev \
libmariadb-dev \
libmariadb-dev-compat \
libsnmp-dev \
libxerces-c-dev \
libsrtp2-dev \
libgsm1-dev \
libopus-dev \
libmbedtls-dev \
libspeex-dev \
libspeexdsp-dev \
libxml2-dev \
redis-server \
# Dependencies of the B2BUA
libvpx-dev \
# Dependencies of the tester
mariadb-server \
# Dependencies of CPack (to build the .deb)
dpkg-dev \
# Clean up
&& apt-get -y autoremove \
&& apt-get -y clean
# Install libnghttp2_asio
# Downloading the gz source and not bz2 to avoid installing bzip2.
# nghttp2-asio has been moved out of nghttp2 from v1.52.0 onward.
RUN wget https://github.com/nghttp2/nghttp2/releases/download/v1.51.0/nghttp2-1.51.0.tar.gz && \
tar xf nghttp2-1.51.0.tar.gz && \
cd nghttp2-1.51.0 && \
./configure --prefix=/usr/local --disable-examples --disable-python-bindings --enable-lib-only --enable-asio-lib && \
make -j4 && \
sudo make -C src install && \
cd - && \
rm -rf nghttp2-1.51.0.tar.gz nghttp2-1.51.0
# Configure user 'bc'
RUN useradd -ms /bin/bash bc && \
echo 'bc:cotcot' | chpasswd && \
echo 'bc ALL=(ALL) NOPASSWD:ALL' >> /etc/sudoers
# Switch to user 'bc'
USER bc
WORKDIR /home/bc
# CVE-2022-24765, from git 2.35.2 onward
RUN git config --global --add safe.directory *
# Example build commands
#
# cd flexisip/
# export CC=gcc
# export CXX=g++
# export BUILD_DIR_NAME="build.debian12"
# cmake -S . -B ./$BUILD_DIR_NAME -G "Ninja" -DCMAKE_BUILD_TYPE=Debug -DCMAKE_INSTALL_PREFIX="$PWD/$BUILD_DIR_NAME/install" -DCMAKE_PREFIX_PATH=/usr/local -DCMAKE_INSTALL_RPATH_USE_LINK_PATH=ON -DENABLE_UNIT_TESTS=ON
# cd $BUILD_DIR_NAME
# cmake --build . --target install
# LSAN_OPTIONS="suppressions=../sanitizer_ignore.txt" bin/flexisip_tester --resource-dir "../tester/"

51
docker/bc-dev-minimal Normal file
View file

@ -0,0 +1,51 @@
###############################################################################
# Dockerfile used to make gitlab.linphone.org:4567/bc/public/flexisip/bc-dev-minimal:20240911_remove_protobuf
###############################################################################
FROM debian:12
MAINTAINER Thibault Lemaire <thibault.lemaire@belledonne-communications.com>
# Update
RUN apt-get -y update \
&& apt-get -y install \
# Install build deps
ccache \
cmake \
g++ \
git \
make \
ninja-build \
python3 \
python3-pystache \
python3-six \
yasm \
# Install all dependencies needed for Flexisip
libssl-dev \
libnghttp2-dev \
# Clean up
&& apt-get -y autoremove \
&& apt-get -y clean
# Configure user 'bc'
RUN useradd -ms /bin/bash bc && \
echo 'bc:cotcot' | chpasswd && \
echo 'bc ALL=(ALL) NOPASSWD:ALL' >> /etc/sudoers
# Switch to user 'bc'
USER bc
WORKDIR /home/bc
# CVE-2022-24765, from git 2.35.2 onward
RUN git config --global --add safe.directory *
# Example build commands
#
# cd flexisip/
# export CC=gcc
# export CXX=g++
# export BUILD_DIR_NAME="build.minimal"
# cmake -S . -B ./$BUILD_DIR_NAME -G "Ninja" -DCMAKE_BUILD_TYPE=Debug -DCMAKE_INSTALL_PREFIX="$PWD/$BUILD_DIR_NAME/install" -DCMAKE_PREFIX_PATH=/usr/local -DENABLE_PRESENCE=OFF -DENABLE_REDIS=OFF -DENABLE_SNMP=OFF -DENABLE_SOCI=OFF -DENABLE_TRANSCODER=OFF -DENABLE_MDNS=OFF -DENABLE_EXTERNAL_AUTH_PLUGIN=OFF -DENABLE_JWE_AUTH_PLUGIN=OFF -DENABLE_CONFERENCE=OFF -DENABLE_SOCI_POSTGRESQL_BACKEND=OFF -DENABLE_B2BUA=OFF -DENABLE_UNIT_TESTS=OFF
# cd $BUILD_DIR_NAME
# cmake --build . --target install

99
docker/bc-dev-rocky8 Normal file
View file

@ -0,0 +1,99 @@
###############################################################################
# Dockerfile used to make gitlab.linphone.org:4567/bc/public/flexisip/bc-dev-rocky8:20240911_remove_protobuf
###############################################################################
FROM rockylinux:8
MAINTAINER Anthony Gauchy <anthony.gauchy@belledonne-communications.com>
# Install common general tools
RUN dnf install -y sudo vim wget
# Configure additional repositories
RUN dnf install -y epel-release && \
sudo dnf install -y dnf-plugins-core && \
sudo dnf config-manager -y --set-enabled powertools
# Update
RUN sudo dnf makecache --refresh && dnf -y update
# Install development tools
RUN sudo dnf -y install \
bzip2 \
ccache \
clang \
cmake \
c-ares-devel \
doxygen \
gcc \
gcc-c++ \
gdb \
git \
libasan \
libev-devel \
libubsan \
libuv-devel \
llvm \
make \
ninja-build \
python3 \
python3-pip \
rpm-build \
yasm \
zlib-devel \
# Install all dependencies needed for Flexisip
openssl-devel \
boost-devel \
hiredis-devel \
jansson-devel \
libsqlite3x-devel \
libpq-devel \
mariadb-devel \
nghttp2 \
libnghttp2-devel \
net-snmp-devel \
xerces-c-devel \
gsm-devel \
opus-devel \
mbedtls-devel \
speex-devel \
speexdsp-devel \
libxml2-devel \
redis \
# Dependencies of the B2BUA
libvpx-devel \
jsoncpp-devel \
# Dependencies of the tester
mariadb-server \
&& dnf -y clean all
# Install CMake 3.22.6
COPY cmake_install.sh .
RUN ./cmake_install.sh 3.22.6 \
&& rm cmake_install.sh \
&& dnf -y remove cmake
# Install libnghttp2_asio 1.39.2
COPY libnghttp2_asio_install.sh .
RUN ./libnghttp2_asio_install.sh 1.39.2 && rm libnghttp2_asio_install.sh
# Configure user bc
RUN useradd -ms /bin/bash bc && \
echo 'bc:cotcot' | chpasswd && \
echo 'bc ALL=(ALL) NOPASSWD:ALL' >> /etc/sudoers
# Switch to 'bc' user
USER bc
WORKDIR /home/bc
# Install python3 modules.
# This must be done as user 'bc' because python modules are installed in /usr/local/lib when pip is invoked
# as root, and rpmbuild prevents python from searching its modules in this prefix. Installing with user 'bc' puts
# the modules in /home/bc/.local/bin.
RUN python3 -m pip install --user pystache six
ENV PATH=$PATH:/home/bc/.local/bin
ENV PS1='\[\e[34m\]\u@bc-dev-rocky8>\[\e[0m\] '
# CVE-2022-24765, from git 2.35.2 onward
RUN git config --global --add safe.directory *

112
docker/bc-dev-rocky9 Normal file
View file

@ -0,0 +1,112 @@
###############################################################################
# Dockerfile used to make gitlab.linphone.org:4567/bc/public/flexisip/bc-dev-rocky9:20240911_remove_protobuf
###############################################################################
FROM rockylinux:9
MAINTAINER Thibault Lemaire <Thibault.lemaire@belledonne-communications.com>
# Install common general tools
RUN dnf install -y sudo vim wget
# Configure additional repositories
RUN dnf install -y \
epel-release \
dnf-plugins-core \
# Code Ready Builder
&& dnf config-manager -y --set-enabled crb
# Update
RUN dnf makecache --refresh \
&& dnf -y update \
# Install development tools
&& dnf -y install \
bzip2 \
ccache \
clang \
cmake \
c-ares-devel \
doxygen \
gcc \
gcc-c++ \
gdb \
git \
libasan \
libev-devel \
libubsan \
libuv-devel \
llvm \
make \
ninja-build \
python3 \
python3-pip \
rpm-build \
yasm \
zlib-devel \
# Install all dependencies needed for Flexisip
openssl-devel \
boost-devel \
hiredis-devel \
jansson-devel \
libsqlite3x-devel \
libpq-devel \
mariadb-devel \
nghttp2 \
libnghttp2-devel \
net-snmp-devel \
xerces-c-devel \
gsm-devel \
opus-devel \
mbedtls-devel \
speex-devel \
speexdsp-devel \
libxml2-devel \
redis \
# Dependencies of the B2BUA
libvpx-devel \
jsoncpp-devel \
# Dependencies of the tester
mariadb-server \
&& dnf -y clean all
# Install CMake 3.22.6
COPY cmake_install.sh .
RUN ./cmake_install.sh 3.22.6 \
&& rm cmake_install.sh \
&& dnf -y remove cmake
# Install libnghttp2_asio 1.39.2
COPY libnghttp2_asio_install.sh .
RUN ./libnghttp2_asio_install.sh 1.39.2
# Configure user bc
RUN useradd -ms /bin/bash bc && \
echo 'bc:cotcot' | chpasswd && \
echo 'bc ALL=(ALL) NOPASSWD:ALL' >> /etc/sudoers
# Switch to 'bc' user
USER bc
WORKDIR /home/bc
# Install python3 modules.
# This must be done as user 'bc' because python modules are installed in /usr/local/lib when pip is invoked
# as root, and rpmbuild prevents python from searching its modules in this prefix. Installing with user 'bc' puts
# the modules in /home/bc/.local/bin.
RUN python3 -m pip install --user pystache six
ENV PATH=$PATH:/home/bc/.local/bin
ENV PS1='\[\e[34m\]\u@bc-dev-rocky9>\[\e[0m\] '
# CVE-2022-24765, from git 2.35.2 onward
RUN git config --global --add safe.directory *
# Example build commands
#
# export CC=gcc
# export CXX=g++
# export BUILD_DIR_NAME="build.rocky9"
# cd flexisip/
# cmake -S . -B ./$BUILD_DIR_NAME -G "Ninja" -DCMAKE_BUILD_TYPE=Debug -DCMAKE_INSTALL_PREFIX="$PWD/$BUILD_DIR_NAME/install" -DENABLE_UNIT_TESTS=ON -DCMAKE_PREFIX_PATH=/usr/local -DCMAKE_INSTALL_RPATH_USE_LINK_PATH=ON
# cd $BUILD_DIR_NAME
# cmake --build . --target install

View file

@ -0,0 +1,82 @@
###############################################################################
# Dockerfile used to make gitlab.linphone.org:4567/bc/public/flexisip/bc-dev-ubuntu-22-04:20240911_remove_protobuf
###############################################################################
FROM ubuntu:22.04
MAINTAINER Anthony Gauchy <anthony.gauchy@belledonne-communications.com>
# Update
RUN apt-get -y update \
# Install common tools
&& apt-get -y install sudo \
vim \
wget \
file \
# Install development tools
ccache \
clang \
cmake \
doxygen \
elfutils \
g++ \
gdb \
git \
llvm \
make \
ninja-build \
python3 \
python3-pip \
yasm \
# Install all dependencies needed for Flexisip
libssl-dev \
libboost-dev \
libboost-system-dev \
libboost-thread-dev \
libhiredis-dev \
libjansson-dev \
libjsoncpp-dev \
libsqlite3-dev \
libpq-dev \
libmariadb-dev \
libmariadb-dev-compat \
mariadb-server \
libnghttp2-dev \
libsnmp-dev \
libxerces-c-dev \
libsrtp2-dev \
libgsm1-dev \
libopus-dev \
libmbedtls-dev \
libspeex-dev \
libspeexdsp-dev \
libxml2-dev \
redis-server \
# Dependencies of the B2BUA
libvpx-dev \
# Clean
&& apt-get -y autoremove \
&& apt-get -y clean
# Install libnghttp2_asio 1.43.0
COPY libnghttp2_asio_install.sh .
RUN ./libnghttp2_asio_install.sh 1.43.0 && rm libnghttp2_asio_install.sh
# Configure user bc
RUN useradd -ms /bin/bash bc && \
echo 'bc:cotcot' | chpasswd && \
echo 'bc ALL=(ALL) NOPASSWD:ALL' >> /etc/sudoers
# Switch for 'bc' user
USER bc
WORKDIR /home/bc
# Install python3 modules.
# This must be done as 'bc' user because some python modules are installed into /usr/local/lib when pip is invoked
# as root, and rpmbuild prevent python from seaching its modules in this prefix. Using 'bc' user make the python
# modules to be installed into /home/bc/.local/bin.
RUN python3 -m pip install --user pystache six
ENV PATH=$PATH:/home/bc/.local/bin
# CVE-2022-24765, from git 2.35.2 onward
RUN git config --global --add safe.directory *

View file

@ -0,0 +1,77 @@
###############################################################################
# Dockerfile used to make gitlab.linphone.org:4567/bc/public/flexisip/bc-dev-ubuntu-24-04:20240911_remove_protobuf
###############################################################################
FROM ubuntu:24.04
RUN userdel -r ubuntu
MAINTAINER Anthony Gauchy <anthony.gauchy@belledonne-communications.com>
# Update
RUN apt-get -y update \
# Install common tools
&& apt-get -y install sudo \
vim \
wget \
file \
# Install development tools
ccache \
clang \
cmake \
doxygen \
elfutils \
g++ \
gdb \
git \
llvm \
make \
ninja-build \
python3 \
python3-pystache \
python3-six \
yasm \
# Install all dependencies needed for Flexisip
libssl-dev \
libboost-dev \
libboost-system-dev \
libboost-thread-dev \
libhiredis-dev \
libjansson-dev \
libjsoncpp-dev \
libsqlite3-dev \
libpq-dev \
libmariadb-dev \
libmariadb-dev-compat \
mariadb-server \
libnghttp2-dev \
libsnmp-dev \
libxerces-c-dev \
libsrtp2-dev \
libgsm1-dev \
libopus-dev \
libmbedtls-dev \
libspeex-dev \
libspeexdsp-dev \
libxml2-dev \
redis-server \
# Dependencies of the B2BUA
libvpx-dev \
# Clean
&& apt-get -y autoremove \
&& apt-get -y clean
# Install libnghttp2_asio 1.51.0
COPY libnghttp2_asio_install.sh .
RUN ./libnghttp2_asio_install.sh 1.51.0 && rm libnghttp2_asio_install.sh
# Configure user bc
RUN useradd -ms /bin/bash bc && \
echo 'bc:cotcot' | chpasswd && \
echo 'bc ALL=(ALL) NOPASSWD:ALL' >> /etc/sudoers
# Switch for 'bc' user
USER bc
WORKDIR /home/bc
# CVE-2022-24765, from git 2.35.2 onward
RUN git config --global --add safe.directory *

View file

@ -0,0 +1,30 @@
#!/usr/bin/env bash
# Copyright (C) 2010-2023 Belledonne Communications SARL
# SPDX-License-Identifier: AGPL-3.0-or-later
#
# Small helper script to build and push a Docker image used for CI.
# Should be called from within the docker/ folder, and takes only one argument: the Dockerfile to build.
# You should update the tag in the Dockerfile before running this script.
# Set {BUILD,RUN,PUSH} env vars to {en,dis}able the corresponding steps
#
# Example usage:
# BUILD=false RUN=true ./build-and-push-ci-image.sh bc-dev-ubuntu-22-04
set -euxo pipefail
DOCKERFILE=$1
BUILD=${BUILD:-true}
RUN=${RUN:-false}
PUSH=${PUSH:-false}
IMAGE_TAG=$(grep --only-matching --regexp="gitlab.linphone.org.*" $DOCKERFILE | head -1)
if $BUILD; then
docker build --pull --network=host -t $IMAGE_TAG -f $DOCKERFILE .
fi
if $RUN; then
docker run --rm -it -v $(pwd)/..:/home/bc/flexisip $IMAGE_TAG /bin/bash
fi
if $PUSH; then
docker push $IMAGE_TAG
fi

32
docker/cmake_install.sh Executable file
View file

@ -0,0 +1,32 @@
#!/bin/bash
script_name="$(basename $0)"
if [ $# -ne 1 ]
then
echo "usage: $script_name <cmake_version>" 1>&2
exit 2
fi
version="$1"
archive="cmake-${version}.tar.gz"
url="https://github.com/Kitware/CMake/releases/download/v${version}/${archive}"
workdir=$(realpath "$PWD/.tmp-$(date '+%Y%m%d-%H%M%S')")
srcdir="${workdir}/cmake-${version}"
bindir="${workdir}/build"
mkdir -p $bindir
pushd $workdir
wget $url \
&& tar xf $archive \
|| exit 1
cd $bindir \
&& cmake -G Ninja -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/usr/local $srcdir \
&& cmake --build . \
&& sudo cmake --build . --target install \
|| exit 1
popd
rm -rf $workdir

32
docker/flex-from-deb Normal file
View file

@ -0,0 +1,32 @@
FROM debian:9
MAINTAINER Jehan Monnier <jehan.monnier@linphone.org>
# Prepare dependencies
RUN apt-get update
RUN apt-get install -y xsdcxx gdb libmariadbclient18
# Get flexisip package
COPY DEBS/*.deb DEBS/*.ddeb deb-packages/
RUN apt-get install -y /deb-packages/*
RUN rm -rf /deb-packages
# Add it to the default path
ENV PATH=$PATH:/opt/belledonne-communications/bin
WORKDIR /opt/belledonne-communications
# Generate a default configuration
RUN flexisip --dump-default all > /etc/flexisip/flexisip.conf
VOLUME /etc/opt/belledonne-communications/flexisip
VOLUME /var/opt/belledonne-communications/log/flexisip
COPY flexisip-entrypoint.sh /
COPY backtrace.gdb /
RUN chmod a+x /flexisip-entrypoint.sh
# Script to wait db before launch flexisip [Licence Apache2]
ADD https://github.com/ufoscout/docker-compose-wait/releases/download/2.2.1/wait /wait
RUN chmod +x /wait
ENTRYPOINT ["/flexisip-entrypoint.sh"]
CMD flexisip

50
docker/flex-from-src Normal file
View file

@ -0,0 +1,50 @@
FROM gitlab.linphone.org:4567/bc/public/flexisip/bc-dev-debian10:20220708_add_jsoncpp
MAINTAINER Jehan Monnier <jehan.monnier@linphone.org>
ARG sanitizer=OFF
ARG build_type=Debug
# Some Python modules are available as 'bc' user only.
USER bc
WORKDIR /home/bc
RUN sudo apt-get install -y iptables
# Get source code
COPY --chown=bc:bc . flexisip/
# Configure & build
RUN cd flexisip \
&& rm -rf work && mkdir work \
&& cmake -S . -B work -G Ninja -DCMAKE_BUILD_TYPE=${build_type} -DENABLE_SANITIZERS=${sanitizer} -DCMAKE_INSTALL_PREFIX=/opt/belledonne-communications -DSYSCONF_INSTALL_DIR=/etc \
&& cmake --build work \
&& sudo cmake --build work --target install
# Cleanup
RUN rm -rf flexisip
# Switch to 'root' user
USER root
WORKDIR /root
# Add Flexisip bindir to the default path
ENV PATH=/opt/belledonne-communications/bin:$PATH
# Create volumes for Flexisip configuration and logs
RUN mkdir -p /var/opt/belledonne-communications/flexisip /etc/flexisip /home/cores
VOLUME /etc/flexisip
VOLUME /var/opt/belledonne-communications/log/flexisip
COPY docker/flexisip-entrypoint.sh /
COPY docker/backtrace.gdb /
RUN chmod a+x /flexisip-entrypoint.sh
# Script to wait db before launch flexisip [Licence Apache2]
ADD https://github.com/ufoscout/docker-compose-wait/releases/download/2.2.1/wait /wait
RUN chmod +x /wait
# Generate a default configuration
RUN flexisip --dump-default all > /etc/flexisip/flexisip.conf
ENTRYPOINT ["/flexisip-entrypoint.sh"]
CMD flexisip

View file

@ -0,0 +1,30 @@
#!/bin/bash
ulimit -c unlimited*
set -- flexisip "$@"
# Wait for needed container startup
/wait || exit $?
echo "Flexisip docker params : $*"
if [[ $@ == *"--server"* ]]; then
echo "--server param found, starting only one Flexisip instance"
"$@" &
else
echo "Server param not found, starting 3 Flexisip instances (proxy, presence, conference)"
( "$@" --server proxy 2>&1| tee /var/opt/belledonne-communications/log/flexisip/flexisip_proxy_stdout.log ) &
( "$@" --server presence 2>&1| tee /var/opt/belledonne-communications/log/flexisip/flexisip_presence_stdout.log ) &
( "$@" --server conference 2>&1| tee /var/opt/belledonne-communications/log/flexisip/flexisip_conference_stdout.log ) &
fi
wait -n
echo "At least one server crashed, stopping every sub process that is still alive"
pkill -P $$
# coredump management, used in unit tests
# we execute gdb on each coredump, with the options given in backtrace.gdb file
# we search in /root because it's the workdir set in Dockerfile and because by default our coredumps are generated by default in the working directory
if [ -n "$(find /root -type f -name "core*")" ]; then
find /root -type f -name "core*" | xargs -L1 gdb flexisip -x /backtrace.gdb;
fi

View file

@ -0,0 +1,22 @@
#!/bin/bash
script_name="$(basename $0)"
if [ $# -ne 1 ]
then
echo "usage: $script_name <libnghttp2_asio_version>" 1>&2
exit 2
fi
version="$1"
srcdir=nghttp2-${version}
srcpkg=${srcdir}.tar.gz
bindir=build_libnghttp2_asio
wget https://github.com/nghttp2/nghttp2/releases/download/v${version}/${srcpkg} && \
tar -xf ${srcpkg} && \
mkdir ${bindir} && cmake -S ${srcdir} -B ${bindir} -GNinja -DENABLE_ASIO_LIB=ON && \
cmake --build ${bindir} --target install && \
rm -rf ${srcpkg} ${srcdir} ${bindir}

82
docker/rpmmacros Normal file
View file

@ -0,0 +1,82 @@
#
# Macros for cmake
#
%_cmake_lib_suffix64 -DLIB_SUFFIX=64
%_cmake_skip_rpath -DCMAKE_SKIP_RPATH:BOOL=ON
%_cmake_version 3.2.3
%__cmake cmake
# - Set default compile flags
# - CMAKE_*_FLAGS_RELEASE are added *after* the *FLAGS environment variables
# and default to -O3 -DNDEBUG. Strip the -O3 so we can override with *FLAGS
# - Turn on verbose makefiles so we can see and verify compile flags
# - Set default install prefixes and library install directories
# - Turn on shared libraries by default
%cmake \
CFLAGS="${CFLAGS:-%optflags}" ; export CFLAGS ; \
CXXFLAGS="${CXXFLAGS:-%optflags}" ; export CXXFLAGS ; \
FFLAGS="${FFLAGS:-%optflags%{?_fmoddir: -I%_fmoddir}}" ; export FFLAGS ; \
FCFLAGS="${FCFLAGS:-%optflags%{?_fmoddir: -I%_fmoddir}}" ; export FCFLAGS ; \
%{?__global_ldflags:LDFLAGS="${LDFLAGS:-%__global_ldflags}" ; export LDFLAGS ;} \
%__cmake \\\
-DCMAKE_C_FLAGS_RELEASE:STRING="-DNDEBUG" \\\
-DCMAKE_CXX_FLAGS_RELEASE:STRING="-DNDEBUG" \\\
-DCMAKE_Fortran_FLAGS_RELEASE:STRING="-DNDEBUG" \\\
-DCMAKE_VERBOSE_MAKEFILE:BOOL=ON \\\
-DCMAKE_INSTALL_PREFIX:PATH=%{_prefix} \\\
-DINCLUDE_INSTALL_DIR:PATH=%{_includedir} \\\
-DLIB_INSTALL_DIR:PATH=%{_libdir} \\\
-DSYSCONF_INSTALL_DIR:PATH=%{_sysconfdir} \\\
-DSHARE_INSTALL_PREFIX:PATH=%{_datadir} \\\
%if "%{?_lib}" == "lib64" \
%{?_cmake_lib_suffix64} \\\
%endif \
-DBUILD_SHARED_LIBS:BOOL=ON
#
# Macros for systemd
#
%_unitdir /lib/systemd/system
%systemd_requires \
Requires(post): systemd \
Requires(preun): systemd \
Requires(postun): systemd \
%{nil}
%systemd_post() \
if [ $1 -eq 1 ] ; then \
# Initial installation \
systemctl preset %{?*} >/dev/null 2>&1 || : \
fi \
%{nil}
%systemd_preun() \
if [ $1 -eq 0 ] ; then \
# Package removal, not upgrade \
systemctl --no-reload disable %{?*} > /dev/null 2>&1 || : \
systemctl stop %{?*} > /dev/null 2>&1 || : \
fi \
%{nil}
%systemd_postun() \
systemctl daemon-reload >/dev/null 2>&1 || : \
%{nil}
%systemd_postun_with_restart() \
systemctl daemon-reload >/dev/null 2>&1 || : \
if [ $1 -ge 1 ] ; then \
# Package upgrade, not uninstall \
systemctl try-restart %{?*} >/dev/null 2>&1 || : \
fi \
%{nil}

25
flake.lock generated Normal file
View file

@ -0,0 +1,25 @@
{
"nodes": {
"nixpkgs": {
"locked": {
"lastModified": 1687886075,
"narHash": "sha256-PeayJDDDy+uw1Ats4moZnRdL1OFuZm1Tj+KiHlD67+o=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "a565059a348422af5af9026b5174dc5c0dcefdae",
"type": "github"
},
"original": {
"id": "nixpkgs",
"type": "indirect"
}
},
"root": {
"inputs": {
"nixpkgs": "nixpkgs"
}
}
},
"root": "root",
"version": 7
}

51
flake.nix Normal file
View file

@ -0,0 +1,51 @@
{
description = "A very basic flake";
outputs = { self, nixpkgs }: {
devShells.x86_64-linux.default = import ./shell.nix {
pkgs = nixpkgs.legacyPackages.x86_64-linux;
};
devShells.x86_64-linux.embedded-like = with nixpkgs.legacyPackages.x86_64-linux;
/* CC=gcc CXX=g++ BUILD_DIR_NAME="build" cmake -DCMAKE_EXPORT_COMPILE_COMMANDS=1 -S . -B ./$BUILD_DIR_NAME -G "Ninja" -DCMAKE_BUILD_TYPE=Debug -DCMAKE_INSTALL_PREFIX="$PWD/$BUILD_DIR_NAME/install"
-DENABLE_PRESENCE=ON
-DENABLE_REDIS=OFF
-DENABLE_SNMP=OFF
-DENABLE_SOCI=ON
-DENABLE_TRANSCODER=ON
-DENABLE_MDNS=OFF
-DENABLE_EXTERNAL_AUTH_PLUGIN=OFF
-DENABLE_JWE_AUTH_PLUGIN=OFF
-DINTERNAL_LIBSRTP2=ON
-DINTERNAL_JSONCPP=OFF
-DENABLE_CONFERENCE=OFF
-DENABLE_SOCI_POSTGRESQL_BACKEND=OFF
-DENABLE_B2BUA=OFF
-DENABLE_UNIT_TESTS=OFF
-DENABLE_STRICT_LINPHONESDK=OFF
*/
mkShell
{
buildInputs =
[
cmake
git
openssl
python3
perl
xercesc
nghttp2
sqlite
speex
libmysqlclient
msgpack
ninja # Optional. You can use "Unix makefiles" instead
];
};
packages.x86_64-linux.hello = nixpkgs.legacyPackages.x86_64-linux.hello;
defaultPackage.x86_64-linux = self.packages.x86_64-linux.hello;
};
}

122
flexisip_tester_script Executable file
View file

@ -0,0 +1,122 @@
#!/bin/bash
#Script to mimic Gitlab-CI configuration of flexisip-flexisip_tester
#execute it with sh -x [script] to see variables values and command launched
FLEXISIP_LOCATION=~/flexisip
#GCI script variables
REBUILD_FLEXISIP_IMAGE_FROM_RPM=false
REBUILD_FLEXISIP_AUXILIARIES=true
REBUILD_SDK=false
SDK_VERSION=4.3
SDK_BRANCH=release/$SDK_VERSION
PARALLEL_TEST_MODE=false
DOCKER_BUILD_OPTIONS="--no-cache --force-rm -t gitlab.linphone.org:4567/bc/public/flexisip/flexisip:latest -f docker/flex-from-src --build-arg=njobs=12"
liblinphone_tester="gitlab.linphone.org:4567/bc/public/linphone-sdk/liblinphone_tester:$SDK_VERSION"
sourcedir=$FLEXISIP_LOCATION/linphone-sdk-docker
workspace=$FLEXISIP_LOCATION/liblinphone_tester_workspace
#docker compose network is default one (it is docker_default ???)
docker_run_options="--volume=$FLEXISIP_LOCATION/liblinphone_tester_workspace:/home/bc/linphone-sdk-build/linphone-sdk/desktop/work --network=docker_default --device=/dev/snd --user 1000:29"
COMPOSE_PROJECT_NAME=$RANDOM
#new variables for flexisip_tester
export FLEXISIP_DOCKER_IMAGE="gitlab.linphone.org:4567/bc/public/flexisip/flexisip"
export FLEXISIP_DOCKER_TAG="latest"
#new variables for liblinphone_tester in docker-compose
export LIBLINPHONE_DOCKER_IMAGE="gitlab.linphone.org:4567/bc/public/linphone-sdk/liblinphone_tester"
export LIBLINPHONE_DOCKER_TAG=$SDK_VERSION
if [ -z $workspace ]; then
echo "Error, $workspace should be set. Aborting to avoid unwanted rm"
exit 1
fi
mkdir -p $workspace
sudo rm -rf $workspace
mkdir -p $workspace/{bin,ext,lime-server-apache}
ls $workspace
#workaround for logs writing
sudo chown apache:apache $workspace
sudo chmod -R 777 $workspace
if [ "$REBUILD_FLEXISIP_IMAGE_FROM_RPM" = "true" ]; then
docker build $DOCKER_BUILD_OPTIONS .
fi
#--no-cache --force-rm
if [ "$REBUILD_SDK" = "true" ]; then
docker build --no-cache --force-rm -t gitlab.linphone.org:4567/bc/public/linphone-sdk/liblinphone_tester:$SDK_VERSION --build-arg="branch=$SDK_BRANCH" --build-arg="njobs=8" -f "$sourcedir/docker-files/liblinphone-tester" $sourcedir/docker-files/
fi
#Handle multiple runs
docker_compose_options=''
#Overriding docker-compose.yaml values with docker-compose-standalone.yaml in the ways specified with docker docs (either OR or AND, depending on key)
for name in 'docker-compose.yaml' 'docker-compose-standalone.yaml'; do
docker_compose_options="$docker_compose_options -f $FLEXISIP_LOCATION/flexisip-tester/docker/$name"
done
#liblinphone_tester_options='--dns-hosts none --log-file liblinphone_tester.log --xml --verbose --show-account-manager-logs'
liblinphone_tester_options="--suite Register --test \"Simple register\" --dns-hosts none --log-file liblinphone_tester.log --xml --verbose --show-account-manager-logs"
if [ "$PARALLEL_TEST_MODE" = "true" ]; then
liblinphone_tester_options="$liblinphone_tester_options --parallel"
fi
export FLEXISIP_LOGS="$workspace"
cd $workspace
docker-compose $docker_compose_options down
if [ "$REBUILD_FLEXISIP_AUXILIARIES" = "true" ]; then
docker-compose $docker_compose_options build
fi
# --exit-code-from liblinphone_tester
docker-compose $docker_compose_options up --exit-code-from liblinphone_tester |& tee logs_all | grep 'liblinphone_tester_1' || EXIT=$?
grep 'file-transfer-server_1' logs_all > file-transfer-server.log
grep 'lime-server_1' logs_all > lime-server_stdout.log
grep 'dbserver_1' logs_all > dbserver_stdout.log
grep 'account-manager_1' logs_all > account-manager_stdout.log
grep 'http-proxy_1' logs_all > http-proxy_stdout.log
grep 'redis-server_1' logs_all > redis-server_stdout.log
#docker-compose $docker_compose_options exec liblinphone_tester bash
#sleep 60
#docker run $(echo $docker_run_options) $liblinphone_tester $liblinphone_tester_options || EXIT=$? && echo 'Tests have failed'
docker-compose $docker_compose_options stop
#after script (coredump management)
# we specify commands to launch for each coredump of liblinphone_tester
echo "set debug-file-directory ../lib64" | tee gdb_options
echo "thread apply all bt" | tee -a gdb_options
# searching for core files and if there are some, launch gdb on all of it
# xargs -L1 means that the command in argument will be executed for each
# line (core dump) found in find output
# The docker syntax is error proning : to override the entrypoint with
# args, we enter the entrypoint first, then the name of the image, then the
# args to the entrypoint command.
# "true ||" is used here to continue the script even if the find fails
if [[ -n $(find . -type f -name "core*") ]]; then find . -type f -name "core*" | xargs -L1 docker run $(echo $docker_run_options) --entrypoint gdb $liblinphone_tester ../bin/liblinphone_tester -x gdb_options; fi || true
echo $EXIT

41
include/CMakeLists.txt Normal file
View file

@ -0,0 +1,41 @@
############################################################################
# CMakeLists.txt
# Copyright (C) 2019 Belledonne Communications, Grenoble France
#
############################################################################
#
# 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, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
############################################################################
find_package(Git)
set(gitversion_script "${CMAKE_CURRENT_SOURCE_DIR}/gitversion.cmake")
set(workdir "${CMAKE_CURRENT_SOURCE_DIR}")
set(outputdir "${CMAKE_CURRENT_BINARY_DIR}/flexisip")
set(flexisip_version_h "${outputdir}/flexisip-version.h")
add_custom_target(flexisip-git-version ALL
COMMAND ${CMAKE_COMMAND} -DGIT_EXECUTABLE=${GIT_EXECUTABLE} -DFLEXISIP_VERSION=${FLEXISIP_VERSION}
-DWORK_DIR=${workdir} -DOUTPUT_DIR=${outputdir} -P "${gitversion_script}"
BYPRODUCTS "${flexisip_version_h}"
)
install(DIRECTORY flexisip DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
install(FILES "${flexisip_version_h}" DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/flexisip")
if(HAS_GCC_BUG_105562)
set_source_files_properties(flexisip/expressionparser-impl.cc PROPERTIES COMPILE_OPTIONS "-Wno-error=maybe-uninitialized")
endif()

View file

@ -0,0 +1,77 @@
/*
Flexisip, a flexible SIP proxy server with media capabilities.
Copyright (C) 2010-2022 Belledonne Communications SARL, All rights reserved.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero 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 Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <string>
#include <vector>
#include <sofia-sip/auth_digest.h>
#include <sofia-sip/auth_module.h>
#include <sofia-sip/msg_types.h>
#include <sofia-sip/su_wait.h>
#include "flexisip/auth/flexisip-auth-status.hh"
#include "flexisip/auth/nonce-store.hh"
#include "flexisip/sofia-wrapper/auth-module.hh"
namespace flexisip {
/**
* @brief Base class for all authentication modules used by Flexisip.
*
* This implementation of AuthModule allows to do HTTP-like authentication
* of SIP requests as described in RFC 3261 §22.
*/
class FlexisipAuthModuleBase : public AuthModule {
public:
/**
* @brief Instantiate a new authentication module without QOP authentication feature.
* @param[in] root Event loop which the module will be working on.
* @param[in] domain The domain name which the module is in charge of.
* @param[in] nonceExpire Validity period for a nonce in seconds.
* @param[in] qopAuth Setting true allows clients to use the same nonce for successive authentication.
*/
FlexisipAuthModuleBase(su_root_t *root, const std::string &domain, int nonceExpire, bool qopAuth);
~FlexisipAuthModuleBase() override = default;
NonceStore &nonceStore() {return mNonceStore;}
protected:
void onCheck(AuthStatus &as, msg_auth_t *credentials, auth_challenger_t const *ach) override;
void onChallenge(AuthStatus &as, auth_challenger_t const *ach) override;
void onCancel(AuthStatus &as) override;
/**
* This method is called each time the module want to authenticate an Authorization header.
* The result of the authentication must be store in 'status' attribute of 'as' parameter as
* described in documentation of auth_mod_verify() function.
*
* @param[in,out] as The context on the authentication. It is also used to return the result.
* @param[in] credentials The authorization header to validate.
*/
virtual void checkAuthHeader(FlexisipAuthStatus &as, msg_auth_t *credentials, auth_challenger_t const *ach) = 0;
void notify(FlexisipAuthStatus &as);
void onError(FlexisipAuthStatus &as);
NonceStore mNonceStore;
bool mQOPAuth = false;
};
}

View file

@ -0,0 +1,79 @@
/*
Flexisip, a flexible SIP proxy server with media capabilities.
Copyright (C) 2010-2022 Belledonne Communications SARL, All rights reserved.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero 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 Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <list>
#include <memory>
#include <string>
#include "flexisip/event.hh"
#include "flexisip/sofia-wrapper/auth-status.hh"
namespace flexisip {
/**
* Specialization of AuthStatus dedicated to be used
* with FlexisipAuthModule class.
*/
class FlexisipAuthStatus : public AuthStatus {
public:
FlexisipAuthStatus(const std::shared_ptr<RequestSipEvent>& ev) : AuthStatus(), mEvent(ev) {
}
/**
* Request that has been used while construction.
*/
const std::shared_ptr<RequestSipEvent> &event() const {return mEvent;}
/**
* This property is to be set by the user of FlexisipAuthModule
* before calling verify(). If true, the module will not return 403
* status code on authentication denied but will submit a new challenge.
*/
bool no403() const {return mNo403;}
void no403(bool no403) {mNo403 = no403;}
/**
* This property is set by FlexisipAuthModule and can
* be read on each time while the authentication is running.
* A 'true' value means that the module has already tried to fetch
* the password from database and has succeeded.
*/
bool passwordFound() const {return mPasswordFound;}
void passwordFound(bool val) {mPasswordFound = val;}
/**
* List of digest algorithms to use for authentication. If there
* are several algorithms, FlexisipAuthModule will generate
* one challenge per algorithm when the Authorization header is missing
* from the request.
*
* This property must be set before calling verify() and must
* contain one element at least.
*/
std::list<std::string> &usedAlgo() {return mAlgoUsed;}
private:
std::shared_ptr<RequestSipEvent> mEvent;
std::list<std::string> mAlgoUsed;
bool mNo403 = false;
bool mPasswordFound = false;
};
}

View file

@ -0,0 +1,53 @@
/*
* Flexisip, a flexible SIP proxy server with media capabilities.
* Copyright (C) 2018 Belledonne Communications SARL, All rights reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <ctime>
#include <map>
#include <mutex>
#include <string>
#include <sofia-sip/msg_types.h>
namespace flexisip {
class NonceStore {
public:
void setNonceExpires(int value) {mNonceExpires = value;}
int getNc(const std::string &nonce);
void insert(const msg_auth_t *response);
void insert(const std::string &nonce);
void updateNc(const std::string &nonce, int newnc);
void erase(const std::string &nonce);
void cleanExpired();
private:
struct NonceCount {
NonceCount(int c, time_t ex) : nc(c), expires(ex) {
}
int nc;
std::time_t expires;
};
std::map<std::string, NonceCount> mNc;
std::mutex mMutex;
int mNonceExpires = 3600;
};
}

119
include/flexisip/common.hh Normal file
View file

@ -0,0 +1,119 @@
/*
Flexisip, a flexible SIP proxy server with media capabilities.
Copyright (C) 2010-2022 Belledonne Communications SARL, All rights reserved.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero 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 Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <sys/timeb.h>
#include <cstdarg>
#include <cstdlib>
#include <map>
#include <string>
#include <vector>
#include "flexisip/logmanager.hh"
#ifndef MAX
#define MAX(a, b) (a) > (b) ? (a) : (b)
#endif
time_t getCurrentTime();
time_t getTimeOffset(time_t current_time);
namespace flexisip {
class Mutex {
public:
Mutex(bool reentrant = false);
void lock();
void unlock();
~Mutex();
private:
const bool mReentrant;
pthread_t mThread;
int mCount;
pthread_mutex_t mMutex;
pthread_mutex_t mInternalMutex;
};
template <typename _type>
class delete_functor {
public:
void operator()(_type* obj) {
delete obj;
}
};
template <typename _first, typename _last>
class map_delete_functor {
public:
void operator()(std::pair<_first, _last> obj) {
delete obj.second;
}
};
#define RESTART_EXIT_CODE 5
// Helper to get ip from host in a portable binary format.
// It has comparison functions, which makes it suitable to use in std::set or std::map, for fast search.
class BinaryIp {
public:
/* Adds a hostname in the form of BinaryIp into a generic STL container.
If numericOnly is set to true, then no DNS lookup will be made. As a result fully qualified domain names will be
ignored.*/
template <typename _containerT>
static _containerT& emplace(_containerT& container, const std::string& hostname, bool numericOnly = false) {
struct addrinfo* ai = resolve(hostname, numericOnly);
struct addrinfo* ai_it;
for (ai_it = ai; ai_it != nullptr; ai_it = ai_it->ai_next) {
container.emplace(ai_it);
}
freeaddrinfo(ai);
return container;
}
/* Builds a BinaryIp from a struct addrinfo containing an AF_INET6 address only !.
* Do not use directly, use static emplace() method to build BinarIps.*/
BinaryIp(const struct addrinfo* ai);
BinaryIp(const char* ip);
bool operator==(const BinaryIp& ip2) const {
return memcmp(&mAddr, &ip2.mAddr, sizeof mAddr) == 0;
}
bool operator<(const BinaryIp& ip2) const {
return memcmp(&mAddr, &ip2.mAddr, sizeof mAddr) < 0;
}
bool operator<=(const BinaryIp& ip2) const {
return memcmp(&mAddr, &ip2.mAddr, sizeof mAddr) <= 0;
}
bool operator>(const BinaryIp& ip2) const {
return memcmp(&mAddr, &ip2.mAddr, sizeof mAddr) > 0;
}
bool operator>=(const BinaryIp& ip2) const {
return memcmp(&mAddr, &ip2.mAddr, sizeof mAddr) >= 0;
}
// turn hummanely readable IP. This function is not optimized for speed.
std::string asString() const;
private:
static struct addrinfo* resolve(const std::string& hostname, bool numericOnly);
struct in6_addr mAddr;
};
std::ostream& operator<<(std::ostream& os, const flexisip::BinaryIp& ip);
} // namespace flexisip

View file

@ -0,0 +1,827 @@
/*
Flexisip, a flexible SIP proxy server with media capabilities.
Copyright (C) 2010-2024 Belledonne Communications SARL, All rights reserved.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero 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 Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <algorithm>
#include <chrono>
#include <cstdlib>
#include <cxxabi.h>
#include <functional>
#include <iostream>
#include <list>
#include <memory>
#include <queue>
#include <regex>
#include <sstream>
#include <stdexcept>
#include <string>
#include <tuple>
#include <typeinfo>
#include <unordered_set>
#include <vector>
// WARNING: keep flexisip-config.h included before any other
// Flexisip includes.
// WARNING2: keep flexisip-config.h included before 'net-snmp'
// header section because ENABLE_SNMP is defined in this header.
#if defined(HAVE_CONFIG_H) && !defined(FLEXISIP_INCLUDED)
#include "flexisip-config.h"
#define FLEXISIP_INCLUDED
#endif
#ifdef ENABLE_SNMP
// SNMP headers are sensitive to the inclusion order.
// clang-format off
#include <net-snmp/net-snmp-config.h>
#include <net-snmp/net-snmp-includes.h>
#include <net-snmp/agent/net-snmp-agent-includes.h>
// clang-format on
#else
typedef unsigned long oid;
#endif /* ENABLE_SNMP */
#include "flexisip/common.hh"
#include "flexisip/flexisip-exception.hh"
#include "flexisip/global.hh"
#include "flexisip/sip-boolean-expressions.hh"
typedef struct sip_s sip_t;
namespace flexisip {
class LpConfig;
enum class ConfigState { Check, Changed, Reset, Committed };
class ConfigValue;
class ConfigValueListener {
public:
ConfigValueListener() = default;
virtual ~ConfigValueListener() = default;
virtual bool doOnConfigStateChanged(const ConfigValue& conf, ConfigState state) = 0;
private:
// FLEXISIP_DISABLE_COPY(ConfigValueListener);
};
enum GenericValueType {
Boolean,
Integer,
IntegerRange,
Counter64,
String,
ByteSize,
StringList,
Struct,
BooleanExpr,
Notification,
RuntimeError,
DurationMS,
DurationS,
DurationMIN
};
/* Allows to have a string for each GenericValueType */
static const std::map<GenericValueType, std::string> GenericValueTypeNameMap = {
#define TypeToName(X) \
{ X, #X }
TypeToName(Boolean), TypeToName(Integer), TypeToName(IntegerRange), TypeToName(Counter64),
TypeToName(String), TypeToName(ByteSize), TypeToName(StringList), TypeToName(Struct),
TypeToName(BooleanExpr), TypeToName(Notification), TypeToName(RuntimeError), TypeToName(DurationMS),
TypeToName(DurationS), TypeToName(DurationMIN)
#undef TypeToName
};
struct ConfigItemDescriptor {
GenericValueType type;
const char* name;
const char* help;
const char* default_value;
};
static const ConfigItemDescriptor config_item_end = {Boolean, NULL, NULL, NULL};
struct StatItemDescriptor {
GenericValueType type;
const char* name;
const char* help;
};
static const StatItemDescriptor stat_item_end = {Boolean, NULL, NULL};
class Oid {
friend class GenericEntry;
friend class StatCounter64;
friend class ConfigValue;
friend class GenericStruct;
friend class RootConfigStruct;
friend class NotificationEntry;
protected:
Oid(Oid& parent, oid leaf);
Oid(std::vector<oid> path);
Oid(std::vector<oid> path, oid leaf);
std::vector<oid>& getValue() {
return mOidPath;
}
virtual ~Oid() = default;
private:
std::vector<oid> mOidPath;
public:
std::string getValueAsString() const {
std::ostringstream oss(std::ostringstream::out);
for (oid i = 0; i < mOidPath.size(); ++i) {
if (i != 0) oss << ".";
oss << mOidPath[i];
}
return oss.str();
}
oid getLeaf() {
return mOidPath[mOidPath.size() - 1];
}
static oid oidFromHashedString(const std::string& str);
};
class GenericEntry {
public:
class DeprecationInfo {
public:
DeprecationInfo() = default;
DeprecationInfo(const std::string& date, const std::string& version, const std::string& text = "") {
setAsDeprecated(date, version, text);
}
bool isDeprecated() const {
return !mDate.empty();
}
void setAsDeprecated(const std::string& date, const std::string& version, const std::string& text = "");
const std::string& getDate() const {
return mDate;
}
const std::string& getVersion() const {
return mVersion;
}
const std::string& getText() const {
return mText;
}
void setText(const std::string& text) {
mText = text;
}
private:
std::string mDate;
std::string mVersion;
std::string mText;
};
static std::string sanitize(const std::string& str);
const std::string& getName() const {
return mName;
}
std::string getCompleteName() const;
std::string getPrettyName() const;
GenericValueType getType() const {
return mType;
}
const std::string& getTypeName() const {
if (GenericValueTypeNameMap.count(mType) == 1) return GenericValueTypeNameMap.at(mType);
else return GenericValueTypeNameMap.at(Integer);
}
const std::string& getHelp() const {
return mHelp;
}
GenericEntry* getParent() const {
return mParent;
}
virtual ~GenericEntry() {
if (mOid) delete mOid;
}
virtual void setParent(GenericEntry* parent);
/*
* @returns entry oid built from parent & object oid index
*/
Oid& getOid() {
return *mOid;
}
std::string getOidAsString() const {
return mOid->getValueAsString();
}
void setErrorMessage(const std::string& msg) {
mErrorMessage = msg;
}
const std::string& getErrorMessage() const {
return mErrorMessage;
}
void setReadOnly(bool ro) {
mReadOnly = ro;
}
bool isExportable() const {
return mExportToConfigFile;
}
void setExportable(bool val) {
mExportToConfigFile = val;
}
#ifdef ENABLE_SNMP
static int sHandleSnmpRequest(netsnmp_mib_handler* handler,
netsnmp_handler_registration* reginfo,
netsnmp_agent_request_info* reqinfo,
netsnmp_request_info* requests);
virtual int handleSnmpRequest(netsnmp_mib_handler*,
netsnmp_handler_registration*,
netsnmp_agent_request_info*,
netsnmp_request_info*) {
return -1;
};
#endif
virtual void mibFragment(std::ostream& ostr, std::string spacing) const = 0;
void setConfigListener(ConfigValueListener* listener) {
mConfigListener = listener;
}
ConfigValueListener* getConfigListener() const {
return mConfigListener;
}
bool onConfigStateChanged(const ConfigValue& conf, ConfigState state);
void setDeprecated(const DeprecationInfo& info) {
mDeprecationInfo = info;
}
void setDeprecated(const std::string& aDate, const std::string& aVersion, const std::string& aText = "") {
mDeprecationInfo.setAsDeprecated(aDate, aVersion, aText);
}
bool isDeprecated() const {
return mDeprecationInfo.isDeprecated();
}
const DeprecationInfo& getDeprecationInfo() const {
return mDeprecationInfo;
}
DeprecationInfo& getDeprecationInfo() {
return mDeprecationInfo;
}
protected:
virtual void doMibFragment(std::ostream& ostr,
const std::string& def,
const std::string& access,
const std::string& syntax,
const std::string& spacing) const;
GenericEntry(const std::string& name, GenericValueType type, const std::string& help, oid oid_index = 0);
static std::string escapeDoubleQuotes(const std::string& str);
Oid* mOid = nullptr;
const std::string mName;
bool mReadOnly = false;
bool mExportToConfigFile = true;
DeprecationInfo mDeprecationInfo;
std::string mErrorMessage;
private:
std::string mHelp;
GenericValueType mType;
GenericEntry* mParent = nullptr;
ConfigValueListener* mConfigListener = nullptr;
oid mOidLeaf = 0;
};
inline std::ostream& operator<<(std::ostream& ostr, const GenericEntry& entry) {
return ostr << entry.getName();
}
class ConfigValue;
class StatCounter64;
struct StatPair;
class GenericStruct : public GenericEntry {
public:
GenericStruct(const std::string& name, const std::string& help, oid oid_index);
template <typename T>
T* addChild(std::unique_ptr<T>&& newEntry) {
auto newEntryPointer = newEntry.get();
newEntryPointer->setParent(this);
for (auto& entry : mEntries) {
if (entry->getName() == newEntry->getName()) {
throw std::runtime_error(std::string("Duplicate entry key: ") + entry->getName());
}
}
mEntries.push_back(std::move(newEntry));
return newEntryPointer;
}
StatCounter64* createStat(const std::string& name, const std::string& help);
void createStatPair(const std::string& name, const std::string& help);
StatCounter64* getStat(const std::string& name);
std::pair<StatCounter64*, StatCounter64*> getStatPair(const std::string& name);
std::unique_ptr<StatPair> getStatPairPtr(const std::string& name);
void addChildrenValues(ConfigItemDescriptor* items);
void addChildrenValues(ConfigItemDescriptor* items, bool hashed);
void deprecateChild(const std::string& name, const DeprecationInfo& info);
const std::list<std::unique_ptr<GenericEntry>>& getChildren() const;
template <typename _retType, typename StrT>
_retType* get(StrT&& name) const;
template <typename _retType>
_retType* getDeep(const std::string& name, bool strict) const;
template <typename Str>
GenericEntry* find(Str&& name) const {
auto it = find_if(mEntries.cbegin(), mEntries.cend(), [&name](const auto& e) { return e->getName() == name; });
return it != mEntries.cend() ? it->get() : nullptr;
}
GenericEntry* findApproximate(const std::string& name) const;
void mibFragment(std::ostream& ost, std::string spacing) const override;
void setParent(GenericEntry* parent) override;
private:
std::list<std::unique_ptr<GenericEntry>> mEntries;
};
class RootConfigStruct : public GenericStruct {
public:
RootConfigStruct(const std::string& name,
const std::string& help,
std::vector<oid> oid_root_prefix,
const std::string& configFile);
~RootConfigStruct() override;
const std::string& getConfigFile() const {
return mConfigFile;
}
void setCommittedChange(bool committedChange) {
mCommittedChange = committedChange;
}
bool hasCommittedChange() const {
return mCommittedChange;
}
private:
const std::string& mConfigFile; // keep a const ref to ConfigManager file
bool mCommittedChange{true};
};
class StatCounter64 : public GenericEntry {
public:
StatCounter64(const std::string& name, const std::string& help, oid oid_index);
#ifdef ENABLE_SNMP
int handleSnmpRequest(netsnmp_mib_handler*,
netsnmp_handler_registration*,
netsnmp_agent_request_info*,
netsnmp_request_info*) override;
#endif
void mibFragment(std::ostream& ost, std::string spacing) const override;
void setParent(GenericEntry* parent) override;
uint64_t read() {
return mValue;
}
void set(uint64_t val) {
mValue = val;
}
void operator++() {
++mValue;
}
void operator++(int) {
mValue++;
}
void operator--() {
--mValue;
}
void operator--(int) {
mValue--;
}
inline void incr() {
mValue++;
}
private:
uint64_t mValue;
};
struct StatPair {
StatCounter64* const start;
StatCounter64* const finish;
StatPair(StatCounter64* istart, StatCounter64* ifinish) : start(istart), finish(ifinish) {
}
inline void incrStart() {
start->incr();
}
inline void incrFinish() {
finish->incr();
}
};
class StatFinishListener {
std::unordered_set<StatCounter64*> mStatList;
public:
void addStatCounter(StatCounter64* stat) {
mStatList.insert(stat);
}
~StatFinishListener() {
for (auto it = mStatList.begin(); it != mStatList.end(); ++it) {
StatCounter64& s = **it;
++s;
}
}
};
class ConfigValue : public GenericEntry {
public:
ConfigValue(const std::string& name,
GenericValueType vt,
const std::string& help,
const std::string& default_value,
oid oid_index);
/* Set the value and mark it as 'not default' */
void set(const std::string& value);
virtual const std::string& get() const;
/* Restore the default value and mark the value as 'default'. */
void restoreDefault();
/*
* Set the default value i.e. the value which will be restored by reset(). If the value is
* marked as 'default', it will be automatically updated to the new default value.
*/
void setDefault(const std::string& value);
const std::string& getDefault() const;
virtual std::string_view getDefaultUnit() const;
void setFallback(const ConfigValue& fallbackValue);
/* Check whether the value is mark as 'default' */
bool isDefault() const {
return mIsDefault;
}
void setNextValue(const std::string& value);
const std::string& getNextValue() const {
return mNextValue;
}
void setNotifPayload(bool b) {
mNotifPayload = b;
}
#ifdef ENABLE_SNMP
int handleSnmpRequest(netsnmp_mib_handler*,
netsnmp_handler_registration*,
netsnmp_agent_request_info*,
netsnmp_request_info*) override;
#endif
void doConfigMibFragment(std::ostream& ostr, const std::string& syntax, const std::string& spacing) const {
doMibFragment(ostr, "", "", syntax, spacing);
}
void setParent(GenericEntry* parent) override;
void mibFragment(std::ostream& ost, std::string spacing) const override;
void doMibFragment(std::ostream& ostr,
const std::string& def,
const std::string& access,
const std::string& syntax,
const std::string& spacing) const override;
protected:
bool invokeConfigStateChanged(ConfigState state);
void checkType(const std::string& value, bool isDefault);
std::string mValue;
std::string mNextValue;
std::string mDefaultValue;
const ConfigValue* mFallback = nullptr;
bool mIsDefault = true;
bool mNotifPayload = false;
};
class ConfigBoolean : public ConfigValue {
public:
static bool parse(const std::string& value);
ConfigBoolean(const std::string& name, const std::string& help, const std::string& default_value, oid oid_index);
bool read() const;
bool readNext() const;
void write(bool value);
#ifdef ENABLE_SNMP
int handleSnmpRequest(netsnmp_mib_handler*,
netsnmp_handler_registration*,
netsnmp_agent_request_info*,
netsnmp_request_info*) override;
#endif
void mibFragment(std::ostream& ost, std::string spacing) const override;
};
class ConfigInt : public ConfigValue {
public:
#ifdef ENABLE_SNMP
int handleSnmpRequest(netsnmp_mib_handler*,
netsnmp_handler_registration*,
netsnmp_agent_request_info*,
netsnmp_request_info*) override;
#endif
ConfigInt(const std::string& name, const std::string& help, const std::string& default_value, oid oid_index);
void mibFragment(std::ostream& ost, std::string spacing) const override;
int read() const;
int readNext() const;
void write(int value);
};
class ConfigIntRange : public ConfigValue {
public:
ConfigIntRange(const std::string& name, const std::string& help, const std::string& default_value, oid oid_index);
int readMin();
int readMax();
int readNextMin();
int readNextMax();
void write(int min, int max);
private:
void parse(const std::string& value);
int mMin{};
int mMax{};
};
template <typename DurationType>
struct DurationInfo {};
template <>
struct DurationInfo<std::chrono::milliseconds> {
static constexpr GenericValueType kValueType = DurationMS;
static constexpr const char* kUnit = "millisecond";
};
template <>
struct DurationInfo<std::chrono::seconds> {
static constexpr GenericValueType kValueType = DurationS;
static constexpr const char* kUnit = "second";
};
template <>
struct DurationInfo<std::chrono::minutes> {
static constexpr GenericValueType kValueType = DurationMIN;
static constexpr const char* kUnit = "minute";
};
template <typename DurationType>
class ConfigDuration : public ConfigValue {
public:
ConfigDuration(const std::string& name, const std::string& help, const std::string& default_value, oid oid_index)
: ConfigValue(name, DurationInfo<DurationType>::kValueType, help, default_value, oid_index) {
}
std::string_view getDefaultUnit() const override {
return DurationInfo<DurationType>::kUnit;
}
std::chrono::milliseconds read() const {
using namespace std::chrono_literals;
static const std::map<std::string, std::chrono::milliseconds> kMapping = {
{"ms", 1ms},
{"s", 1000ms},
{"min", 60000ms},
{"h", 3600 * 1000ms},
{"d", 24 * 3600 * 1000ms},
{"m", static_cast<long>(30.436875 * 24 * 3600) * 1000ms},
{"y", static_cast<long>(365.2425 * 24 * 3600) * 1000ms}};
const auto [value, unit] = parse();
auto unitIterator = kMapping.find(unit);
// If not found, it may be intentional (use default unit) or for backward compatibility.
if (unitIterator == kMapping.end()) {
return std::chrono::duration_cast<std::chrono::milliseconds>(DurationType{value});
}
if (unit == "ms" and getDefaultUnit() == "second") {
throw std::runtime_error("(" + getCompleteName() + ") duration precision (\"" + unit +
R"(") is too high, "second" is maximum precision for this parameter)");
}
if ((unit == "ms" or unit == "s") and getDefaultUnit() == "minute") {
throw std::runtime_error("(" + getCompleteName() + ") duration precision (\"" + unit +
R"(") is too high, "minute" is maximum precision for this parameter)");
}
return unitIterator->second * value;
}
private:
std::pair<long, std::string> parse() const {
std::smatch matchResult{};
if (!std::regex_match(get(), matchResult, std::regex("([0-9]+)(|ms|s|min|h|d|m|y)"))) {
throw std::runtime_error(
"(" + getCompleteName() + ") duration value is ill-formed (parameter = \"" + get() +
"\"). Please use the following syntax: <value>[ms|s|min|h|d|m|y] (example: 250ms).");
}
return {std::stol(matchResult[1]), matchResult[2]};
}
};
class ConfigRuntimeError : public ConfigValue {
mutable std::string mErrorStr;
public:
ConfigRuntimeError(const std::string& name, const std::string& help, oid oid_index);
std::string generateErrors() const;
#ifdef ENABLE_SNMP
int handleSnmpRequest(netsnmp_mib_handler*,
netsnmp_handler_registration*,
netsnmp_agent_request_info*,
netsnmp_request_info*) override;
#endif
void writeErrors(const GenericEntry* entry, std::ostringstream& oss) const;
};
class ConfigString : public ConfigValue {
public:
ConfigString(const std::string& name, const std::string& help, const std::string& default_value, oid oid_index);
~ConfigString();
const std::string& read() const;
};
class ConfigByteSize : public ConfigValue {
public:
using ValueType = std::uint64_t;
ConfigByteSize(const std::string& name, const std::string& help, const std::string& default_value, oid oid_index);
ValueType read() const;
};
class ConfigStringList : public ConfigValue {
public:
ConfigStringList(const std::string& name, const std::string& help, const std::string& default_value, oid oid_index);
std::list<std::string> read() const;
bool contains(const std::string& ref) const;
static std::list<std::string> parse(const std::string& in);
private:
};
class ConfigBooleanExpression : public ConfigValue {
public:
ConfigBooleanExpression(const std::string& name,
const std::string& help,
const std::string& default_value,
oid oid_index);
std::shared_ptr<SipBooleanExpression> read() const;
};
template <typename _retType, typename StrT>
_retType* GenericStruct::get(StrT&& name) const {
GenericEntry* e = find(name);
if (e == NULL) {
std::ostringstream err{};
err << "No ConfigEntry with name [" << name << "] in struct [" << getName() << "]";
LOGA("%s", err.str().c_str());
}
_retType* ret = dynamic_cast<_retType*>(e);
if (ret == NULL) {
int status;
std::string type_name = abi::__cxa_demangle(typeid(_retType).name(), 0, 0, &status);
std::ostringstream err{};
err << "Config entry [" << name << "] in struct [" << e->getParent()->getName()
<< "] does not have the expected type '" << type_name << "'.";
LOGA("%s", err.str().c_str());
}
return ret;
};
template <typename _retType>
_retType* GenericStruct::getDeep(const std::string& name, bool strict) const {
size_t len = name.length();
size_t next, prev = 0;
const GenericStruct *next_node, *prev_node = this;
while (std::string::npos != (next = name.find('/', prev))) {
std::string next_node_name = name.substr(prev, next - prev);
GenericEntry* e = find(next_node_name.c_str());
if (!e) {
if (!strict) return NULL;
LOGE("No ConfigEntry with name [%s] in struct [%s]", name.c_str(), prev_node->getName().c_str());
for (auto it = prev_node->mEntries.begin(); it != prev_node->mEntries.end(); ++it) {
LOGE("-> %s", (*it)->getName().c_str());
}
LOGF("end");
return NULL;
}
next_node = dynamic_cast<GenericStruct*>(e);
if (!next_node) {
LOGA("Config entry [%s] in struct [%s] does not have the expected type", e->getName().c_str(),
e->getParent()->getName().c_str());
return NULL;
}
prev_node = next_node;
prev = next + 1;
}
std::string leaf(name.substr(prev, len - prev));
return prev_node->get<_retType>(leaf.c_str());
};
class FileConfigReader {
public:
FileConfigReader(GenericStruct* root);
int read(const std::string& filename);
int reload();
void checkUnread();
~FileConfigReader();
private:
int read2(GenericEntry* entry, int level);
GenericStruct* mRoot;
std::unique_ptr<flexisip::LpConfig> mCfg;
std::string mFilename;
bool mHaveUnreads;
};
class NotificationEntry : public GenericEntry {
Oid& getStringOid();
bool mInitialized;
std::queue<std::tuple<const GenericEntry*, std::string>> mPendingTraps;
public:
NotificationEntry(const std::string& name, const std::string& help, oid oid_index);
virtual void mibFragment(std::ostream& ost, std::string spacing) const;
void send(const std::string& msg);
void send(const GenericEntry* source, const std::string& msg);
void setInitialized(bool status);
};
class ConfigManager : protected ConfigValueListener {
friend class ConfigArea;
public:
// Statically register add section functions
static std::vector<std::function<void(GenericStruct&)>>& defaultInit();
ConfigManager();
int load(const std::string& configFile);
const GenericStruct* getRoot() const;
GenericStruct* getRoot();
const std::string& getConfigFile() const {
return mConfigFile;
}
void setOverrideMap(const std::map<std::string, std::string>& overrides) {
mOverrides = overrides;
}
void setOverrideMap(std::map<std::string, std::string>&& overrides) {
mOverrides = std::move(overrides);
}
std::map<std::string, std::string>& getOverrideMap() {
return mOverrides;
}
const GenericStruct* getGlobal() const;
StatCounter64& findStat(const std::string& key);
void addStat(const std::string& key, StatCounter64& stat);
NotificationEntry* getSnmpNotifier() {
return mNotifier;
}
void sendTrap(const GenericEntry* source, const std::string& msg) {
mNotifier->send(source, msg);
}
void sendTrap(const std::string& msg) {
mNotifier->send(&mConfigRoot, msg);
}
void applyOverrides(bool strict);
bool mNeedRestart = false;
bool mDirtyConfig = false;
private:
bool doIsValidNextConfig(const ConfigValue& cv);
bool doOnConfigStateChanged(const ConfigValue& conf, ConfigState state) override;
std::string mConfigFile;
RootConfigStruct mConfigRoot;
FileConfigReader mReader;
std::map<std::string, std::string> mOverrides;
std::map<std::string, StatCounter64*> mStatMap;
std::unordered_set<std::string> mStatOids;
NotificationEntry* mNotifier = nullptr;
};
} // namespace flexisip

View file

@ -0,0 +1,79 @@
/*
Flexisip, a flexible SIP proxy server with media capabilities.
Copyright (C) 2010-2024 Belledonne Communications SARL, All rights reserved.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero 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 Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <set>
#include <unordered_map>
#include "flexisip/module.hh"
namespace flexisip {
class ThreadPool;
class BanExecutor;
struct DosContext {
uint64_t recv_msg_count_since_last_check = 0;
double last_check_recv_msg_check_time = 0;
double packet_count_rate = 0;
};
class ModuleDoSProtection : public Module {
friend std::shared_ptr<Module> ModuleInfo<ModuleDoSProtection>::create(Agent*);
public:
~ModuleDoSProtection() override = default;
#ifdef ENABLE_UNIT_TESTS
void clearWhiteList() {
mWhiteList.clear();
}
void setBanExecutor(const std::shared_ptr<BanExecutor>& executor);
#endif
private:
explicit ModuleDoSProtection(Agent* ag, ModuleInfoBase* moduleInfo);
void onLoad(const GenericStruct* mc) override;
void onUnload() override;
void onRequest(std::shared_ptr<RequestSipEvent>& ev) override;
void onResponse(std::shared_ptr<ResponseSipEvent>&) override{};
void onIdle() override;
bool isValidNextConfig(const ConfigValue& value) override;
bool isIpWhiteListed(const char* ip);
void registerUnbanTimer(const std::string& ip, const std::string& port, const std::string& protocol);
void unbanIP(const std::string& ip, const std::string& port, const std::string& protocol);
static ModuleInfo<ModuleDoSProtection> sInfo;
int mTimePeriod;
int mPacketRateLimit;
int mBanTime;
bool mExecutorConfigChecked = false;
std::set<BinaryIp> mWhiteList;
std::unordered_map<std::string, DosContext> mDosContexts;
std::unordered_map<std::string, DosContext>::iterator mDOSHashtableIterator;
std::unique_ptr<ThreadPool> mThreadPool;
std::shared_ptr<BanExecutor> mBanExecutor;
};
} // namespace flexisip

225
include/flexisip/event.hh Normal file
View file

@ -0,0 +1,225 @@
/*
Flexisip, a flexible SIP proxy server with media capabilities.
Copyright (C) 2010-2024 Belledonne Communications SARL, All rights reserved.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero 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 Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <functional>
#include <list>
#include <map>
#include <memory>
#include <ostream>
#include <regex.h>
#include <string>
#include <sofia-sip/msg.h>
#include <sofia-sip/nta.h>
#include <sofia-sip/sip.h>
#include <sofia-sip/tport.h>
#include "flexisip/sofia-wrapper/msg-sip.hh"
namespace flexisip {
class Agent;
class Module;
class IncomingAgent;
class OutgoingAgent;
class IncomingTransaction;
class OutgoingTransaction;
class EventLog;
class EventLogWriteDispatcher;
class SocketAddress;
using MsgSip = sofiasip::MsgSip;
class SipEvent : public std::enable_shared_from_this<SipEvent> {
friend class Agent;
public:
SipEvent(const std::shared_ptr<IncomingAgent>& inAgent,
const std::shared_ptr<MsgSip>& msgSip,
tport_t* tport = NULL);
SipEvent(const std::shared_ptr<OutgoingAgent>& outAgent,
const std::shared_ptr<MsgSip>& msgSip,
tport_t* tport = NULL);
SipEvent(const SipEvent& sipEvent);
inline const std::shared_ptr<MsgSip>& getMsgSip() const {
return mMsgSip;
}
inline su_home_t* getHome() const {
return mMsgSip->getHome();
}
inline sip_t* getSip() const {
return mMsgSip->getSip();
}
inline void setMsgSip(std::shared_ptr<MsgSip> msgSip) {
mMsgSip = msgSip;
}
virtual void terminateProcessing();
virtual void suspendProcessing();
virtual void restartProcessing();
inline bool isSuspended() const {
return mState == State::SUSPENDED;
}
inline bool isTerminated() const {
return mState == State::TERMINATED;
}
std::shared_ptr<IncomingAgent> getIncomingAgent() const;
std::shared_ptr<OutgoingAgent> getOutgoingAgent() const;
virtual inline void setIncomingAgent(const std::shared_ptr<IncomingAgent>& agent) {
mIncomingAgent = agent;
}
virtual inline void setOutgoingAgent(const std::shared_ptr<OutgoingAgent>& agent) {
mOutgoingAgent = agent;
}
virtual void send(const std::shared_ptr<MsgSip>& msg,
url_string_t const* u = NULL,
tag_type_t tag = 0,
tag_value_t value = 0,
...) = 0;
virtual ~SipEvent();
const std::weak_ptr<Module>& getCurrentModule() {
return mCurrModule;
}
template <typename _eventLogT>
std::shared_ptr<_eventLogT> getEventLog() {
return std::dynamic_pointer_cast<_eventLogT>(mEventLog);
}
void setEventLog(const std::shared_ptr<EventLog>& log);
void flushLog(); /*to be used exceptionally when an event log needs to be flushed immediately, for example because
you need to submit a new one.*/
// Write given EventLog immediately
void writeLog(const std::shared_ptr<const EventLogWriteDispatcher>&);
std::shared_ptr<IncomingTransaction> getIncomingTransaction();
std::shared_ptr<OutgoingTransaction> getOutgoingTransaction();
const std::shared_ptr<tport_t>& getIncomingTport() const;
std::shared_ptr<SocketAddress> getMsgAddress() const;
protected:
enum class State {
STARTED,
SUSPENDED,
TERMINATED,
};
static std::string stateStr(State s) {
switch (s) {
case State::STARTED:
return "STARTED";
case State::SUSPENDED:
return "SUSPENDED";
case State::TERMINATED:
return "TERMINATED";
}
return "invalid";
}
std::weak_ptr<Module> mCurrModule;
std::shared_ptr<MsgSip> mMsgSip;
std::shared_ptr<EventLog> mEventLog;
std::weak_ptr<Agent> mAgent;
State mState;
private:
std::shared_ptr<tport_t> mIncomingTport;
std::weak_ptr<IncomingAgent> mIncomingAgent;
std::weak_ptr<OutgoingAgent> mOutgoingAgent;
};
class RequestSipEvent : public SipEvent {
public:
RequestSipEvent(std::shared_ptr<IncomingAgent> incomingAgent,
const std::shared_ptr<MsgSip>& msgSip,
tport_t* tport = NULL);
RequestSipEvent(const std::shared_ptr<RequestSipEvent>& sipEvent);
// Sip event extends enable_shared_from_this and constructor should be private.
static std::shared_ptr<RequestSipEvent> makeRestored(std::shared_ptr<IncomingAgent> incomingAgent,
const std::shared_ptr<MsgSip>& msgSip,
const std::weak_ptr<Module>& currModule);
void suspendProcessing() override;
void terminateProcessing() override;
std::shared_ptr<IncomingTransaction> createIncomingTransaction();
std::shared_ptr<OutgoingTransaction> createOutgoingTransaction();
void send(const std::shared_ptr<MsgSip>& msg,
url_string_t const* u = NULL,
tag_type_t tag = 0,
tag_value_t value = 0,
...) override;
void reply(int status, char const* phrase, tag_type_t tag, tag_value_t value, ...);
void setIncomingAgent(const std::shared_ptr<IncomingAgent>& agent) override;
~RequestSipEvent();
/** Find if incoming tport TLS client certificate contains a given entry */
bool findIncomingSubject(const char* searched) const;
const char* findIncomingSubject(const std::list<std::string>& in) const;
bool matchIncomingSubject(regex_t* regex);
void unlinkTransactions();
bool mRecordRouteAdded;
private:
void checkContentLength(const url_t* url);
void linkTransactions();
std::shared_ptr<OutgoingTransaction> mOutgoingTransactionOwner;
};
class ResponseSipEvent : public SipEvent {
public:
ResponseSipEvent(std::shared_ptr<OutgoingAgent> outgoingAgent,
const std::shared_ptr<MsgSip>& msgSip,
tport_t* tport = NULL);
ResponseSipEvent(const std::shared_ptr<ResponseSipEvent>& sipEvent);
virtual void send(const std::shared_ptr<MsgSip>& msg,
url_string_t const* u = NULL,
tag_type_t tag = 0,
tag_value_t value = 0,
...);
virtual void setOutgoingAgent(const std::shared_ptr<OutgoingAgent>& agent);
~ResponseSipEvent();
private:
void checkContentLength(const std::shared_ptr<MsgSip>& msg, const sip_via_t* via);
bool mPopVia; // set to true if the response comes from an outgoing transaction.
};
} // namespace flexisip

View file

@ -0,0 +1,513 @@
/*
Flexisip, a flexible SIP proxy server with media capabilities.
Copyright (C) 2010-2023 Belledonne Communications SARL, All rights reserved.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero 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 Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/*
* Implementation file for the BooleanExpression and BooleanExpressionBuilder templates.
* This file must be included from the compilation unit where the template is instanciated.
*/
#include <algorithm>
#include <cstring>
#include <regex>
#include <sstream>
#include "flexisip/expressionparser.hh"
#include "flexisip/logmanager.hh"
using namespace std;
namespace flexisip {
template <typename _valuesT>
class ConstantBooleanExpression : public BooleanExpression<_valuesT> {
public:
ConstantBooleanExpression(bool ret) : mRet(ret){};
virtual bool eval([[maybe_unused]] const _valuesT& args) override {
return mRet;
}
bool mRet;
};
template <typename _valuesT>
class LogicalAnd : public BooleanExpression<_valuesT> {
private:
using Expr = BooleanExpression<_valuesT>;
shared_ptr<Expr> mExp1, mExp2;
public:
LogicalAnd(const shared_ptr<Expr> &exp1, const shared_ptr<Expr> &exp2) : mExp1(exp1), mExp2(exp2) {};
virtual bool eval(const _valuesT &args) override{
return mExp1->eval(args) && mExp2->eval(args);
}
};
template <typename _valuesT>
class LogicalOr : public BooleanExpression<_valuesT> {
public:
using Expr = BooleanExpression<_valuesT>;
LogicalOr(const shared_ptr<Expr> &exp1, const shared_ptr<Expr> &exp2) : mExp1(exp1), mExp2(exp2) {
}
virtual bool eval(const _valuesT &args) override{
return mExp1->eval(args) || mExp2->eval(args);
}
private:
shared_ptr<Expr> mExp1, mExp2;
};
template <typename _valuesT>
class LogicalNot : public BooleanExpression<_valuesT> {
public:
using Expr = BooleanExpression<_valuesT>;
LogicalNot(const shared_ptr<Expr> &exp) : mExp(exp) {
}
virtual bool eval(const _valuesT &args) override{
return !mExp->eval(args);
}
private:
shared_ptr<Expr> mExp;
};
template <typename _valuesT>
class EqualsOp : public BooleanExpression<_valuesT> {
public:
using Var = Variable<_valuesT>;
EqualsOp(const shared_ptr<Var> &var1, const shared_ptr<Var> &var2) : mVar1(var1), mVar2(var2) {
}
virtual bool eval(const _valuesT &args) override{
return mVar1->get(args) == mVar2->get(args);
}
private:
shared_ptr<Var> mVar1, mVar2;
};
template <typename _valuesT>
class UnEqualsOp : public BooleanExpression<_valuesT> {
public:
using Var = Variable<_valuesT>;
UnEqualsOp(const shared_ptr<Var> &var1, const shared_ptr<Var> &var2) : mVar1(var1), mVar2(var2) {
}
virtual bool eval(const _valuesT &args) override{
return mVar1->get(args) != mVar2->get(args);
}
private:
shared_ptr<Var> mVar1, mVar2;
};
/*
* This operator evaluates whether a variable is purely numeric or not.
*/
template <typename _valuesT>
class NumericOp : public BooleanExpression<_valuesT>{
public:
using Var = Variable<_valuesT>;
NumericOp(const shared_ptr<Var> &var) : mVar(var) {
}
virtual bool eval(const _valuesT &args) override{
string var = mVar->get(args);
bool res = true;
for (auto it = var.begin(); it != var.end(); ++it) {
if (!isdigit(*it)) {
res = false;
break;
}
}
return res;
}
private:
shared_ptr<Var> mVar;
};
/*
* This operator returns true if the variable is defined in the context of provided _valuesT.
* It directly uses the defined() method of variable to do this.
*/
template <typename _valuesT>
class DefinedOp : public BooleanExpression<_valuesT> {
public:
using Var = Variable<_valuesT>;
DefinedOp(const shared_ptr<Var> &var) : mVar(var) {
}
virtual bool eval(const _valuesT &args) {
return mVar->defined(args);
}
private:
shared_ptr<Var> mVar;
};
template <typename _valuesT> class RegexpOp : public BooleanExpression<_valuesT> {
public:
using Var = Variable<_valuesT>;
RegexpOp(const shared_ptr<Var>& input, const shared_ptr<Constant<_valuesT>>& pattern)
: mInput(input), mRegex(pattern->get(), std::regex::ECMAScript | std::regex::nosubs) {
}
virtual bool eval(const _valuesT& args) {
if (regex_match(mInput->get(args), mRegex)) {
return true;
}
return false;
}
private:
shared_ptr<Var> mInput;
std::regex mRegex;
};
template <typename _valuesT>
class ContainsOp : public BooleanExpression<_valuesT> {
public:
using Var = Variable<_valuesT>;
ContainsOp(const shared_ptr<Var> &var1, const shared_ptr<Var> &var2) : mVar1(var1), mVar2(var2) {
}
virtual bool eval(const _valuesT &args) override{
string var1 = mVar1->get(args);
string var2 = mVar2->get(args);
return var1.find(var2) != string::npos;
}
private:
shared_ptr<Var> mVar1, mVar2;
};
/*
* Evaluates whether a variable has its value equal to an element of a list of other variables.
*/
template <typename _valuesT>
class InOp : public BooleanExpression<_valuesT> {
public:
using Var = Variable<_valuesT>;
InOp(const shared_ptr<Var> &var1, const shared_ptr<Var> &var2) : mVar1(var1), mVar2(var2) {
}
virtual bool eval(const _valuesT &args) {
bool res = false;
list<string> values = mVar2->getAsList(args);
string varValue = mVar1->get(args);
for (auto it = values.begin(); it != values.end(); ++it) {
if (varValue == *it) {
res = true;
break;
}
}
return res;
}
private:
shared_ptr<Var> mVar1, mVar2;
};
template< typename _valuesT>
size_t BooleanExpressionBuilder<_valuesT>::findFirstNonWord(const string &expr, size_t offset) {
size_t i;
for (i = offset; i < expr.size(); ++i) {
char c = expr[i];
if (c != '-' && c != '_' && c != '.' && !isalnum(c))
return i;
}
return i;
}
template< typename _valuesT>
shared_ptr<Variable<_valuesT>> BooleanExpressionBuilder<_valuesT>::buildVariable(const string &expr, size_t *newpos) {
shared_ptr<Var> ret = dynamic_pointer_cast<Var>(buildElement(expr,newpos));
if (ret == nullptr){
throw invalid_argument("Expected variable at " + expr.substr(*newpos, string::npos));
}
return ret;
}
template< typename _valuesT>
shared_ptr<Constant<_valuesT>> BooleanExpressionBuilder<_valuesT>::buildConstant(const string &expr, size_t *newpos) {
shared_ptr<Constant<_valuesT>> ret = dynamic_pointer_cast<Constant<_valuesT>>(buildElement(expr,newpos));
if (ret == nullptr){
throw invalid_argument("Expected constant at " + expr.substr(*newpos, string::npos));
}
return ret;
}
template< typename _valuesT>
std::shared_ptr<ExpressionElement> BooleanExpressionBuilder<_valuesT>::buildElement(const std::string &expr, size_t *newpos){
while (expr[*newpos] == ' ' || expr[*newpos] == '\t'){
*newpos += 1;
}
if (expr[*newpos] == '\'') {
size_t end = expr.find_first_of('\'', *newpos + 1);
if (end == string::npos) throw invalid_argument("Missing quote around " + expr);
size_t len = end - *newpos - 1;
auto cons = expr.substr(*newpos + 1, len);
*newpos += len + 2; // remove the two '
return make_shared<Constant<_valuesT>>(cons);
}else{
// Can be a variable or a named operator
size_t eow = findFirstNonWord(expr, *newpos);
if (eow <= *newpos && expr.size() > eow) {
throw invalid_argument("no variable recognized in X" + expr.substr(*newpos, string::npos) + "XX");
}
size_t len = eow - *newpos;
auto word = expr.substr(*newpos, len);
*newpos += len;
auto varIt = mRules.variables.find(word);
auto opIt = mRules.operators.find(word);
if (varIt != mRules.variables.end()){
return make_shared<Variable<_valuesT>>((*varIt).second);
}else if (opIt != mRules.operators.end()){
return make_shared<NamedOperator<_valuesT>>((*opIt).second);
}else{
throw invalid_argument("Element '" + word + "' is not a variable nor operator name");
}
}
}
template< typename _valuesT>
size_t BooleanExpressionBuilder<_valuesT>::findMatchingClosingParenthesis(const string &expr, size_t offset) {
size_t i;
int match = 1;
for (i = offset; i < expr.size(); ++i) {
if (expr[i] == '(')
++match;
else if (expr[i] == ')')
--match;
if (match == 0)
return i;
}
return string::npos;
}
template< typename _valuesT>
bool BooleanExpressionBuilder<_valuesT>::isKeyword(const string &expr, size_t *newpos, const string &keyword) {
size_t pos = *newpos;
size_t keyLen = keyword.size();
size_t availableLen = expr.size() - pos;
if (availableLen < keyLen)
return false;
for (size_t i = 0; i < keyLen; ++i) {
if (expr[pos + i] != keyword[i])
return false;
}
if (availableLen > keyLen && isalnum(expr[pos + keyLen]))
return false;
*newpos += keyLen;
return true;
}
template< typename _valuesT>
void BooleanExpressionBuilder<_valuesT>::checkRulesOverlap(){
for(const string & builtin : sBuiltinOperators){
if (mRules.variables.find(builtin) != mRules.variables.end()){
LOGF("BooleanExpressionBuilder: variable name '%s' conflicts with builtin operator name.", builtin.c_str());
}
if (mRules.operators.find(builtin) != mRules.operators.end()){
LOGF("BooleanExpressionBuilder: variable name '%s' conflicts with builtin operator name.", builtin.c_str());
}
}
for (auto p : mRules.operators){
if (mRules.variables.find(p.first) != mRules.variables.end()){
LOGF("BooleanExpressionBuilder: variable name '%s' conflicts with operator name.", p.first.c_str());
}
}
}
template< typename _valuesT>
BooleanExpressionBuilder<_valuesT>::BooleanExpressionBuilder(const ExpressionRules<_valuesT> &rules) : mRules(rules){
checkRulesOverlap();
}
template< typename _valuesT>
std::shared_ptr<BooleanExpression<_valuesT>> BooleanExpressionBuilder<_valuesT>::parse(const std::string &expression){
if (expression.empty())
return make_shared<ConstantBooleanExpression<_valuesT>>(true); /* By arbitrary decision, we evaluate void to true.*/
size_t pos = 0;
return parseExpression(expression, &pos);
}
template< typename _valuesT>
const std::list<std::string> BooleanExpressionBuilder<_valuesT>::sBuiltinOperators = {
"&&", "||", "!", "==", "!=", "contains", "in", "notin", "nin", "defined", "regexp", "regex", "numeric",
"true", "false"
};
template< typename _valuesT>
shared_ptr<BooleanExpression<_valuesT>> BooleanExpressionBuilder<_valuesT>::parseExpression(const string &expr, size_t *newpos, bool immediateNeighbour) {
size_t i;
shared_ptr<Expr> cur_exp;
shared_ptr<Var> cur_var;
for (i = 0; i < expr.size();) {
size_t j = 0;
size_t prev_i = i;
switch (expr[i]) {
case '(': {
size_t end = findMatchingClosingParenthesis(expr, i + 1);
if (end != string::npos) {
cur_exp = parseExpression(expr.substr(i + 1, end - i - 1), &j);
i = end + 1;
} else {
throw invalid_argument("Missing parenthesis around " + expr);
}
} break;
case '&':
if (expr[i + 1] == '&') {
if (!cur_exp) {
throw new logic_error("&& operator expects first operand.");
}
i += 2;
cur_exp = make_shared<LogicalAnd<_valuesT>>(cur_exp, parseExpression(expr.substr(i), &j));
i += j;
} else {
throw new logic_error("Bad operator '&'");
}
break;
case '|':
if (expr[i + 1] == '|') {
if (!cur_exp) {
throw new logic_error("|| operator expects first operand.");
}
i += 2;
cur_exp = make_shared<LogicalOr<_valuesT>>(cur_exp, parseExpression(expr.substr(i), &j));
i += j;
} else {
throw invalid_argument("Bad operator '|'");
}
break;
case '!':
if (expr[i + 1] == '=') {
if (!cur_var) {
throw invalid_argument("!= operator expects first variable or const operand.");
}
i += 2;
cur_exp = make_shared<UnEqualsOp<_valuesT>>(cur_var, buildVariable(expr.substr(i), &j));
} else {
if (cur_exp) {
throw invalid_argument("Parsing error around '!'");
}
i++;
cur_exp = make_shared<LogicalNot<_valuesT>>(parseExpression(expr.substr(i), &j, true));
}
i += j;
break;
case '=':
if (expr[i + 1] == '=') {
if (!cur_var) {
throw invalid_argument("== operator expects first variable or const operand.");
}
i += 2;
cur_exp = make_shared<EqualsOp<_valuesT>>(cur_var, buildVariable(expr.substr(i), &j));
i += j;
} else {
throw invalid_argument("Bad operator =");
}
break;
case ' ':
i++;
break;
case 'c':
if (isKeyword(expr.substr(i), &j, "contains")) {
i += j;
j = 0;
if (cur_var == nullptr) throw invalid_argument("'contains' operator has no left-hand operand.");
auto rightVar = buildVariable(expr.substr(i), &j);
cur_exp = make_shared<ContainsOp<_valuesT>>(cur_var, rightVar);
i += j;
}
break;
case 'd':
if (isKeyword(expr.substr(i), &j, "defined")) {
i += j;
j = 0;
auto rightVar = buildVariable(expr.substr(i), &j);
cur_exp = make_shared<DefinedOp<_valuesT>>(rightVar);
i += j;
}
break;
case 'r':
if (isKeyword(expr.substr(i), &j, "regexp") || isKeyword(expr.substr(i), &j, "regex")) {
i += j;
j = 0;
auto pattern = buildConstant(expr.substr(i), &j);
cur_exp = make_shared<RegexpOp<_valuesT>>(cur_var, pattern);
i += j;
}
break;
case 'i':
if (isKeyword(expr.substr(i), &j, "in")) {
i += j;
j = 0;
auto rightVar = buildVariable(expr.substr(i), &j);
cur_exp = make_shared<InOp<_valuesT>>(cur_var, rightVar);
i += j;
}
break;
case 'n':
if (isKeyword(expr.substr(i), &j, "numeric")) {
if (cur_exp || cur_var) {
throw invalid_argument("Parsing error around 'numeric'");
}
i += j;
j = 0;
auto var = buildVariable(expr.substr(i), &j);
cur_exp = make_shared<NumericOp<_valuesT>>(var);
i += j;
j = 0;
} else if (isKeyword(expr.substr(i), &j, "nin") || isKeyword(expr.substr(i), &j, "notin")) {
i += j;
j = 0;
auto rightVar = buildVariable(expr.substr(i), &j);
auto in = make_shared<InOp<_valuesT>>(cur_var, rightVar);
cur_exp = make_shared<LogicalNot<_valuesT>>(in);
i += j;
}
break;
case 't':
if (isKeyword(expr.substr(i), &j, "true")) {
i += j;
cur_exp = make_shared<ConstantBooleanExpression<_valuesT>>(true);
}
break;
case 'f':
if (isKeyword(expr.substr(i), &j, "false")) {
i += j;
cur_exp = make_shared<ConstantBooleanExpression<_valuesT>>(false);
}
break;
default:
break;
}
if (prev_i == i){ /*nothing was handled*/
shared_ptr<ExpressionElement> element = buildElement(expr.substr(i), &j);
i += j;
if (dynamic_pointer_cast<Var>(element)){
cur_var = dynamic_pointer_cast<Var>(element);
}else if (dynamic_pointer_cast<NamedOperator<_valuesT>>(element)){
cur_exp = dynamic_pointer_cast<NamedOperator<_valuesT>>(element);
}
}
if (immediateNeighbour && cur_exp) break;
}
*newpos += i;
if (!cur_exp){
throw invalid_argument("Meaning-less expression, possibly without operator.");
}
return cur_exp;
};
// rajouter is_request, is_response
} //end of namespace

View file

@ -0,0 +1,177 @@
/*
Flexisip, a flexible SIP proxy server with media capabilities.
Copyright (C) 2010-2015 Belledonne Communications SARL, All rights reserved.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero 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 Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <string>
#include <memory>
#include <map>
#include <list>
#include <functional>
namespace flexisip {
class ExpressionElement{
public:
virtual ~ExpressionElement() = default;
};
/*
* Variable represents a text field which is evaluated at run-time using the _valuesT argument.
*/
template <typename _valuesT>
class Variable : public ExpressionElement{
public:
Variable(const std::function< std::string (const _valuesT &)> &func) : mFunc(func){
}
~Variable() = default;
virtual std::string get(const _valuesT &args){
return mFunc(args);
}
virtual bool defined(const _valuesT &args){
if (get(args).empty()) return false;
return true;
}
virtual std::list<std::string> getAsList(const _valuesT &args) {
std::list<std::string> valueList;
std::string s = get(args);
size_t pos1 = 0;
size_t pos2 = 0;
for (pos2 = 0; pos2 < s.size(); ++pos2) {
if (s[pos2] != ' ') {
if (s[pos1] == ' ')
pos1 = pos2;
continue;
}
if (s[pos2] == ' ' && s[pos1] == ' ') {
pos1 = pos2;
continue;
}
valueList.push_back(s.substr(pos1, pos2 - pos1));
pos1 = pos2;
}
if (pos1 != pos2)
valueList.push_back(s.substr(pos1, pos2 - pos1));
return valueList;
}
private:
std::function< std::string (const _valuesT &)> mFunc;
protected:
Variable() = default;
};
/*
* Constant can be seen as a special kind of variable that always evaluates to the same thing, regardless of _valuesT argument contains.
* They are enclosed by single quotes in the boolean expression.
*/
template <typename _valuesT>
class Constant : public Variable<_valuesT>{
std::string mVal;
public:
Constant(const std::string &val) : Variable<_valuesT>(), mVal(val) {
}
virtual std::string get([[maybe_unused]] const _valuesT &arg) override{
return mVal;
}
std::string get()const{
return mVal;
}
};
/*
* Base class for our boolean expression.
* It contains the factory method to create BooleanExpression from an input string,
* and the eval() method to evaluates the BooleanExpression to true or false according
* to supplied _valuesT arguments.
*/
template <typename _valuesT>
class BooleanExpression {
public:
virtual ~BooleanExpression() = default;
virtual bool eval(const _valuesT &args) = 0;
protected:
BooleanExpression() = default;
};
/*
* A named operator is a function that evaluates on the values provided to return
* true or false.
*/
template <typename _valuesT>
class NamedOperator : public BooleanExpression<_valuesT>, public ExpressionElement{
public:
NamedOperator(const std::function< bool (const _valuesT &)> func) : mFunc(func){
}
virtual bool eval(const _valuesT &args) override{
return mFunc(args);
}
private:
std::function< bool (const _valuesT &)> mFunc;
};
/*
* The ExpressionRules consist of two maps suitable to be initialized with builtin initializers.
* The variables map provides mapping between a variable name and function to be called to get the
* variable's value in the _valuesT argument.
* The operators map provides the mapping between operator names and the function that evaluates them.
*/
template <typename _valuesT>
struct ExpressionRules{
public:
std::map<std::string, std::function< std::string (const _valuesT &)>> variables; // the map of variables with their function to evaluate
std::map<std::string, std::function< bool (const _valuesT &)>> operators; // the named operators, with their function to evaluate.
};
/*
* The BooleanExpressionBuilder creates BooleanExpression by parsing an input string.
* The BooleanExpression is constructed according to the rules provided in the constructor,
* which gave the map of allowed variables and named operators.
*/
template <typename _valuesT>
class BooleanExpressionBuilder{
public:
using Var = Variable<_valuesT>;
using Const = Constant<_valuesT>;
using Expr = BooleanExpression<_valuesT>;
BooleanExpressionBuilder(const ExpressionRules<_valuesT> &rules);
std::shared_ptr<BooleanExpression<_valuesT>> parse(const std::string &expression);
private:
void checkRulesOverlap();
size_t findFirstNonWord(const std::string &expr, size_t offset);
size_t findMatchingClosingParenthesis(const std::string &expr, size_t offset);
bool isKeyword(const std::string &expr, size_t *newpos, const std::string &keyword);
std::shared_ptr<Var> buildVariable(const std::string &expr, size_t *newpos);
std::shared_ptr<Const> buildConstant(const std::string &expr, size_t *newpos);
std::shared_ptr<ExpressionElement> buildElement(const std::string &expr, size_t *newpos);
std::shared_ptr<Expr> parseExpression(const std::string &expr, size_t *newpos, bool immediateNeighbour = false);
const ExpressionRules<_valuesT> mRules;
static const std::list<std::string> sBuiltinOperators;
};
}

View file

@ -0,0 +1,143 @@
/*
Flexisip, a flexible SIP proxy server with media capabilities.
Copyright (C) 2010-2023 Belledonne Communications SARL, All rights reserved.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero 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 Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stdexcept>
#include <string>
#include <string_view>
#include "sofia-sip/sip_status.h"
#include <bctoolbox/exception.hh>
namespace flexisip {
/**
* @brief This exception inherits \ref BctoolboxException.
*
*
*/
class FlexisipException : public BctbxException {
public:
FlexisipException() = default;
explicit FlexisipException(const std::string& message) : BctbxException(message) {
}
explicit FlexisipException(const char* message) : BctbxException(message) {
}
~FlexisipException() throw() override = default;
FlexisipException(const FlexisipException& other) = default;
template <typename T2>
FlexisipException& operator<<(const T2& val) {
BctbxException::operator<<(val);
return *this;
}
};
#define FLEXISIP_EXCEPTION FlexisipException() << " " << __FILE__ << ":" << __LINE__ << " "
/**
* @brief Sip response class.
*
*/
class SipStatus {
public:
SipStatus(int code, std::string_view phrase) : mCode(code), mReason(phrase) {
}
int getCode() const {
return mCode;
}
const char* getReason() const {
return mReason.c_str();
}
private:
int mCode;
std::string mReason;
};
/**
* @brief This exception reports all sip errors.
*
*/
class GenericSipException : public std::runtime_error {
public:
GenericSipException(int statusCode, std::string_view statusPhrase)
: GenericSipException(statusCode, statusPhrase, "") {
}
GenericSipException(int statusCode, std::string_view statusPhrase, std::string_view additionalMsg)
: std::runtime_error(statusPhrase.data()), mSipStatus(statusCode, statusPhrase), mMsg(statusPhrase) {
if (!additionalMsg.empty()) mMsg += std::string(": ") + additionalMsg.data();
}
virtual ~GenericSipException() = default;
void addLocation(const char* file, int line) {
mMsg = std::string("Exception in ") + file + ":" + std::to_string(line) + " " + mMsg;
}
virtual const SipStatus& getSipStatus() const noexcept {
return mSipStatus;
};
virtual const char* what() const noexcept override {
return mMsg.data();
};
private:
const SipStatus mSipStatus;
std::string mMsg;
};
/**
* @brief This exception inherits \ref GenericSipException and reports client errors on request.
*
*/
class InvalidRequestError : public GenericSipException {
public:
InvalidRequestError() : InvalidRequestError("") {
}
InvalidRequestError(std::string_view additionalMsg) : GenericSipException(SIP_400_BAD_REQUEST, additionalMsg) {
}
InvalidRequestError(std::string_view reasonSuffix, std::string_view additionalMsg)
: GenericSipException(400, std::string(sip_status_phrase(400)) + " - " + reasonSuffix.data(), additionalMsg) {
}
virtual ~InvalidRequestError() = default;
};
/**
* @brief This exception inherits \ref GenericSipException and reports server errors.
*
*/
class InternalError : public GenericSipException {
public:
InternalError() : InternalError("") {
}
InternalError(std::string_view additionalMsg) : GenericSipException(SIP_500_INTERNAL_SERVER_ERROR, additionalMsg) {
}
InternalError(std::string_view reasonSuffix, std::string_view additionalMsg)
: GenericSipException(500, std::string(sip_status_phrase(500)) + " - " + reasonSuffix.data(), additionalMsg) {
}
virtual ~InternalError() = default;
};
#define THROW_LINE(myException, ...) \
do { \
auto e = myException(__VA_ARGS__); \
e.addLocation(__FILE__, __LINE__); \
throw e; \
} while (false)
} // namespace flexisip

View file

@ -0,0 +1,151 @@
/*
Flexisip, a flexible SIP proxy server with media capabilities.
Copyright (C) 2010-2023 Belledonne Communications SARL, All rights reserved.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero 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 Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <chrono>
#include <memory>
#include <vector>
#include "flexisip/pushnotification/pushnotification-context-observer.hh"
#include "flexisip/sofia-wrapper/msg-sip.hh"
namespace flexisip {
enum class FinalStatusMode;
class BranchInfo;
class IncomingTransaction;
class OutgoingTransaction;
class RequestSipEvent;
class ResponseSipEvent;
class SipUri;
struct ExtendedContact;
struct ForkContextConfig {
int mDeliveryTimeout = 0; /* in seconds, used for "late" forking*/
std::chrono::seconds mUrgentTimeout{5}; /*timeout for sending buffered urgent or retryable responses (like 415).*/
std::chrono::seconds mPushResponseTimeout{0}; /*timeout for receiving response to push */
int mCurrentBranchesTimeout = 0; /*timeout for receiving response on current branches*/
bool mForkLate = false;
bool mSaveForkMessageEnabled = false;
bool mTreatAllErrorsAsUrgent = false; /*treat all SIP response code as urgent replies in the fork mechanism.*/
bool mForkNoGlobalDecline = false;
bool mTreatDeclineAsUrgent =
false; /*treat 603 declined as a urgent response, only useful is mForkNoGlobalDecline==true*/
bool mPermitSelfGeneratedProvisionalResponse = true; /* Self explicit. Ex: 110 Push sent, 180 Ringing*/
};
class ForkContext : public PushNotificationContextObserver {
public:
virtual ~ForkContext() = default;
// Obtain the ForkContext that manages a transaction.
static std::shared_ptr<ForkContext> getFork(const std::shared_ptr<IncomingTransaction>& tr);
static std::shared_ptr<ForkContext> getFork(const std::shared_ptr<OutgoingTransaction>& tr);
// Set the ForkContext managed by an incoming transaction.
static void setFork(const std::shared_ptr<IncomingTransaction>& tr, const std::shared_ptr<ForkContext>& fork);
// Called by the router module to notify a cancellation.
static void processCancel(const std::shared_ptr<RequestSipEvent>& ev);
// called by the router module to notify the arrival of a response.
static bool processResponse(const std::shared_ptr<ResponseSipEvent>& ev);
bool isEqual(const std::shared_ptr<ForkContext>& other) const {
return getPtrForEquality() == other->getPtrForEquality();
}
// Called by the Router module to create a new branch.
virtual std::shared_ptr<BranchInfo> addBranch(const std::shared_ptr<RequestSipEvent>& ev,
const std::shared_ptr<ExtendedContact>& contact) = 0;
virtual bool allCurrentBranchesAnswered(FinalStatusMode finalStatusMode) const = 0;
// Request if the fork has other branches with lower priorities to try
virtual bool hasNextBranches() const = 0;
/**
* Called when a fatal internal error is thrown in Flexisip. Send a custom response and cancel all branches if
* necessary.
* @param status The status of the custom response to send.
* @param phrase The content of the custom response to send.
*/
virtual void processInternalError(int status, const char* phrase) = 0;
// Start the processing of the highest priority branches that are not completed yet
virtual void start() = 0;
virtual void addKey(const std::string& key) = 0;
virtual const std::vector<std::string>& getKeys() const = 0;
/**
* Informs the forked call context that a new register from a potential destination of the fork just arrived.
* If the fork context is interested in handling this new destination he can call
* ForkContextListener::onDispatchNeeded, call ForkContextListener::onUselessRegisterNotification otherwise.
*
* Typical case for refusing it is when another transaction already exists or existed for this contact.
*/
virtual void
onNewRegister(const SipUri& dest, const std::string& uid, const std::shared_ptr<ExtendedContact>& newContact) = 0;
// Notifies the cancellation of the fork process.
virtual void onCancel(const std::shared_ptr<RequestSipEvent>& ev) = 0;
// Notifies the arrival of a new response on a given branch
virtual void onResponse(const std::shared_ptr<BranchInfo>& br, const std::shared_ptr<ResponseSipEvent>& event) = 0;
virtual const std::shared_ptr<RequestSipEvent>& getEvent() = 0;
virtual const std::shared_ptr<ForkContextConfig>& getConfig() const = 0;
virtual bool isFinished() const = 0;
/**
* Check if the fork context should be considered as finished. A final answer is sent if needed.
* If a final answer is sent and correspond to a branch, this branch is returned.
*/
virtual std::shared_ptr<BranchInfo> checkFinished() = 0;
virtual sofiasip::MsgSipPriority getMsgPriority() const = 0;
virtual const ForkContext* getPtrForEquality() const = 0;
protected:
// Protected methods
std::string errorLogPrefix() const;
std::string logPrefix() const;
virtual const char* getClassName() const = 0;
};
enum class DispatchStatus {
DispatchNeeded,
DispatchNotNeeded,
PendingTransaction,
};
class ForkContextListener {
public:
virtual ~ForkContextListener() = default;
virtual void onForkContextFinished(const std::shared_ptr<ForkContext>& ctx) = 0;
/**
* Called when a fork context need a dispatch for specific contact.
*/
virtual std::shared_ptr<BranchInfo> onDispatchNeeded(const std::shared_ptr<ForkContext>& ctx,
const std::shared_ptr<ExtendedContact>& newContact) = 0;
/**
* Called when onNewRegister was called on a fork and that no dispatch was needed for this contact.
*/
virtual void onUselessRegisterNotification(const std::shared_ptr<ForkContext>& ctx,
const std::shared_ptr<ExtendedContact>& newContact,
const SipUri& dest,
const std::string& uid,
const DispatchStatus reason) = 0;
};
} // namespace flexisip

View file

@ -0,0 +1,25 @@
/*
Flexisip, a flexible SIP proxy server with media capabilities.
Copyright (C) 2010-2018 Belledonne Communications SARL.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero 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 Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
// =============================================================================
#define FLEXISIP_DISABLE_COPY(CLASS) \
CLASS(const CLASS &) = delete; \
CLASS &operator=(const CLASS &) = delete

View file

@ -0,0 +1,242 @@
/*
Flexisip, a flexible SIP proxy server with media capabilities.
Copyright (C) 2010-2024 Belledonne Communications SARL, All rights reserved.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero 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 Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <cstdio>
#include <memory>
#include <mutex>
#include <string>
#ifndef FLEXISIP_USER_ERRORS_LOG_DOMAIN
#define FLEXISIP_USER_ERRORS_LOG_DOMAIN "flexisip-users"
#endif
#define FLEXISIP_LOG_DOMAIN "flexisip"
#ifndef BCTBX_LOG_DOMAIN
#define BCTBX_LOG_DOMAIN FLEXISIP_LOG_DOMAIN
#endif
#include <bctoolbox/logging.h>
#include <sofia-sip/sip.h>
#include "flexisip/sip-boolean-expressions.hh"
#include "flexisip/sofia-wrapper/timer.hh"
/*
* These are the classic C-style logging macros.
*/
#define LOGT bctbx_debug
#define LOGD bctbx_debug
#define LOGI bctbx_message
#define LOGW bctbx_warning
#define LOGE bctbx_error
#define LOGA bctbx_fatal
#define LOGV(thelevel, thefmt, theargs) bctbx_logv(FLEXISIP_LOG_DOMAIN, thelevel, (thefmt), (theargs))
#define LOGDV(thefmt, theargs) LOGV(BCTBX_LOG_DEBUG, thefmt, theargs)
/*
* These are the C++ logging macros, that can be used with << operator.
*/
#define SLOG(thelevel) BCTBX_SLOG(FLEXISIP_LOG_DOMAIN, thelevel)
#define SLOGT SLOG(BCTBX_LOG_DEBUG)
#define SLOGD SLOG(BCTBX_LOG_DEBUG)
#define SLOGI SLOG(BCTBX_LOG_MESSAGE)
#define SLOGW SLOG(BCTBX_LOG_WARNING)
#define SLOGE SLOG(BCTBX_LOG_ERROR)
#define SLOGUE BCTBX_SLOG(FLEXISIP_USER_ERRORS_LOG_DOMAIN, BCTBX_LOG_ERROR)
namespace sofiasip {
class MsgSip;
}
namespace flexisip {
class SipLogContext;
using MsgSip = sofiasip::MsgSip;
/*
* The LogManager is the main entry point to configure logs in Flexisip.
*/
class LogManager {
public:
// Public types
struct Parameters {
std::shared_ptr<sofiasip::SuRoot> root{nullptr}; /* MUST be set to have reopenFiles() working. */
std::string logDirectory{};
std::string logFilename{};
size_t fileMaxSize{std::numeric_limits<decltype(fileMaxSize)>::max()};
BctbxLogLevel level{BCTBX_LOG_ERROR};
BctbxLogLevel syslogLevel{BCTBX_LOG_ERROR};
bool enableSyslog{true};
bool enableUserErrors{false};
bool enableStdout{false};
};
// Public ctor
LogManager(const LogManager&) = delete;
~LogManager();
// Public methods
BctbxLogLevel logLevelFromName(const std::string& name) const;
// Initialize logging system
void initialize(const Parameters& params);
// Change log level
void setLogLevel(BctbxLogLevel level);
// Change log level
void setSyslogLevel(BctbxLogLevel level);
void enableUserErrorsLogs(bool val);
/*
* Set a contextual filter based on sip message contents, and associated log level to use when the filter matches.
* Returns -1 if the filter is not valid.
*/
int setContextualFilter(const std::string& expression);
/*
* Set the log level when the contextual filter is matched.
*/
void setContextualLevel(BctbxLogLevel level);
// Disable all logs.
void disable();
bool syslogEnabled() const {
return mSysLogHandler != nullptr;
};
/**
* @brief Require the reopening of each log file.
* @note This method can be used inside UNIX signal handlers.
*/
void reopenFiles() {
mReopenRequired = true;
}
// Public class methods
static LogManager& get();
private:
// Private ctor
LogManager() = default;
// Private methods
void setCurrentContext(const SipLogContext& ctx);
void clearCurrentContext();
void checkForReopening();
static void stdoutLogHandler(const char* domain, BctbxLogLevel level, const char* msg, va_list args);
static void logStub(const char* domain, BctbxLogLevel level, const char* msg, va_list args);
// Private attributes
std::mutex mMutex{};
mutable std::mutex mRootDomainMutex{};
std::shared_ptr<SipBooleanExpression> mCurrentFilter{};
std::string mRootDomain{}; // This domain prefixed the domain part of every log message. Useful to distinct the log
// messages comming from other processus.
BctbxLogLevel mLevel{BCTBX_LOG_ERROR}; // The normal log level.
BctbxLogLevel mContextLevel{BCTBX_LOG_ERROR}; // The log level when log context matches the condition.
bctbx_log_handler_t* mLogHandler{nullptr};
bctbx_log_handler_t* mSysLogHandler{nullptr};
std::unique_ptr<sofiasip::Timer> mTimer{};
bool mInitialized{false};
bool mReopenRequired{false};
// Private class attributes
static std::unique_ptr<LogManager> sInstance;
// Friendship
friend class SipLogContext;
friend class LogContext;
};
class LogContext {
public:
LogContext() = default;
~LogContext();
};
/*
* Class for contextual logs.
* For now it just uses the MsgSip being processed by Flexisip.
* This class should typically be instantiated on stack (not with new).
* When it goes out of scope, it automatically clears the context with the LogManager.
*/
class SipLogContext : public LogContext {
friend class LogManager;
public:
SipLogContext(const MsgSip& msg);
SipLogContext(const std::shared_ptr<MsgSip>& msg);
private:
const MsgSip& mMsgSip;
};
} // end of namespace flexisip
static BctbxLogLevel flexisip_sysLevelMin = BCTBX_LOG_ERROR;
/*
* We want LOGN to output all the time (in standard output or syslog): this is for startup notice.
*/
template <typename... Args>
inline void LOGN(const char* format, const Args&... args) {
if (!flexisip::LogManager::get().syslogEnabled()) {
fprintf(stdout, format, args...);
fprintf(stdout, "\n");
} else if (flexisip_sysLevelMin >= BCTBX_LOG_MESSAGE) {
syslog(LOG_INFO, format, args...);
}
bctbx_set_thread_log_level(NULL, BCTBX_LOG_MESSAGE);
bctbx_log(FLEXISIP_LOG_DOMAIN, BCTBX_LOG_MESSAGE, format, args...);
bctbx_clear_thread_log_level(NULL);
}
/**
* LOGEN and LOGF must be used to report any startup or configuration fatal error that needs to be seen by the
* operator.
* This is why it goes to standard output if syslog is not used (mostly for daemon mode).
**/
template <typename... Args>
inline void LOGEN(const char* format, const Args&... args) {
if (!flexisip::LogManager::get().syslogEnabled()) {
fprintf(stderr, format, args...);
fprintf(stderr, "\n");
}
bctbx_set_thread_log_level(NULL, BCTBX_LOG_MESSAGE);
bctbx_log(FLEXISIP_LOG_DOMAIN, BCTBX_LOG_ERROR, format, args...);
bctbx_clear_thread_log_level(NULL);
}
template <typename... Args>
inline void LOGF(const char* format, const Args&... args) {
LOGEN(format, args...);
exit(-1);
}
/**
* Remove and secure : warning - format string is not a string literal (potentially insecure)
* While using a string with no arguments
*/
inline void LOGEN(const char* simpleLog) {
LOGEN("%s", simpleLog);
}
inline void LOGF(const char* simpleLog) {
LOGF("%s", simpleLog);
}

View file

@ -0,0 +1,70 @@
/*
Flexisip, a flexible SIP proxy server with media capabilities.
Copyright (C) 2010-2024 Belledonne Communications SARL, All rights reserved.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero 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 Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "flexisip/module-authentication-base.hh"
#include "flexisip/module.hh"
#include "flexisip/sofia-wrapper/auth-module.hh"
namespace flexisip {
class Agent;
class AuthDb;
class Authentication : public ModuleAuthenticationBase {
friend std::shared_ptr<Module> ModuleInfo<Authentication>::create(Agent*);
public:
StatCounter64* mCountAsyncRetrieve = nullptr;
StatCounter64* mCountSyncRetrieve = nullptr;
StatCounter64* mCountPassFound = nullptr;
StatCounter64* mCountPassNotFound = nullptr;
~Authentication() override;
void onLoad(const GenericStruct* mc) override;
bool tlsClientCertificatePostCheck(const std::shared_ptr<RequestSipEvent>& ev);
virtual bool handleTlsClientAuthentication(const std::shared_ptr<RequestSipEvent>& ev);
void onResponse(std::shared_ptr<ResponseSipEvent>& ev) override;
void onIdle() override;
bool doOnConfigStateChanged(const ConfigValue& conf, ConfigState state) override;
static void declareConfig(GenericStruct& moduleConfig);
protected:
Authentication(Agent* ag, const ModuleInfoBase* moduleInfo);
private:
FlexisipAuthModuleBase* createAuthModule(const std::string& domain, int nonceExpire, bool qopAuth) override;
void processAuthentication(const std::shared_ptr<RequestSipEvent>& request, FlexisipAuthModuleBase& am) override;
const char* findIncomingSubjectInTrusted(const std::shared_ptr<RequestSipEvent>& ev, const char* fromDomain);
static ModuleInfo<Authentication> sInfo;
std::list<std::string> mTrustedClientCertificates;
regex_t mRequiredSubject;
bool mNewAuthOn407 = false;
bool mRequiredSubjectCheckSet = false;
bool mRejectWrongClientCertificates = false;
bool mTrustDomainCertificates = false;
AuthDb& mAuthDb;
};
} // namespace flexisip

View file

@ -0,0 +1,126 @@
/*
Flexisip, a flexible SIP proxy server with media capabilities.
Copyright (C) 2010-2024 Belledonne Communications SARL, All rights reserved.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero 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 Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <array>
#include <list>
#include <map>
#include <memory>
#include <set>
#include <string>
#include "flexisip/auth/flexisip-auth-module-base.hh"
#include "flexisip/module.hh"
namespace flexisip {
// Forward declaration to avoid to have to publish the RealmExtractor class header.
class RealmExtractor;
/**
* Base class for Flexisip authentication modules.
*/
class ModuleAuthenticationBase : public Module {
public:
ModuleAuthenticationBase(Agent* agent, const ModuleInfoBase* moduleInfo);
~ModuleAuthenticationBase();
bool isTrustedPeer(const std::shared_ptr<RequestSipEvent>& ev);
static void declareConfig(GenericStruct& root);
protected:
// ================
// Protected types
// ================
/**
* This exception is globally caught by ModuleAuthenticationBase::onRequest()
* causing onRequest() return. It is to used in any sub-functions
* of onRequest() in order to stop the request event processing
* and pass to the next Flexisip module.
*/
class StopRequestProcessing : public std::exception {};
// ==================
// Protected methods
// ==================
void onLoad(const GenericStruct* root) override;
void onRequest(std::shared_ptr<RequestSipEvent>& ev) override;
void onResponse([[maybe_unused]] std::shared_ptr<ResponseSipEvent>& ev) override {
}
/**
* Override this method to specify the specialization of #FlexisipAuthModuleBase to instantiate.
*/
virtual FlexisipAuthModuleBase* createAuthModule(const std::string& domain, int nonceExpire, bool qopAuth) = 0;
/**
* @brief Create and configure a #FlexisipAuthStatus according the information extracted from ev.
*
* This method may be overridden in order to instantiate a specialization of #FlexisipAuthStatus. Should it be,
* the overriding method might call #configureAuthStatus() for configuring the base of the returned object.
*/
virtual FlexisipAuthStatus* createAuthStatus(const std::shared_ptr<RequestSipEvent>& ev);
/**
* Called by createAuthStatus() for setting #FlexisipAuthStatus attribute for the event request information.
*/
void configureAuthStatus(FlexisipAuthStatus& as, const std::shared_ptr<RequestSipEvent>& ev);
void validateRequest(const std::shared_ptr<RequestSipEvent>& request);
virtual void processAuthentication(const std::shared_ptr<RequestSipEvent>& request, FlexisipAuthModuleBase& am);
/**
* Called by onRequest() for getting a #FlexisipAuthModuleBase instance from a domain name.
*/
FlexisipAuthModuleBase* findAuthModule(const std::string name);
/**
* This method is called synchronously or asynchronously on result of AuthModule::verify() method.
* It calls onSuccess() and errorReply() according the authentication result.
*/
void processAuthModuleResponse(AuthStatus& as);
virtual void onSuccess(const FlexisipAuthStatus& as);
virtual void errorReply(const FlexisipAuthStatus& as);
void loadTrustedHosts(const ConfigStringList& trustedHosts);
bool empty(const char* value) {
return value == NULL || value[0] == '\0';
}
/**
* Test whether a string match a valid algorithm in specified by sValidAlgos.
*/
static bool validAlgo(const std::string& algo);
// =====================
// Protected attributes
// =====================
std::set<BinaryIp> mTrustedHosts;
std::map<std::string, std::unique_ptr<FlexisipAuthModuleBase>> mAuthModules;
std::list<std::string> mAlgorithms;
auth_challenger_t mRegistrarChallenger;
auth_challenger_t mProxyChallenger;
RealmExtractor* mRealmExtractor{nullptr}; /* initially, this attribute was declared as
std::unique_ptr<RealmExtractor> but that broke the compilation on Debian/Ubuntu platforms although the default
destructor of ModuleAuthenticationBase was declared in the .cc file */
std::shared_ptr<SipBooleanExpression> mNo403Expr;
static const std::array<std::string, 2> sValidAlgos;
};
} // namespace flexisip

View file

@ -0,0 +1,186 @@
/*
Flexisip, a flexible SIP proxy server with media capabilities.
Copyright (C) 2010-2024 Belledonne Communications SARL, All rights reserved.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero 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 Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <sofia-sip/sip_status.h>
#include <sofia-sip/su_random.h>
#include "flexisip/module.hh"
#include "flexisip/registrar/registar-listeners.hh"
#include "flexisip/signal-handling/sofia-driven-signal-handler.hh"
namespace flexisip {
struct RegistrarStats {
std::unique_ptr<StatPair> mCountBind;
std::unique_ptr<StatPair> mCountClear;
StatCounter64* mCountLocalActives;
};
class ModuleRegistrar;
class Agent;
class ResponseContext;
// Listener class NEED to copy the shared pointer
class OnRequestBindListener : public ContactUpdateListener {
ModuleRegistrar* mModule;
std::shared_ptr<RequestSipEvent> mEv;
sip_from_t* mSipFrom;
su_home_t mHome;
sip_contact_t* mContact;
sip_path_t* mPath;
public:
OnRequestBindListener(ModuleRegistrar* module,
std::shared_ptr<RequestSipEvent> ev,
const sip_from_t* sipuri = NULL,
sip_contact_t* contact = NULL,
sip_path_t* path = NULL);
~OnRequestBindListener();
void onContactUpdated(const std::shared_ptr<ExtendedContact>& ec) override;
void onRecordFound(const std::shared_ptr<Record>& r) override;
void onError(const SipStatus& response) override;
void onInvalid(const SipStatus& response) override;
};
class OnResponseBindListener : public ContactUpdateListener {
ModuleRegistrar* mModule;
std::shared_ptr<ResponseSipEvent> mEv;
std::shared_ptr<OutgoingTransaction> mTr;
std::shared_ptr<ResponseContext> mCtx;
public:
OnResponseBindListener(ModuleRegistrar* module,
std::shared_ptr<ResponseSipEvent> ev,
std::shared_ptr<OutgoingTransaction> tr,
std::shared_ptr<ResponseContext> ctx);
void onContactUpdated(const std::shared_ptr<ExtendedContact>& ec) override;
void onRecordFound(const std::shared_ptr<Record>& r) override;
void onError(const SipStatus& response) override;
void onInvalid(const SipStatus& response) override;
};
// Listener class NEED to copy the shared pointer
class OnStaticBindListener : public ContactUpdateListener {
friend class ModuleRegistrar;
sofiasip::Home mHome;
std::string mContact;
std::string mFrom;
public:
OnStaticBindListener(const url_t* from, const sip_contact_t* ct);
void onContactUpdated(const std::shared_ptr<ExtendedContact>& ec) override;
void onRecordFound(const std::shared_ptr<Record>& r) override;
void onError(const SipStatus& response) override;
void onInvalid(const SipStatus& response) override;
};
class FakeFetchListener : public ContactUpdateListener {
friend class ModuleRegistrar;
public:
FakeFetchListener();
void onContactUpdated(const std::shared_ptr<ExtendedContact>& ec) override;
void onRecordFound(const std::shared_ptr<Record>& r) override;
void onError(const SipStatus& response) override;
void onInvalid(const SipStatus& response) override;
};
class ResponseContext {
public:
ResponseContext(std::shared_ptr<RequestSipEvent>&& ev, int globalDelta);
const std::shared_ptr<RequestSipEvent> mRequestSipEvent;
};
class ModuleRegistrar : public Module {
friend std::shared_ptr<Module> ModuleInfo<ModuleRegistrar>::create(Agent*);
friend class OnRequestBindListener;
friend class OnResponseBindListener;
public:
~ModuleRegistrar() {
}
virtual void onLoad(const GenericStruct* mc);
virtual void onUnload();
virtual void onRequest(std::shared_ptr<RequestSipEvent>& ev);
virtual void onResponse(std::shared_ptr<ResponseSipEvent>& ev);
template <typename SipEventT, typename ListenerT>
void processUpdateRequest(std::shared_ptr<SipEventT>& ev, const sip_t* sip);
void idle();
void
reply(std::shared_ptr<RequestSipEvent>& ev, int code, const char* reason, const sip_contact_t* contacts = NULL);
void readStaticRecords();
static void declareConfig(GenericStruct& moduleConfig);
protected:
ModuleRegistrar(Agent* ag, const ModuleInfoBase* moduleInfo);
private:
static int numberOfContactHeaders(const sip_contact_t* rootHeader);
std::shared_ptr<RequestSipEvent> createUpstreamRequestEvent(std::shared_ptr<RequestSipEvent>&& ev, int globalDelta);
void deleteResponseContext(const std::shared_ptr<ResponseContext>& ctx);
void updateLocalRegExpire();
bool isManagedDomain(const url_t* url);
bool isAdjacentRegistration(const sip_t* sip);
std::string routingKey(const url_t* sipUri);
void removeInternalParams(sip_contact_t* ct);
RegistrarStats mStats;
bool mUpdateOnResponse;
bool mAllowDomainRegistrations;
std::list<std::string> mDomains;
std::list<std::string> mUniqueIdParams;
std::string mServiceRoute;
static std::list<std::string> mPushNotifParams;
std::string mRoutingParam;
unsigned int mMaxExpires, mMinExpires;
std::string mStaticRecordsFile;
su_timer_t* mStaticRecordsTimer;
int mStaticRecordsTimeout;
int mStaticRecordsVersion;
bool mAssumeUniqueDomains;
static ModuleInfo<ModuleRegistrar> sInfo;
bool mUseGlobalDomain;
int mExpireRandomizer;
int mMaxContactsPerRegistration;
std::list<std::string> mParamsToRemove;
std::unique_ptr<signal_handling::SofiaDrivenSignalHandler> mSignalHandler = nullptr;
};
class RegistrarMgt {
public:
virtual unsigned long long int getTotalNumberOfAddedRecords() = 0;
virtual unsigned long long int getTotalNumberOfExpiredRecords() = 0;
};
} // namespace flexisip

View file

@ -0,0 +1,41 @@
/*
Flexisip, a flexible SIP proxy server with media capabilities.
Copyright (C) 2010-2023 Belledonne Communications SARL, All rights reserved.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero 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 Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <memory>
namespace flexisip {
class ForkContext;
class RequestSipEvent;
/**
* Interface for the ModuleRouter object, this interface is under construction.
* For now it allow to mock the Agent in some test cases.
*/
class ModuleRouterInterface {
public:
virtual ~ModuleRouterInterface() = default;
virtual void sendToInjector(const std::shared_ptr<RequestSipEvent>& ev,
const std::shared_ptr<ForkContext>& context,
const std::string& contactId) = 0;
};
} // namespace flexisip

View file

@ -0,0 +1,201 @@
/*
Flexisip, a flexible SIP proxy server with media capabilities.
Copyright (C) 2010-2024 Belledonne Communications SARL, All rights reserved.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero 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 Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <memory>
#include <vector>
#include "flexisip/fork-context/fork-context.hh"
#include "flexisip/module-router-interface.hh"
#include "flexisip/module.hh"
#include "flexisip/registrar/registar-listeners.hh"
#include "flexisip/utils/sip-uri.hh"
namespace flexisip {
struct RouterStats {
std::unique_ptr<StatPair> mCountForks;
std::shared_ptr<StatPair> mCountBasicForks;
std::shared_ptr<StatPair> mCountCallForks;
std::shared_ptr<StatPair> mCountMessageForks;
std::shared_ptr<StatPair> mCountMessageProxyForks;
};
class OnContactRegisteredListener;
class Injector;
class Agent;
class Record;
class ModuleRouter : public Module,
public ModuleRouterInterface,
public ForkContextListener,
public std::enable_shared_from_this<ModuleRouter> {
friend std::shared_ptr<Module> ModuleInfo<ModuleRouter>::create(Agent*);
public:
~ModuleRouter();
void onLoad(const GenericStruct* mc) override;
void onUnload() override{};
void onRequest(std::shared_ptr<RequestSipEvent>& ev) override;
void onResponse(std::shared_ptr<ResponseSipEvent>& ev) override;
void onForkContextFinished(const std::shared_ptr<ForkContext>& ctx) override;
std::shared_ptr<BranchInfo> onDispatchNeeded(const std::shared_ptr<ForkContext>& ctx,
const std::shared_ptr<ExtendedContact>& newContact) override;
void onUselessRegisterNotification(const std::shared_ptr<ForkContext>& ctx,
const std::shared_ptr<ExtendedContact>& newContact,
const SipUri& dest,
const std::string& uid,
const DispatchStatus reason) override;
void sendReply(std::shared_ptr<RequestSipEvent>& ev,
int code,
const char* reason,
int warn_code = 0,
const char* warning = nullptr);
void routeRequest(std::shared_ptr<RequestSipEvent>& ev, const std::shared_ptr<Record>& aor, const url_t* sipUri);
void onContactRegistered(const std::shared_ptr<OnContactRegisteredListener>& listener,
const std::string& uid,
const std::shared_ptr<Record>& aor);
const std::string& getFallbackRoute() const {
return mFallbackRoute;
}
const url_t* getFallbackRouteParsed() const {
return mFallbackRouteParsed;
}
bool isFallbackToParentDomainEnabled() const {
return mFallbackParentDomain;
}
bool isDomainRegistrationAllowed() const {
return mAllowDomainRegistrations;
}
bool isManagedDomain(const url_t* url) const;
const std::shared_ptr<SipBooleanExpression>& getFallbackRouteFilter() const {
return mFallbackRouteFilter;
}
const std::shared_ptr<ForkContextConfig>& getCallForkCfg() const {
return mCallForkCfg;
}
const std::shared_ptr<ForkContextConfig>& getMessageForkCfg() const {
return mMessageForkCfg;
}
const std::shared_ptr<ForkContextConfig>& getOtherForkCfg() const {
return mOtherForkCfg;
}
void sendToInjector(const std::shared_ptr<RequestSipEvent>& ev,
const std::shared_ptr<ForkContext>& context,
const std::string& contactId) override;
static void setMaxPriorityHandled(sofiasip::MsgSipPriority maxPriorityHandled) {
sMaxPriorityHandled = maxPriorityHandled;
}
static void declareConfig(GenericStruct& moduleConfig);
RouterStats mStats;
protected:
ModuleRouter(Agent* ag, const ModuleInfoBase* moduleInfo);
using ForkMapElem = std::shared_ptr<ForkContext>;
using ForkMap = std::multimap<std::string, ForkMapElem>;
using ForkRefList = std::vector<ForkMapElem>;
// This template method has a single specialization that returns the key
// as a Record::Key object. It is declared in the module-router.cc file.
// Originally a standard method, it was transformed into a template method
// to prevent the need for including "record.hh", which would otherwise
// require placing this header in the public API.
//
// @todo Remove this method and replace its usage with direct Record::Key construction.
template <typename KeyT>
KeyT routingKey(const url_t* sipUri);
std::vector<std::string> split(const char* data, const char* delim);
ForkRefList getLateForks(const std::string& key) const noexcept;
std::shared_ptr<BranchInfo> dispatch(const std::shared_ptr<ForkContext>& context,
const std::shared_ptr<ExtendedContact>& contact,
const std::string& targetUris = "");
std::list<std::string> mDomains;
std::shared_ptr<ForkContextConfig> mCallForkCfg;
std::shared_ptr<ForkContextConfig> mMessageForkCfg;
std::shared_ptr<ForkContextConfig> mOtherForkCfg;
ForkMap mForks;
bool mUseGlobalDomain = false;
bool mAllowDomainRegistrations = false;
bool mAllowTargetFactorization = false;
bool mResolveRoutes = false;
bool mFallbackParentDomain = false;
std::string mFallbackRoute;
url_t* mFallbackRouteParsed = nullptr;
private:
#if ENABLE_SOCI
void restoreForksFromDatabase();
#endif
static ModuleInfo<ModuleRouter> sInfo;
static sofiasip::MsgSipPriority sMaxPriorityHandled;
std::shared_ptr<SipBooleanExpression> mFallbackRouteFilter;
std::shared_ptr<OnContactRegisteredListener> mOnContactRegisteredListener{nullptr};
std::unique_ptr<Injector> mInjector;
std::vector<SipUri> mStaticTargets;
};
class OnContactRegisteredListener : public ContactRegisteredListener,
public ContactUpdateListener,
public std::enable_shared_from_this<OnContactRegisteredListener> {
friend class ModuleRouter;
ModuleRouter* mModule;
sofiasip::Home mHome;
public:
OnContactRegisteredListener(ModuleRouter* module) : mModule(module) {
SLOGD << "OnContactRegisteredListener created";
}
~OnContactRegisteredListener() = default;
void onContactRegistered(const std::shared_ptr<Record>& r, const std::string& uid) override;
void onRecordFound([[maybe_unused]] const std::shared_ptr<Record>& r) override {
}
void onError(const SipStatus&) override {
}
void onInvalid(const SipStatus&) override {
}
void onContactUpdated([[maybe_unused]] const std::shared_ptr<ExtendedContact>& ec) override {
}
};
} // namespace flexisip

261
include/flexisip/module.hh Normal file
View file

@ -0,0 +1,261 @@
/*
Flexisip, a flexible SIP proxy server with media capabilities.
Copyright (C) 2010-2024 Belledonne Communications SARL, All rights reserved.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero 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 Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <sofia-sip/msg_header.h>
#include <sofia-sip/nta_tport.h>
#include <sofia-sip/tport.h>
#include "flexisip/configmanager.hh"
#include "flexisip/event.hh"
#include "flexisip/sofia-wrapper/home.hh"
namespace flexisip {
// =============================================================================
// -----------------------------------------------------------------------------
// Module.
// -----------------------------------------------------------------------------
class ModuleInfoBase;
template <typename T>
class ModuleInfo;
class SharedLibrary;
class EntryFilter;
enum class ModuleClass { Experimental, Production };
/**
* Abstract base class for all Flexisip module.
* A module is an object that is able to process sip requests and sip responses.
* It must implements at least:
* virtual void onRequest(SipEvent *ev)=0;
* virtual void onResponse(SipEvent *ev)=0;
**/
class Module : protected ConfigValueListener {
template <typename T>
friend class ModuleInfo;
public:
Module(Agent* agent, const ModuleInfoBase* moduleInfo);
virtual ~Module();
Agent* getAgent() const {
return mAgent;
}
nta_agent_t* getSofiaAgent() const;
const std::string& getModuleName() const;
const std::string& getModuleConfigName() const;
void checkConfig();
void load();
void unload();
void reload();
StatCounter64& findStat(const std::string& statName) const;
void idle();
bool isEnabled() const;
ModuleClass getClass() const;
void processRequest(std::shared_ptr<RequestSipEvent>& ev);
void processResponse(std::shared_ptr<ResponseSipEvent>& ev);
void process(std::shared_ptr<RequestSipEvent>& ev) {
processRequest(ev);
}
void process(std::shared_ptr<ResponseSipEvent>& ev) {
processResponse(ev);
}
virtual void injectRequestEvent(const std::shared_ptr<RequestSipEvent>& ev);
const ModuleInfoBase* getInfo() const {
return mInfo;
}
protected:
virtual void onLoad([[maybe_unused]] const GenericStruct* root) {
}
virtual void onUnload() {
}
virtual void onRequest(std::shared_ptr<RequestSipEvent>& ev) = 0;
virtual void onResponse(std::shared_ptr<ResponseSipEvent>& ev) = 0;
virtual bool doOnConfigStateChanged(const ConfigValue& conf, ConfigState state);
virtual void onIdle() {
}
virtual bool onCheckValidNextConfig() {
return true;
}
virtual bool isValidNextConfig([[maybe_unused]] const ConfigValue& cv) {
return true;
}
void sendTrap(const std::string& msg);
protected:
sofiasip::Home mHome;
Agent* mAgent = nullptr;
const ModuleInfoBase* mInfo;
GenericStruct* mModuleConfig = nullptr;
std::unique_ptr<EntryFilter> mFilter;
};
// -----------------------------------------------------------------------------
// ModuleInfo.
// -----------------------------------------------------------------------------
class ModuleInfoBase;
class ModuleInfoManager {
friend class ModuleInfoBase;
public:
~ModuleInfoManager();
const std::list<ModuleInfoBase*>& getRegisteredModuleInfo() const {
return mRegisteredModuleInfo;
}
std::list<ModuleInfoBase*> buildModuleChain() const;
static ModuleInfoManager* get();
private:
void registerModuleInfo(ModuleInfoBase* moduleInfo);
void unregisterModuleInfo(ModuleInfoBase* moduleInfo);
void dumpModuleDependencies(const std::list<ModuleInfoBase*>& l) const;
bool moduleDependenciesPresent(const std::list<ModuleInfoBase*>& sortedList, ModuleInfoBase* module) const;
void replaceModules(std::list<ModuleInfoBase*>& sortedList,
const std::list<ModuleInfoBase*>& replacingModules) const;
std::list<ModuleInfoBase*> mRegisteredModuleInfo;
static std::unique_ptr<ModuleInfoManager> sInstance;
};
class ModuleInfoBase {
public:
enum ModuleOid {
SanityChecker = 3,
DoSProtection = 4,
GarbageIn = 5,
Capabilities = 10,
NatHelper = 30,
Authentication = 60,
CustomAuthentication = 61,
DateHandler = 75,
GatewayAdapter = 90,
Registrar = 120,
StatisticsCollector = 123,
Router = 125,
PushNotification = 130,
ContactRouteInserter = 150,
LoadBalancer = 180,
MediaRelay = 210,
Transcoder = 240,
Forward = 270,
Redirect = 290,
Presence = 300,
RegEvent = 305,
B2bua = 307,
InterDomainConnections = 310,
Plugin = 320
};
ModuleInfoBase(const std::string& moduleName,
const std::string& help,
const std::vector<std::string>& after,
ModuleOid oid,
std::function<void(GenericStruct&)> declareConfig,
ModuleClass moduleClass,
const std::string& replace)
: mName(moduleName), mHelp(help), mAfter(after), mOidIndex(oid), mDeclareConfig(declareConfig),
mClass(moduleClass), mReplace(replace) {
ModuleInfoManager::get()->registerModuleInfo(this);
}
virtual ~ModuleInfoBase() {
if (mRegistered) {
ModuleInfoManager::get()->unregisterModuleInfo(this);
}
}
const std::string& getModuleName() const {
return mName;
}
const std::string& getModuleHelp() const {
return mHelp;
}
const std::vector<std::string>& getAfter() const {
return mAfter;
}
unsigned int getOidIndex() const {
return mOidIndex;
}
ModuleClass getClass() const {
return mClass;
}
const std::string& getReplace() const {
return mReplace;
}
const std::string& getFunction() const {
return mReplace.empty() ? mName : mReplace;
}
void setRegistered(bool newState) {
mRegistered = newState;
}
void declareConfig(GenericStruct& rootConfig) const;
virtual std::shared_ptr<Module> create(Agent* agent) = 0;
private:
std::string mName;
std::string mHelp;
std::vector<std::string> mAfter;
oid mOidIndex;
std::function<void(GenericStruct&)> mDeclareConfig;
ModuleClass mClass;
std::string mReplace;
bool mRegistered{false};
};
template <typename T>
class ModuleInfo : public ModuleInfoBase {
public:
using ModuleType = T;
ModuleInfo(const std::string& moduleName,
const std::string& help,
const std::vector<std::string>& after,
ModuleOid oid,
std::function<void(GenericStruct&)> declareConfig,
ModuleClass moduleClass = ModuleClass::Production,
const std::string& replace = "")
: ModuleInfoBase(moduleName, help, after, oid, declareConfig, moduleClass, replace) {
}
std::shared_ptr<Module> create(Agent* agent) override {
std::shared_ptr<Module> module;
module.reset(new T(agent, this));
return module;
}
};
} // namespace flexisip

View file

@ -0,0 +1,63 @@
/*
Flexisip, a flexible SIP proxy server with media capabilities.
Copyright (C) 2010-2024 Belledonne Communications SARL, All rights reserved.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero 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 Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <flexisip/flexisip-version.h>
#include <flexisip/module.hh>
#ifndef FLEXISIP_GIT_VERSION
#define FLEXISIP_GIT_VERSION "undefined"
#endif // ifndef FLEXISIP_GIT_VERSION
#define FLEXISIP_PLUGIN_API_VERSION FLEXISIP_GIT_VERSION
#ifdef WIN32
#define FLEXISIP_PLUGIN_EXPORT extern "C" __declspec(dllexport)
#else
#define FLEXISIP_PLUGIN_EXPORT extern "C"
#endif // ifdef WIN32
namespace flexisip {
struct PluginInfo {
const char* className;
const char* name;
int version;
const char* apiVersion;
};
inline std::ostream& operator<<(std::ostream& os, const PluginInfo& info) {
os << "Plugin info:" << std::endl
<< " Name: " << info.name << std::endl
<< " Class Name: " << info.className << std::endl
<< " Version: " << info.version << std::endl
<< " Api Version: " << info.apiVersion;
return os;
}
#define FLEXISIP_DECLARE_PLUGIN(MODULE_INFO, NAME, VERSION) \
static_assert(std::is_base_of<ModuleInfoBase, decltype(MODULE_INFO)>::value, \
"Flexisip plugin must be derived from ModuleInfoBase class."); \
FLEXISIP_PLUGIN_EXPORT const ModuleInfoBase* __flexisipGetPluginModuleInfo() { \
return &MODULE_INFO; \
} \
FLEXISIP_PLUGIN_EXPORT const PluginInfo __flexisipPluginInfo = {#MODULE_INFO, NAME, VERSION, \
FLEXISIP_PLUGIN_API_VERSION};
} // namespace flexisip

View file

@ -0,0 +1,40 @@
/*
Flexisip, a flexible SIP proxy server with media capabilities.
Copyright (C) 2010-2022 Belledonne Communications SARL, All rights reserved.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero 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 Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
namespace flexisip {
class PushNotificationContext;
/**
* Interface for PushNotificationContext observers.
*/
class PushNotificationContextObserver {
public:
virtual ~PushNotificationContextObserver() = default;
/**
* Notify the observer that a push notification has been sent by the PushNotificationContext.
* @param aPNCtx The PushNotificationContext that has sent the PN.
* @param aRingingPush Tells whether the sent PN makes the callee's device to ring without
* waking the user agent up.
*/
virtual void onPushSent(PushNotificationContext& aPNCtx, bool aRingingPush) noexcept = 0;
};
} // namespace flexisip

View file

@ -0,0 +1,96 @@
/*
Flexisip, a flexible SIP proxy server with media capabilities.
Copyright (C) 2010-2025 Belledonne Communications SARL, All rights reserved.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero 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 Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <memory>
#include "flexisip/configmanager.hh"
#include "flexisip/flexisip-exception.hh"
namespace flexisip {
class Record;
struct ExtendedContact;
/**
* @brief Interface for RegistrarDB listeners.
*/
class RegistrarDbListener : public StatFinishListener {
public:
virtual ~RegistrarDbListener();
/**
* Method called when searching for a record matching a given SIP identity is completed.
*
* @param record The found record or nullptr if no record could be found. If not null, the ownership on the object
* is held by the implementation and the object might be destroyed immediately after onRecordFound() has returned.
*/
virtual void onRecordFound(const std::shared_ptr<Record>& record) = 0;
/**
* Internal error, translated to a 5xx response by the registrar module.
*/
virtual void onError(const SipStatus& response) = 0;
/**
* Client error, translated to a 4xx (Invalid) response by the registrar module.
*/
virtual void onInvalid(const SipStatus& response) = 0;
};
class ContactUpdateListener : public RegistrarDbListener {
public:
~ContactUpdateListener() override;
virtual void onContactUpdated(const std::shared_ptr<ExtendedContact>& ec) = 0;
};
class ListContactUpdateListener {
public:
virtual ~ListContactUpdateListener();
virtual void onContactsUpdated() = 0;
std::vector<std::shared_ptr<Record>> records;
};
/*TODO: the listener should be also used to report when the subscription is active.
* Indeed if we send a push notification to a device while REDIS has not yet confirmed the subscription, we will not do
* anything when receiving the REGISTER from the device. The router module should wait confirmation that subscription is
* active before injecting the forked request to the module chain.*/
class ContactRegisteredListener {
public:
virtual ~ContactRegisteredListener();
virtual void onContactRegistered(const std::shared_ptr<Record>& record, const std::string& uid) = 0;
};
class LocalRegExpireListener {
public:
virtual ~LocalRegExpireListener();
virtual void onLocalRegExpireUpdated(unsigned int count) = 0;
};
class RegistrarDbStateListener {
public:
virtual ~RegistrarDbStateListener();
virtual void onRegistrarDbWritable(bool writable) = 0;
};
} // namespace flexisip

View file

@ -0,0 +1,70 @@
/** Copyright (C) 2010-2023 Belledonne Communications SARL
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
#pragma once
#include <unistd.h>
#include <csignal>
#include <cstdint>
#include <unordered_map>
#include <vector>
namespace flexisip {
namespace signal_handling {
using SigNum = decltype(SIGTERM);
using PipeDescriptor = int;
union SignalData {
SigNum signum;
uint8_t bytes[sizeof(signum)];
};
/**
* Registers a simple handler for the given POSIX signals that will forward the signal number to a pipe on reception.
* A safe handler can then be implemented that reads the value from the pipe outside of the special signal context.
*
* On destruction, the PipedSignal will unbind the signal handler from the given signals and close the pipe.
*
* In the event that two PipedSignals are created for the same signal, the one created last will shadow the first (which
* will therefore never be called again)
*/
class PipedSignal {
public:
PipedSignal(std::vector<SigNum>&& signals);
~PipedSignal();
PipedSignal(PipedSignal&& other) = delete;
PipedSignal(const PipedSignal& other) = delete;
PipedSignal& operator=(const PipedSignal& other) = delete;
PipedSignal& operator=(PipedSignal&& other) = delete;
/* Get the file descriptor for the read end of the pipe */
PipeDescriptor descriptor() {
return mPipe.fd.read;
}
/* Read a signal number from the pipe into `data`.
* This is just a wrapper for POSIX `read` and will __block__ until there is data in the pipe
*/
ssize_t read(SignalData& data);
private:
static std::unordered_map<SigNum, PipeDescriptor> sSignalToPipe;
union SignalPipe {
struct Descriptors {
PipeDescriptor read;
PipeDescriptor write;
} fd;
PipeDescriptor array[2];
} mPipe;
std::vector<SigNum> mSignals{};
};
} // namespace signal_handling
} // namespace flexisip

View file

@ -0,0 +1,71 @@
/** Copyright (C) 2010-2023 Belledonne Communications SARL
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
#pragma once
#include <functional>
#include "flexisip/logmanager.hh"
#include "flexisip/signal-handling/signal-handling.hh"
#include "flexisip/sofia-wrapper/waker.hh"
namespace flexisip {
namespace signal_handling {
/**
* Register a lambda to be called by the sofia loop after the given POSIX signals have been received.
*
* This lambda runs outside of the signal action callback and therefore does not need to be signal-safe.
*
* On destruction, this object unregisters from the sofia loop and signal handling mechanism.
*
* In the event that two handlers are created for the same signal, the one created last will shadow the first (which
* will therefore never be called again)
*/
class SofiaDrivenSignalHandler {
using Callback = std::function<void(SigNum)>;
public:
/** SAFETY:
* - `root` MUST NOT be null and MUST be valid for the lifetime of the Handler
*/
SofiaDrivenSignalHandler(su_root_t* root, std::vector<SigNum>&& signals, Callback&& callback)
: mSignalPipe(std::move(signals)), mCallback(std::move(callback)),
mWaker(
root,
mSignalPipe.descriptor(),
// SAFETY: Capturing `this` is safe because we own the Waker.
[this](su_root_magic_t*, su_wait_t* waiter) noexcept {
// Logging is safe in here since we're out of the signal handler
if (waiter->revents & SU_WAIT_ERR) {
LOGE("Error on signal pipe");
return 0;
}
if (waiter->revents & SU_WAIT_HUP) {
LOGE("Signal pipe closed");
return 0;
}
SignalData signal;
if (mSignalPipe.read(signal) != sizeof(signal)) {
LOGE("Error reading from signal pipe");
return 0;
}
mCallback(signal.signum);
return 0;
},
su_pri_normal) {
}
private:
PipedSignal mSignalPipe;
Callback mCallback;
sofiasip::Waker mWaker;
};
} // namespace signal_handling
} // namespace flexisip

View file

@ -0,0 +1,40 @@
/*
Flexisip, a flexible SIP proxy server with media capabilities.
Copyright (C) 2010 Belledonne Communications SARL.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero 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 Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "flexisip/expressionparser.hh"
typedef struct sip_s sip_t;
namespace flexisip {
using SipBooleanExpression = BooleanExpression<sip_t>;
class SipBooleanExpressionBuilder : public BooleanExpressionBuilder<sip_t> {
public:
static SipBooleanExpressionBuilder &get();
std::shared_ptr<SipBooleanExpression> parse(const std::string &expression);
private:
SipBooleanExpressionBuilder();
static std::shared_ptr<SipBooleanExpressionBuilder> sInstance;
};
} //end of namespace

View file

@ -0,0 +1,96 @@
/*
Flexisip, a flexible SIP proxy server with media capabilities.
Copyright (C) 2010-2022 Belledonne Communications SARL, All rights reserved.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero 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 Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <memory>
#include <sofia-sip/auth_module.h>
#include "flexisip/sofia-wrapper/auth-status.hh"
namespace flexisip {
/**
* @brief Interface for authentication modules.
* @note This class is a plain C++ wrapper of SofiaSip's auth_mod_t
* object. Please look at http://sofia-sip.sourceforge.net/refdocs/iptsec/auth__module_8h.html
* for a complete documentation.
*/
class AuthModule {
public:
AuthModule(su_root_t* root, tag_type_t, tag_value_t, ...);
virtual ~AuthModule() {
auth_mod_destroy(mAm);
}
/**
* Return a pointer on the underlying SofiaSip's authentication module.
* This method is useful if you mean to call a SofiaSip function that needs
* an auth_mod_t object as parameter.
*/
auth_mod_t* getPtr() const {
return mAm;
}
/**
* Event loop which the authentication module is working on.
* This has been define on module construction.
*/
su_root_t* getRoot() const {
return mRoot;
}
/**
* These methods are C++ version of public method of auth_mod_t API. To find the associated
* SofiaSip function, just prefix the name of the method by "auth_mod_" e.g. verify() -> auth_mod_verify().
*/
void verify(AuthStatus& as, msg_auth_t* credentials, auth_challenger_t const* ach) {
auth_mod_verify(mAm, as.getPtr(), credentials, ach);
}
void challenge(AuthStatus& as, auth_challenger_t const* ach) {
auth_mod_challenge(mAm, as.getPtr(), ach);
}
void authorize(AuthStatus& as, auth_challenger_t const* ach) {
auth_mod_challenge(mAm, as.getPtr(), ach);
}
void cancel(AuthStatus& as) {
auth_mod_cancel(mAm, as.getPtr());
}
protected:
virtual void onCheck(AuthStatus& as, msg_auth_t* credentials, auth_challenger_t const* ach) = 0;
virtual void onChallenge(AuthStatus& as, auth_challenger_t const* ach) = 0;
virtual void onCancel(AuthStatus& as) = 0;
auth_mod_t* mAm = nullptr;
private:
static void checkCb(auth_mod_t* am, auth_status_t* as, msg_auth_t* auth, auth_challenger_t const* ch) noexcept;
static void challengeCb(auth_mod_t* am, auth_status_t* as, auth_challenger_t const* ach) noexcept;
static void cancelCb(auth_mod_t* am, auth_status_t* as) noexcept;
static void registerScheme();
su_root_t* mRoot = nullptr;
static const char* sMethodName;
static auth_scheme_t sAuthScheme;
static bool sSchemeRegistered;
};
} // namespace flexisip

View file

@ -0,0 +1,223 @@
/*
Flexisip, a flexible SIP proxy server with media capabilities.
Copyright (C) 2010-2022 Belledonne Communications SARL, All rights reserved.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero 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 Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <functional>
#include <string>
#include <sofia-sip/auth_module.h>
namespace flexisip {
/**
* @brief Plain C++ wrapper for SofiaSip's auth_status_t structure.
* @warning Like auth_status_t, this classe doesn't take ownership
* on strings that have been set by setters. That means you must
* guarantees the life of such strings while the authentication is processing.
* If you cannot, you may copy the string by using the AuthStatus' internal
* home_t object before giving it to the setter method. This one can be
* got thanks to home() method.
* @see See http://sofia-sip.sourceforge.net/refdocs/iptsec/structauth__status__t.html
* for more documentation.
*/
class AuthStatus {
public:
using ResponseCb = std::function<void(AuthStatus& as)>;
AuthStatus() {
su_home_init(&mHome);
mPriv = auth_status_new(&mHome);
mPriv->as_plugin = reinterpret_cast<auth_splugin_t*>(this);
mPriv->as_callback = responseCb;
}
AuthStatus(const AuthStatus& other) = delete;
virtual ~AuthStatus() {
su_home_deinit(&mHome);
}
bool allow() const {
return mPriv->as_allow;
}
void allow(bool val) {
mPriv->as_allow = val;
}
bool anonymous() const {
return mPriv->as_anonymous;
}
void anonymous(bool val) {
mPriv->as_anonymous = val;
}
const void* body() const {
return mPriv->as_body;
}
void body(const void* val) {
mPriv->as_body = val;
}
isize_t bodyLen() const {
return mPriv->as_bodylen;
}
void bodyLen(isize_t val) {
mPriv->as_bodylen = val;
}
bool blacklist() const {
return mPriv->as_blacklist;
}
void blacklist(bool val) {
mPriv->as_blacklist = val;
}
const ResponseCb& callback() const {
return mResponseCb;
}
void callback(const ResponseCb& cb) {
mResponseCb = cb;
}
const char* display() const {
return mPriv->as_display;
}
void display(const char* val) {
mPriv->as_display = val;
}
/**
* Internal home_t, which will be destroyed on destruction
* of the AuthStatus.
*/
su_home_t* home() {
return mPriv->as_home;
}
msg_header_t* info() const {
return mPriv->as_info;
}
void info(msg_header_t* val) {
mPriv->as_info = val;
}
auth_magic_t* magic() const {
return mPriv->as_magic;
}
void magic(auth_magic_t* val) {
mPriv->as_magic = val;
}
msg_header_t* match() const {
return mPriv->as_match;
}
void match(msg_header_t* val) {
mPriv->as_match = val;
}
const char* method() const {
return mPriv->as_method;
}
void method(const char* val) {
mPriv->as_method = val;
}
msg_time_t nonceIssued() const {
return mPriv->as_nonce_issued;
}
void nonceIssued(msg_time_t val) {
mPriv->as_nonce_issued = val;
}
const char* phrase() const {
return mPriv->as_phrase;
}
void phrase(const char* val) {
mPriv->as_phrase = val;
}
const char* realm() const {
return mPriv->as_realm;
}
void realm(const char* val) {
mPriv->as_realm = val;
}
void realm(const std::string& val) {
mPriv->as_realm = su_strdup(&mHome, val.c_str());
}
msg_header_t* response() const {
return mPriv->as_response;
}
void response(msg_header_t* val) {
mPriv->as_response = val;
}
su_addrinfo_t* source() const {
return mPriv->as_source;
}
void source(su_addrinfo_t* val) {
mPriv->as_source = val;
}
bool stale() const {
return mPriv->as_stale;
}
void stale(bool val) {
mPriv->as_stale = val;
}
int status() const {
return mPriv->as_status;
}
void status(int val) {
mPriv->as_status = val;
}
const char* user() const {
return mPriv->as_user;
}
void user(const char* val) {
mPriv->as_user = val;
}
const url_t* userUri() const {
return mPriv->as_user_uri;
}
void userUri(const url_t* val) {
mPriv->as_user_uri = val;
}
/**
* Return the underlying SofiaSip's auth_status_t object.
*/
auth_status_t* getPtr() {
return mPriv;
}
private:
static void responseCb([[maybe_unused]] auth_magic_t* magic, auth_status_t* as) {
AuthStatus& authStatus = *reinterpret_cast<AuthStatus*>(as->as_plugin);
if (authStatus.mResponseCb) authStatus.mResponseCb(authStatus);
}
su_home_t mHome;
auth_status_t* mPriv = nullptr;
ResponseCb mResponseCb;
};
} // namespace flexisip

View file

@ -0,0 +1,112 @@
/*
Flexisip, a flexible SIP proxy server with media capabilities.
Copyright (C) 2010-2023 Belledonne Communications SARL, All rights reserved.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero 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 Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <cctype>
#include <initializer_list>
#include <ostream>
#include <sstream>
#include <string_view>
#include <type_traits>
#include "sofia-sip/sip_protos.h"
#include "sofia-sip/su_alloc.h"
#include "flexisip/template-metaprogramming.hh"
namespace sofiasip {
/**
* A wrapper for SofiaSip's su_home_t type.
*/
class Home {
public:
Home() noexcept {
su_home_init(mHome);
}
Home(const Home& src) = delete;
Home(Home&& src) noexcept : Home() {
su_home_move(mHome, src.mHome);
}
~Home() noexcept {
su_home_deinit(mHome);
}
Home& operator=(const Home& src) = delete;
Home& operator=(Home&& src) noexcept {
reset();
su_home_move(mHome, src.mHome);
return *this;
}
su_home_t* home() noexcept {
return mHome;
}
const su_home_t* home() const noexcept {
return mHome;
}
// Free all the buffers which are referenced by this Home.
void reset() noexcept {
su_home_deinit(mHome);
su_home_init(mHome);
}
void* alloc(std::size_t size) noexcept {
return su_alloc(mHome, size);
}
void free(void* data) noexcept {
return su_free(mHome, data);
}
char* vsprintf(char const* fmt, va_list ap) noexcept {
return su_vsprintf(mHome, fmt, ap);
}
template <typename... Args>
char* sprintf(const char* fmt, Args&&... args) noexcept {
return su_sprintf(mHome, fmt, args...);
}
// Equivalent to sip_contact_create
template <typename... IterableOrStreamable>
sip_contact_t* createContact(const std::string_view& url, IterableOrStreamable&&... params) {
std::ostringstream contact{};
contact << '<' << url << '>';
(appendParam(contact, params), ...);
return sip_contact_make(mHome, contact.str().c_str());
}
private:
template <typename IterableOfStreamable,
typename = std::enable_if_t<type::is_iterable<IterableOfStreamable>>,
typename = std::enable_if_t<!type::is_streamable<IterableOfStreamable>>>
static void appendParam(std::ostream& contact, const IterableOfStreamable& params) {
for (const auto& param : params) {
appendParam(contact, param);
}
}
template <typename Streamable, typename = std::enable_if_t<type::is_streamable<Streamable>>>
static void appendParam(std::ostream& contact, const Streamable& param) {
contact << ';' << param;
}
su_home_t mHome[1]{};
};
} // namespace sofiasip

View file

@ -0,0 +1,170 @@
/*
Flexisip, a flexible SIP proxy server with media capabilities.
Copyright (C) 2010-2024 Belledonne Communications SARL.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero 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 Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <algorithm>
#include <ostream>
#include <set>
#include <string>
#include <string_view>
#include <sofia-sip/msg_addr.h>
#include <sofia-sip/msg_types.h>
#include <sofia-sip/sip_protos.h>
#include <sofia-sip/su_alloc.h>
#include <bctoolbox/ownership.hh>
#include "flexisip/sip-boolean-expressions.hh"
#include "sip-header.hh"
using namespace ownership;
namespace sofiasip {
/**
* Don't forget to update MsgSip::getPreviousPriority and MsgSip::getOrderedPrioritiesList if you update this enum.
*/
enum class MsgSipPriority { NonUrgent = 0, Normal = 1, Urgent = 2, Emergency = 3 };
class MsgSip {
public:
MsgSip() : mMsg{msg_create(sip_default_mclass(), 0)} {
}
MsgSip(Owned<msg_t>&& msg) : mMsg(std::move(msg)) {
}
MsgSip(BorrowedMut<msg_t> msg) : mMsg(msg_ref_create(msg)) {
}
MsgSip(MsgSip&& other) : mMsg(std::move(other.mMsg)) {
}
MsgSip(const MsgSip& other);
/**
* Construct a MsgSip parsing the string_view parameter.
*
* @throw Throw std::runtime_error if a parsing error occurred.
*/
MsgSip(int flags, std::string_view msg);
~MsgSip() noexcept {
msg_destroy(mMsg.take());
}
MsgSip& operator=(MsgSip&& other) {
mMsg = std::move(other.mMsg);
return *this;
}
Borrowed<msg_t> getMsg() const {
return {mMsg};
}
BorrowedMut<msg_t> getMsg() {
return mMsg.borrow();
}
const sip_t* getSip() const {
return (sip_t*)msg_object(mMsg);
}
sip_t* getSip() {
return (sip_t*)msg_object(mMsg);
}
su_home_t* getHome() {
return msg_home(static_cast<msg_t*>(mMsg.borrow()));
}
sockaddr* getSockAddr() {
return msg_addrinfo(mMsg.borrow())->ai_addr;
}
msg_header_t* findHeader(const std::string& name, bool searchUnknowns = false);
const msg_header_t* findHeader(const std::string& name, bool searchUnknowns = false) const {
return const_cast<MsgSip*>(this)->findHeader(name, searchUnknowns);
}
sip_method_t getSipMethod() const {
const auto rq = getSip()->sip_request;
return rq != nullptr ? rq->rq_method : sip_method_unknown;
}
std::string getCallID() const;
MsgSipPriority getPriority() const;
void serialize() {
msg_serialize(mMsg.borrow(), (msg_pub_t*)getSip());
}
std::string msgAsString() const;
std::string contextAsString() const;
bool isGroupChatInvite() const noexcept;
bool isChatService() noexcept;
/**
* Change the sip filter used by Flexisip to show or not request's body in logs.
*
* @param filterString string containing the name of the method.
* @throw invalid_argument if filterString is not valid, or empty.
*/
static void setShowBodyFor(const std::string& filterString);
static std::shared_ptr<flexisip::SipBooleanExpression>& getShowBodyForFilter() {
if (!sShowBodyFor) {
sShowBodyFor = flexisip::SipBooleanExpressionBuilder::get().parse("content-type == 'application/sdp'");
}
return sShowBodyFor;
}
/**
* Return the priority just before the one in parameter;
* @throw logic_error if current == MsgSipPriority::NonUrgent
*/
static MsgSipPriority getPreviousPriority(MsgSipPriority current);
static std::array<MsgSipPriority, 4> getOrderedPrioritiesList() {
return {MsgSipPriority::Emergency, MsgSipPriority::Urgent, MsgSipPriority::Normal, MsgSipPriority::NonUrgent};
};
/**
* Insert or add a SIP header in the SIP message.
* If the header already exists in the message and is to be unique, then the new header replace the old one.
* If the header already exists in the message and isn't to be unique, then the new header is inserted after
* or before the current headers according the kind of the header.
*/
void insertHeader(SipHeader&& header) {
su_home_move(getHome(), header.mHome.home());
msg_header_insert(mMsg.borrow(), nullptr, header.mNativePtr);
header.mNativePtr = nullptr;
}
/**
* Create and insert a header in a SIP message.
* @param HeaderT The header type.
* @param args The arguments to give to the header constructor.
*/
template <typename HeaderT, typename... ArgsT>
void makeAndInsert(ArgsT&&... args) {
insertHeader(HeaderT{std::forward<ArgsT>(args)...});
}
private:
// Private attributes
Owned<msg_t> mMsg{nullptr};
static std::shared_ptr<flexisip::SipBooleanExpression> sShowBodyFor;
};
std::ostream& operator<<(std::ostream& strm, const sofiasip::MsgSip& obj) noexcept;
}; // namespace sofiasip

View file

@ -0,0 +1,329 @@
/*
Flexisip, a flexible SIP proxy server with media capabilities.
Copyright (C) 2010-2024 Belledonne Communications SARL, All rights reserved.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero 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 Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "sofia-sip/sdp.h"
#include <memory>
#include <string_view>
#include <variant>
namespace sofiasip {
/** The error message associated with an SDP parser */
class SdpParsingError : public std::string_view {
public:
friend std::ostream& operator<<(std::ostream&, const SdpParsingError&);
};
/**
* SDP session or media attribute
* ("a=" line)
*/
class SdpAttribute : private ::sdp_attribute_t {
public:
/**
* An SDP attribute is just a bunch of pointers.
* The default copy constructor will be shallow and (very) unsafe.
* If you need a copy, write a dedicated function and think it through.
*/
SdpAttribute(const SdpAttribute&) = delete;
static SdpAttribute* wrap(::sdp_attribute_t*);
std::string_view name() const {
return a_name;
}
std::string_view value() const {
return a_value;
}
};
/**
* An abstraction to iterate over all members of a linked-list of ::sdp_attribute_t that match a given name
*
* This class is iterable.
*/
class SdpMediaAttributeFilter {
public:
class Iterator {
public:
explicit Iterator(::sdp_attribute_t* ptr, const char* name);
Iterator& operator++();
bool operator!=(const Iterator& other) const {
return mPtr != other.mPtr;
}
SdpAttribute& operator*() {
return *SdpAttribute::wrap(mPtr);
}
private:
const char* const mName;
::sdp_attribute_t* mPtr;
};
explicit SdpMediaAttributeFilter(::sdp_attribute_t* head, const char* name) : mHead(head), mName(name) {
}
Iterator begin() {
return Iterator(mHead, mName);
}
Iterator end() {
return Iterator(nullptr, mName);
}
private:
::sdp_attribute_t* mHead;
const char* const mName;
};
/**
* An abstraction to access a linked-list of ::sdp_attribute_t
*/
class SdpMediaAttributeList {
public:
explicit SdpMediaAttributeList(::sdp_attribute_t* head) : mHead(head){};
/** Find attributes matching given name. */
SdpMediaAttributeFilter find(const char* name) {
return SdpMediaAttributeFilter(mHead, name);
}
private:
::sdp_attribute_t* mHead;
};
/**
* SDP connection - host or group address.
* ("c=" line)
*
* Some getters and setters are not yet implemented. They may be added later.
*/
class SdpConnection : private ::sdp_connection_t {
public:
using SofiaType = ::sdp_connection_t;
/**
* An SDP connection is just a bunch of pointers.
* The default copy constructor will be shallow and (very) unsafe.
* If you need a copy, write a dedicated function and think it through.
*/
SdpConnection(const SdpConnection&) = delete;
static SdpConnection* wrap(SofiaType*);
SdpConnection* next() {
return wrap(c_next);
}
// Host or group address
std::string_view address() const {
return c_address;
}
};
/**
* An abstraction to iterate over sofia's embedded linked-list pattern.
*
* This class is iterable.
*/
template <typename WrapperT>
class List {
public:
class Iterator {
public:
explicit Iterator(WrapperT* ptr) : mPtr(ptr) {
}
Iterator& operator++() {
mPtr = mPtr->next();
return *this;
}
bool operator==(const Iterator& other) const {
return mPtr == other.mPtr;
}
bool operator!=(const Iterator& other) const {
return mPtr != other.mPtr;
}
WrapperT& operator*() {
return *mPtr;
}
private:
WrapperT* mPtr;
};
explicit List(typename WrapperT::SofiaType* head) : mHead(WrapperT::wrap(head)) {
}
Iterator begin() {
return Iterator(mHead);
}
static Iterator end() {
return Iterator(nullptr);
}
private:
WrapperT* mHead;
};
/** Mapping from RTP payload to codec.
* ("a=rtpmap:")
*
* Defines a mapping from an RTP payload to a particular codec.
* In case of well-known payloads, the SdpRtpmap may be predefined, that is, generated by SDP parser without
* corresponding "a" line in the SDP. It may also* contain the @c fmtp attribute, which is used to convey
* format-specific parameters.
*/
class SdpRtpmap : private ::sdp_rtpmap_t {
public:
using SofiaType = ::sdp_rtpmap_t;
SdpRtpmap(const SdpRtpmap&) = delete;
static SdpRtpmap* wrap(SofiaType*);
SdpRtpmap* next() {
return wrap(rm_next);
}
// Codec name
auto encoding() const {
return std::string_view(rm_encoding);
}
// Sampling rate
unsigned long rate() const {
return rm_rate;
}
};
/** Media announcement.
* ("m=" line)
*
* This structure describes one media type, e.g., audio. The description
* contains the transport address (IP address and port) used for the group,
* the transport protocol used, the media formats or RTP payload types, and
* optionally media-specific bandwidth specification, encryption key and
* attributes.
*
* There is a pointer (m_user) for the application data, too.
*
* Some getters and setters are not yet implemented. They may be added later.
*/
class SdpMedia : private ::sdp_media_t {
public:
using SofiaType = ::sdp_media_t;
/**
* An SDP media is just a bunch of pointers.
* The default copy constructor will be shallow and (very) unsafe.
* If you need a copy, write a dedicated function and think it through.
*/
SdpMedia(const SdpMedia&) = delete;
static SdpMedia* wrap(SofiaType*);
SdpMedia* next() {
return wrap(m_next);
}
// Media attributes
SdpMediaAttributeList attributes() {
return SdpMediaAttributeList(m_attributes);
}
// List of addresses used
auto connections() {
return List<SdpConnection>(m_connections);
}
// List of RTP maps
auto rtpMaps() {
return List<SdpRtpmap>(m_rtpmaps);
}
// Media type name
std::string_view typeName() const {
return m_type_name;
}
};
/**
* SDP session description
*
* Created by `SdpParser`
*
* Some getters and setters are not yet implemented. They may be added later.
*/
class SdpSession : private ::sdp_session_t {
public:
/**
* An SDP session is just a bunch of pointers.
* The default copy constructor will be shallow and (very) unsafe.
* If you need a copy, write a dedicated function and think it through.
*/
SdpSession(const SdpSession&) = delete;
static SdpSession* wrap(::sdp_session_t*);
// Media descriptors
auto medias() {
return List<SdpMedia>(sdp_media);
}
// Group (or member) address
SdpConnection& connection();
};
/**
* A thin wrapper for SofiaSip's `sdp_parser` type.
*/
class SdpParser {
public:
struct Deleter {
void operator()(SdpParser*) noexcept;
};
using UniquePtr = std::unique_ptr<SdpParser, Deleter>;
// The flags list is not exhaustive. The other flags supported by sdp_parse may be added later.
enum class Flags : int {
None = 0,
Strict = ::sdp_parse_flags_e::sdp_f_strict,
};
// Create a new stand-alone SdpParser
static UniquePtr parse(std::string_view msg, Flags flags = Flags::None);
// Create a new SdpParser managed by a su_home_t
static SdpParser& parse(su_home_t&, std::string_view msg, Flags flags = Flags::None);
// Prevent creating instances of this class.
// Only references will be obtained via `reinterpret_cast`ing
SdpParser() = delete;
SdpParser(const SdpParser&) = delete;
SdpParser& operator=(const SdpParser&) = delete;
~SdpParser() = delete;
/** Retrieve an SDP session structure.
*
* @return
* Returns a reference to a parsed SDP message or, if an error has occurred, a string description of the error.
* The reference and all the data in the structure are valid until the SdpParser is destructed.
*/
std::variant<std::reference_wrapper<SdpSession>, SdpParsingError> session();
private:
// Bare wrapper to `sdp_parse`
static SdpParser* parse(su_home_t*, std::string_view msg, Flags flags);
// Retrieve the raw sofia pointer
::sdp_parser toSofia();
};
} // namespace sofiasip

View file

@ -0,0 +1,119 @@
/*
Flexisip, a flexible SIP proxy server with media capabilities.
Copyright (C) 2010-2024 Belledonne Communications SARL, All rights reserved.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero 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 Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <iostream>
#include <string>
#include <string_view>
#include <utility>
#include <sofia-sip/msg_header.h>
#include "flexisip/flexisip-exception.hh"
#include "flexisip/sofia-wrapper/home.hh"
namespace sofiasip {
/**
* Base class for all SIP header classes.
* It is actually a wrapper for the SofiaSip msg_header_t type.
* Every header are copy-constructable and move-constructable.
*/
class SipHeader {
public:
const msg_header_t* getNativePtr() const {
return mNativePtr;
}
protected:
friend class MsgSip;
SipHeader() = default;
SipHeader(const SipHeader& src) {
mNativePtr = msg_header_dup(mHome.home(), src.mNativePtr);
}
SipHeader(SipHeader&& src) noexcept {
mHome = std::move(src.mHome);
mNativePtr = src.mNativePtr;
src.mNativePtr = nullptr;
}
virtual ~SipHeader() = default;
template <typename HeaderT>
void setNativePtr(HeaderT* header) {
mNativePtr = reinterpret_cast<msg_header_t*>(header);
}
Home mHome{};
msg_header_t* mNativePtr{nullptr};
};
/**
* Represent a sofiasip @ref msg_param_t.
*/
class SipMsgParam {
public:
SipMsgParam() = delete;
/**
* Automatically parses the given parameter.
* The parameter should be formatted as "key=value".
*
* @throw flexisip::FlexisipException if the parameter is ill-formatted.
*/
explicit SipMsgParam(std::string_view param) : mParam(param) {
const auto delimiterPosition = mParam.find('=');
if (delimiterPosition == std::string::npos) {
throw flexisip::FlexisipException{R"(parameter is ill-formatted, missing "=" character ")" +
std::string{mParam} + "\""};
}
mKey = mParam.substr(0, delimiterPosition);
mValue = mParam.substr(delimiterPosition + 1, mParam.size());
}
SipMsgParam(const SipMsgParam& other) = default;
SipMsgParam(SipMsgParam&& other) = default;
/*
* Get raw parameter value.
*/
std::string_view getParam() const {
return mParam;
}
std::string_view getKey() const {
return mKey;
}
std::string_view getValue() const {
return mValue;
}
const char* str() const {
return mParam.data();
}
bool operator==(const SipMsgParam& other) const {
return mParam == other.mParam;
}
private:
std::string_view mParam;
std::string_view mKey;
std::string_view mValue;
};
} // namespace sofiasip

View file

@ -0,0 +1,87 @@
/*
Flexisip, a flexible SIP proxy server with media capabilities.
Copyright (C) 2010-2022 Belledonne Communications SARL.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero 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 Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <chrono>
#include <functional>
#include <list>
#include <stdexcept>
#include "flexisip/sofia-wrapper/timer.hh"
namespace sofiasip {
class SuRoot {
public:
using NativeDuration = std::chrono::duration<su_duration_t, std::milli>;
SuRoot() : mCPtr{su_root_create(nullptr)} {
if (mCPtr == nullptr) {
throw std::runtime_error{"su_root_t allocation failed"};
}
}
SuRoot(const SuRoot&) = delete;
~SuRoot() {
// Clear the list first because su_root_destroy free all timers, and lead to
// heap-use-after-free if done before the list destruction.
mOneShotTimerList.clear();
su_root_destroy(mCPtr);
}
su_root_t* getCPtr() const noexcept {
return mCPtr;
}
template <typename Duration>
auto step(Duration timeout) {
return NativeDuration(su_root_step(mCPtr, std::chrono::duration_cast<NativeDuration>(timeout).count()));
}
template <typename Duration>
auto sleep(Duration duration) {
return NativeDuration(su_root_sleep(mCPtr, std::chrono::duration_cast<NativeDuration>(duration).count()));
}
void run() {
su_root_run(mCPtr);
}
void quit() {
su_root_break(mCPtr);
}
_su_task_r getTask() const {
return su_root_task(mCPtr);
}
void addToMainLoop(std::function<void()>&& functionToAdd);
void addToMainLoop(const std::function<void()>& functionToAdd);
void addOneShotTimer(const std::function<void()>& timerFunction, NativeDuration ms);
template <typename Duration>
void addOneShotTimer(const std::function<void()>& timerFunction, Duration ms) {
addOneShotTimer(timerFunction, std::chrono::duration_cast<NativeDuration>(ms));
}
private:
static void mainLoopFunctionCallback(su_root_magic_t* rm, su_msg_r msg, void* u) noexcept;
static void mainLoopFunctionCallbackDeinitializer(su_msg_arg_t* data) noexcept;
::su_root_t* mCPtr{nullptr};
std::list<sofiasip::Timer> mOneShotTimerList{};
};
} // namespace sofiasip

View file

@ -0,0 +1,126 @@
/*
Flexisip, a flexible SIP proxy server with media capabilities.
Copyright (C) 2010-2025 Belledonne Communications SARL, All rights reserved.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero 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 Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <chrono>
#include <functional>
#include <memory>
#include <sofia-sip/su_wait.h>
namespace sofiasip {
class SuRoot;
/**
* @brief Helping class for manipulating SofiaSip's timers.
*/
class Timer {
public:
/**
* @brief Callbacks that is called when the timer expires.
*/
using Func = std::function<void()>;
using NativeDuration = std::chrono::duration<su_duration_t, std::milli>;
/**
* @brief Create a timer.
* @param[in] root SofiaSip's event loop.
* @param[in] intervalMs Default timer expiration interval in milliseconds.
* @throw std::logic_error if the timer couldn't been created.
*/
[[deprecated]] explicit Timer(su_root_t* root, su_duration_t intervalMs = 0);
[[deprecated]] Timer(su_root_t* root, NativeDuration interval) : Timer{root, interval.count()} {};
[[deprecated]] Timer(const sofiasip::SuRoot& root, NativeDuration interval);
[[deprecated]] explicit Timer(const std::shared_ptr<sofiasip::SuRoot>& root, su_duration_t intervalMs = 0);
explicit Timer(const std::shared_ptr<sofiasip::SuRoot>& root, NativeDuration interval);
~Timer();
/**
* Copying or moving a timer has no sense.
*/
Timer(const Timer&) = delete;
Timer(Timer&&) = delete;
/**
* @brief Start the timer with the default expiration time.
* @param[in] func The function to call when the timer expires. The
* context of the function is copied and automatically destroyed on
* timer expiration.
* @throw std::logic_error if the time couldn't be set.
*/
void set(const Func& func);
/**
* @brief Start the timer with a specific expiration time.
* @param[in] func The function to call when the timer expires. The
* context of the function is copied and automatically destroyed on
* timer expiration.
* @param[in] intervalMs The expiration time in ms.
* @throw std::logic_error if the timer couldn't been set.
*/
void set(const Func& func, su_duration_t intervalMs);
/**
* @brief Same as before, but using std::chrono for time interval.
*/
void set(const Func& func, NativeDuration interval) {
set(func, interval.count());
}
template <typename Duration>
void set(const Func& func, Duration interval) {
set(func, std::chrono::duration_cast<NativeDuration>(interval));
}
/**
* @brief Start the timer to be executed regularly.
* @param[in] func The function to call on each interval
* of time. The context of the function is copied and is
* only destroyed on reset() call.
* @throw std::logic_error if the timer couldn't be stated.
*
* Use with care as it will be called numerous times in case of time leap.
*/
void run(const Func& func);
/**
* @brief Same as run() except it doesn't try to catch up missed callbacks.
*/
void setForEver(const Func& func);
/**
* @brief Stop the timer and delete the internal function.
* @throw std::logic_error if the timer couldn't been reset.
*/
void reset();
/**
* @brief Check whether the timer has already been set.
*/
bool isRunning() const;
private:
static void _oneShotTimerCb(su_root_magic_t* magic, su_timer_t* t, su_timer_arg_t* arg) noexcept;
static void _regularTimerCb(su_root_magic_t* magic, su_timer_t* t, su_timer_arg_t* arg) noexcept;
std::shared_ptr<sofiasip::SuRoot> mRoot{};
su_timer_t* _timer = nullptr;
Func _func;
};
} // namespace sofiasip

View file

@ -0,0 +1,50 @@
/** Copyright (C) 2010-2023 Belledonne Communications SARL
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
#pragma once
#include <functional>
#include <memory>
#include <sofia-sip/su_wait.h>
namespace sofiasip {
/**
* Wraps a sofia wait register to unregister it on destruction
*/
class Waker {
public:
using Callback = std::function<int(su_root_magic_t*, su_wait_t*)>;
/** SAFETY:
* - `root` MUST NOT be null and MUST be valid for the lifetime of the Waker
*/
Waker(su_root_t* root, int fileDescriptor, Callback&& callback, int priority)
: mCallback(std::move(callback)), mRoot(root) {
su_wait_create(&mWait, fileDescriptor, SU_WAIT_IN);
su_root_register(
mRoot, &mWait,
[](su_root_magic_t* root, su_wait_t* wait, su_wakeup_arg_t* arg) noexcept {
auto& lambda = *static_cast<Callback*>(arg);
return lambda(root, wait);
},
&mCallback, priority);
}
~Waker() {
su_root_unregister(mRoot, &mWait, nullptr, &mCallback);
}
Waker(const Waker& other) = delete;
Waker& operator=(const Waker& other) = delete;
Waker(Waker&& other) = delete;
Waker& operator=(Waker&& other) = delete;
private:
Callback mCallback;
su_root_t* mRoot; // borrow
su_wait_t mWait{0};
};
} // namespace sofiasip

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