pax_global_header00006660000000000000000000000064137657221610014524gustar00rootroot0000000000000052 comment=13475d1df6799f5d937498c3fba62fc76e2384c9 liblinphone-4.4.21/000077500000000000000000000000001376572216100141175ustar00rootroot00000000000000liblinphone-4.4.21/.clang-format000066400000000000000000000036721376572216100165020ustar00rootroot00000000000000--- # BasedOnStyle: LLVM AccessModifierOffset: -2 AlignAfterOpenBracket: true AlignEscapedNewlinesLeft: false AlignOperands: true AlignTrailingComments: true AllowAllParametersOfDeclarationOnNextLine: true AllowShortBlocksOnASingleLine: false AllowShortCaseLabelsOnASingleLine: false AllowShortFunctionsOnASingleLine: false AllowShortIfStatementsOnASingleLine: false AllowShortLoopsOnASingleLine: false AlwaysBreakAfterDefinitionReturnType: false AlwaysBreakBeforeMultilineStrings: false AlwaysBreakTemplateDeclarations: false BinPackArguments: true BinPackParameters: true BreakBeforeBinaryOperators: None BreakBeforeBraces: Attach BreakBeforeTernaryOperators: true BreakConstructorInitializersBeforeComma: false ColumnLimit: 120 CommentPragmas: '^ IWYU pragma:' ConstructorInitializerAllOnOneLineOrOnePerLine: false ConstructorInitializerIndentWidth: 4 ContinuationIndentWidth: 4 Cpp11BracedListStyle: true DerivePointerAlignment: false DisableFormat: false ExperimentalAutoDetectBinPacking: false ForEachMacros: [ foreach, Q_FOREACH, BOOST_FOREACH ] IndentCaseLabels: false IndentFunctionDeclarationAfterType: false IndentWidth: 4 IndentWrappedFunctionNames: false KeepEmptyLinesAtTheStartOfBlocks: true Language: Cpp MaxEmptyLinesToKeep: 1 NamespaceIndentation: None ObjCBlockIndentWidth: 2 ObjCSpaceAfterProperty: false ObjCSpaceBeforeProtocolList: true PenaltyBreakBeforeFirstCallParameter: 19 PenaltyBreakComment: 300 PenaltyBreakFirstLessLess: 120 PenaltyBreakString: 1000 PenaltyExcessCharacter: 1000000 PenaltyReturnTypeOnItsOwnLine: 60 PointerAlignment: Right SpaceAfterCStyleCast: false SpaceBeforeAssignmentOperators: true SpaceBeforeParens: ControlStatements SpaceInEmptyParentheses: false SpacesBeforeTrailingComments: 1 SpacesInAngles: false SpacesInContainerLiterals: true SpacesInCStyleCastParentheses: false SpacesInParentheses: false SpacesInSquareBrackets: false Standard: Cpp11 TabWidth: 4 UseTab: Always ... liblinphone-4.4.21/.cproject000066400000000000000000000274001376572216100157340ustar00rootroot00000000000000 make -j4 CFLAGS="-g -Wall -Werror -Qunused-arguments" CXXFLAGS="-g" all true true true make -j4 CFLAGS="-g -Wall -Werror -Qunused-arguments" CXXFLAGS="-g" true true true make -j4 CFLAGS="-g -Wall -Werror -Qunused-arguments" CXXFLAGS="-g" all true true true make -j4 CFLAGS="-g -Wall -Werror -Qunused-arguments" CXXFLAGS="-g" all true true true liblinphone-4.4.21/.git-pre-commit000077500000000000000000000043501376572216100167620ustar00rootroot00000000000000#!/bin/bash # This hook purpose is to keep coding style consistent between all developers # It is automatically installed in .git/hooks folder by cmake on first run. # From https://github.com/tatsuhiro-t/nghttp2/blob/master/pre-commit function invalid-format-detected { cat git-clang-format.diff echo "*****************" echo "$0: Invalid coding style detected (see git-clang-format.diff for issues). Please correct it using one of the following:" echo "1) Apply patch located at git-clang-format.diff using:" echo " cd $(git rev-parse --show-toplevel) && $1" echo "2) Use clang-format to correctly format source code using:" echo " $2" echo "3) Reformat these lines manually." echo "*** Aborting commit.***" exit 1 } function git-clang-format-diffing { format_diff=$(which git-clang-format) format_diff_options="--style=file" #only diffing commited files, ignored staged one $format_diff $format_diff_options --diff $(git --no-pager diff --cached --name-status | grep -v '^D' | cut -f2) > git-clang-format.diff if ! grep -q -E '(no modified files to format|clang-format did not modify any files)' git-clang-format.diff; then invalid-format-detected "git apply git-clang-format.diff" "clang-format $format_diff_options -i " fi } function clang-format-diff-diffing { format_diff=$(find /usr/bin/ -name 'clang-format-diff*' -type f | tail -n1) format_diff_options="-style file" git diff-index --cached --diff-filter=ACMR -p HEAD -- | $format_diff $format_diff_options -p1 > git-clang-format.diff if [ -s git-clang-format.diff ]; then invalid-format-detected "patch -p0 < git-clang-format.diff" "${format_diff/-diff/} $format_diff_options -i " fi } set -e if which git-clang-format &>/dev/null; then git-clang-format-diffing $@ elif [ ! -z "$(find /usr/bin/ /usr/local/bin/ /opt/bin/ -name 'clang-format-diff*' -type f 2>/dev/null)" ]; then # Warning! We need at least version 1.6... clang-format-diff-diffing $@ else echo "$0: Please install clang-format (coding style checker) - could not find git-clang-format nor clang-format-diff in PATH. Skipping code verification..." exit 0 fi liblinphone-4.4.21/.gitignore000066400000000000000000000032351376572216100161120ustar00rootroot00000000000000Makefile Makefile.in aclocal.m4 autom4te.cache compile config.guess config.h config.h.in config.log config.status config.sub configure depcomp install-sh intltool-extract intltool-merge intltool-update libtool ltmain.sh missing mkinstalldirs speex stamp-h1 linphone.spec linphone.iss msx264.iss *.o *.exe *.zip *~ linphone-install buddylookup-install msx264-install *.tar.gz intltool-extract.in intltool-merge.in intltool-update.in INSTALL Specfile .anjuta/ .anjuta_sym_db.db gtk-glade/version_date.h share/linphone.desktop share/audio-assistant.desktop Debug/ build/macos/Info-linphone.plist coreapi/help/Doxyfile coreapi/help/buddy_status coreapi/help/chatroom coreapi/help/helloworld coreapi/help/registration coreapi/help/realtimetext_receiver coreapi/help/realtimetext_sender coreapi/test_ecc coreapi/test_lsd gtk/version_date.h daemon/linphone-daemon daemon/linphone-daemon-pipetest *.la *.lo *.deps *.libs coreapi/test_numbers specs.c *.orig *.rej *.kdev4 *.swp .deps .libs tools/test_ecc tools/test_lsd tools/test_numbers coreapi/help/notify tester/liblinphone_tester tools/lp-gen-wrappers tools/lpc2xml_test tools/xml2lpc_test coreapi/help/filetransfer tester/receive_file.dump tester/tmp.db .DS_Store Linphone.app *.dmg tester/linphone*.log tester/linphone_log.txt .tx/linphone-gtk.linphonedesktopin/ po/linphone.pot .tx/linphone-gtk.audio-assistantdesktopin/ tester/linphone_log.gz.txt tools/auto_answer tools/lp-autoanswer build/macos/pkg-distribution.xml record_for_lc_*.wav tester/record-call_with_file_player.wav tester/ZIDCache*.xml tester/stereo-record.wav .dirstamp git-clang-format.diff *.log .bc_tester_utils.tmp tools/lp-test-ecc tools/lp-sendmsg *.pyc liblinphone.spec liblinphone-4.4.21/.project000066400000000000000000000044651376572216100155770ustar00rootroot00000000000000 linphone org.eclipse.cdt.managedbuilder.core.genmakebuilder clean,full,incremental, ?name? org.eclipse.cdt.make.core.append_environment true org.eclipse.cdt.make.core.autoBuildTarget all org.eclipse.cdt.make.core.buildArguments CFLAGS="-g -Werror -Wall" CXXFLAGS="-g" V=1 org.eclipse.cdt.make.core.buildCommand make org.eclipse.cdt.make.core.cleanBuildTarget clean org.eclipse.cdt.make.core.contents org.eclipse.cdt.make.core.activeConfigSettings org.eclipse.cdt.make.core.enableAutoBuild false org.eclipse.cdt.make.core.enableCleanBuild true org.eclipse.cdt.make.core.enableFullBuild true org.eclipse.cdt.make.core.fullBuildTarget all org.eclipse.cdt.make.core.stopOnError true org.eclipse.cdt.make.core.useDefaultBuildCmd false org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder org.eclipse.cdt.core.cnature org.eclipse.cdt.core.ccnature org.eclipse.cdt.managedbuilder.core.managedBuildNature org.eclipse.cdt.managedbuilder.core.ScannerConfigNature liblinphone-4.4.21/CHANGELOG.md000066400000000000000000000052151376572216100157330ustar00rootroot00000000000000# Changelog All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). # Preamble This changelog file was started on October 2019. Previous changes were more or less tracked in the *NEWS* file. ## [Unreleased] ### Added - New method linphone_core_audio_route_changed(), to fix audio issues when switching audio to some low sample rate Bluetooth devices. ## [4.4.0] 2020-06-16 ### Added - Simplified integration with CallKit, see https://wiki.linphone.org/xwiki/wiki/public/view/Lib/Getting%20started/iOS/#HCallKitIntegration - Specific additions to take into account new iOS 13 constraints, see https://wiki.linphone.org/xwiki/wiki/public/view/Lib/Getting%20started/iOS/iOS13%20Migration%20guide/ * compatibility for inclusion within an app extension * helper functions to create a LinphoneCore that can be shared between an app and its extension * helper functions to process remote push notifications announcing IM messages - RTP bundle mode feature according to https://tools.ietf.org/html/draft-ietf-mmusic-sdp-bundle-negotiation-54, providing increased interoperability with WebRTC. - TURN over TCP or TLS - Ephemeral IM - let user program automatic destruction of IM messages. - SIP Session-Timers (RFC4028) - New method linphone_core_stop_async(), to ensure clean shutdown can be done without blocking main thread. ### Changed - Big internal refactoring of how streams are managed within offer/answer exchanges, in order to facilitate bundle mode implementation, as well as development of new features. - ICE now uses all IP addresses detected on the host (except IPv6 temporary ones) - Better handling of parameter changes in streams during the session, which avoids unecessary restarts. - Do not notify phone number being too short in account manager anymore, our dial plan isn't precise enough to garanty phone number is invalid in this case - Swift wrapper is no longer compiled due to its limited binary compatibility. It is instead exported as source code to be compiled with the application making use of it. ### Fixed - Internal refactoring of management of locally played tones, in order to fix race conditions. - Issues when handling SDP offer with only text stream. - Random file transfer issues when used with LIME. ## [4.3.0] - 2019-10-14 ### Added - New cmake options to make "small" builds of liblinphone, by excluding adavanced IM and DB storage. ### Changed - Optimisations in chatrooms loading from Sqlite DB, improving startup time. - License changed to GNU GPLv3. liblinphone-4.4.21/CMakeLists.txt000066400000000000000000000335501376572216100166650ustar00rootroot00000000000000############################################################################ # CMakeLists.txt # Copyright (C) 2010-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. # ############################################################################ cmake_minimum_required(VERSION 3.1) project(linphone LANGUAGES C CXX) file(GLOB LINPHONE_PO_FILES RELATIVE "${CMAKE_CURRENT_LIST_DIR}/po" "${CMAKE_CURRENT_LIST_DIR}/po/*.po") string(REGEX REPLACE "([a-zA-Z_]+)\\.po" "\\1" LINPHONE_ALL_LANGS_LIST "${LINPHONE_PO_FILES}") string(REPLACE ";" " " LINPHONE_ALL_LANGS "${LINPHONE_ALL_LANGS_LIST}") include(CMakeDependentOption) include(cmake/Tools.cmake) option(ENABLE_SHARED "Build shared library." YES) option(ENABLE_STATIC "Build static library." YES) option(ENABLE_ADVANCED_IM "Enable advanced instant messaging such as group chat." YES) option(ENABLE_CONSOLE_UI "Turn on or off compilation of console interface." YES) option(ENABLE_CSHARP_WRAPPER "Build the C# wrapper for Liblinphone." OFF) option(ENABLE_CXX_WRAPPER "Build the C++ wrapper for Liblinphone." YES) option(ENABLE_DB_STORAGE "Enable database storage." YES) option(ENABLE_SWIFT_WRAPPER "Build the swift wrapper sources for Liblinphone." OFF) option(ENABLE_SWIFT_WRAPPER_COMPILATION "Compile and package the swift wrapper framework from the built sources." OFF) option(ENABLE_JAZZY_DOC "Build the jazzy doc for swift module of Liblinphone." OFF) option(ENABLE_DAEMON "Enable the linphone daemon interface." YES) option(ENABLE_DATE "Use build date in internal version number." NO) option(ENABLE_DEBUG_LOGS "Turn on or off debug level logs." NO) option(ENABLE_DOC "Enable API documentation generation." NO) option(ENABLE_LIME_X3DH "Enable LIMEv2 and X3DH encryption protocol." YES) option(ENABLE_JAVA_WRAPPER "Build the Java wrapper for Liblinphone." OFF) option(ENABLE_JAVADOC "Add a target to generate documentation for Java API" NO) option(ENABLE_LDAP "Enable LDAP support." NO) option(ENABLE_LIME "Enable Instant Messaging Encryption." YES) option(ENABLE_RELATIVE_PREFIX "Find resources relatively to the installation directory." NO) option(ENABLE_STRICT "Build with strict compile options." YES) option(ENABLE_TOOLS "Turn on or off compilation of tools." YES) option(ENABLE_TUNNEL "Turn on compilation of tunnel support." NO) option(ENABLE_TUTORIALS "Enable compilation of tutorials." YES) option(ENABLE_UNIT_TESTS "Enable compilation of unit tests." YES) option(ENABLE_UPDATE_CHECK "Enable update check." NO) option(ENABLE_VCARD "Turn on compilation of vcard4 support." YES) option(ENABLE_VIDEO "Build with video support." YES) option(ENABLE_ASSETS "Package sound assets." YES) option(ENABLE_PACKAGE_SOURCE "Create 'package_source' target for source archive making (CMake >= 3.11)" OFF) cmake_dependent_option(ENABLE_NOTIFY "Enable libnotify support." YES "ENABLE_GTK_UI;NOT APPLE" NO) cmake_dependent_option(ENABLE_ASSISTANT "Turn on assistant compiling." YES "ENABLE_GTK_UI" NO) set(CMAKE_CXX_STANDARD 11) set(CMAKE_CXX_EXTENSIONS NO) if(NOT CMAKE_BUILD_TYPE) set(CMAKE_BUILD_TYPE "Release") endif() if(CMAKE_BUILD_TYPE STREQUAL "Debug") add_definitions("-DDEBUG") endif() if(ENABLE_STATIC) set(LINPHONE_LIBS_FOR_TOOLS linphone-static) else() set(LINPHONE_LIBS_FOR_TOOLS linphone) endif() if(WIN32 AND NOT CMAKE_SYSTEM_NAME STREQUAL "WindowsStore") list(APPEND LINPHONE_LIBS_FOR_TOOLS "Ws2_32" "Iphlpapi") endif() list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake") include(CheckSymbolExists) include(CMakePushCheckState) include(GNUInstallDirs) include(CheckCXXCompilerFlag) check_symbol_exists(getifaddrs "sys/types.h;ifaddrs.h" HAVE_GETIFADDRS) 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(MSVC_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/include/MSVC") if(MSVC) list(APPEND CMAKE_REQUIRED_INCLUDES "${MSVC_INCLUDE_DIR}") endif() # find_package should be invoked here to check for libraries - however do NOT # call include_directories here (see below) if (ENABLE_VCARD) find_package(belcard CONFIG) if(NOT BELCARD_FOUND) message(WARNING "Could not find the belcard library!") set(ENABLE_VCARD OFF CACHE BOOL "Enable vcard support." FORCE) else() add_definitions(-DVCARD_ENABLED) endif() endif() find_package(BelleSIP CONFIG REQUIRED) find_package(Mediastreamer2 CONFIG REQUIRED) find_package(ortp CONFIG REQUIRED) find_package(bctoolbox 0.0.3 CONFIG REQUIRED OPTIONAL_COMPONENTS tester) find_package(belr CONFIG REQUIRED) # the version shuold be defined here because bctoolbox is required bc_compute_lib_version(LIB_VERSION "4.4.0") project(linphone VERSION ${LIB_VERSION}) set(LINPHONE_MAJOR_VERSION ${PROJECT_VERSION_MAJOR}) set(LINPHONE_MINOR_VERSION ${PROJECT_VERSION_MINOR}) set(LINPHONE_MICRO_VERSION ${PROJECT_VERSION_PATCH}) set(LINPHONE_VERSION ${PROJECT_VERSION}) set(LINPHONE_SO_VERSION "10") #incremented for 4.4.0 release. if(ENABLE_ADVANCED_IM) find_package(LibXsd REQUIRED) set(HAVE_ADVANCED_IM 1) endif() find_package(Sqlite3 REQUIRED) find_package(XML2 REQUIRED) if (ENABLE_DB_STORAGE) #APPLE platform does not use dlopen for soci backend if (APPLE OR ANDROID) find_package(Soci REQUIRED COMPONENTS sqlite3) else() find_package(Soci REQUIRED) endif() set(HAVE_DB_STORAGE 1) endif() find_package(ZLIB) if(ENABLE_TUNNEL) if(LINPHONE_BUILDER_GROUP_EXTERNAL_SOURCE_PATH_BUILDERS) include("${EP_tunnel_CONFIG_DIR}/TunnelConfig.cmake") else() find_package(Tunnel) endif() if(NOT TUNNEL_FOUND) message(WARNING "Could not find the tunnel library!") set(ENABLE_TUNNEL OFF CACHE BOOL "Enable tunnel support." FORCE) endif() endif() if(ENABLE_NOTIFY) find_package(Notify) if(NOTIFY_FOUND) set(HAVE_NOTIFY4 1) else() message(WARNING "Could not find the notify library!") set(ENABLE_NOTIFY OFF CACHE BOOL "Enable libnotify support." FORCE) endif() endif() if(ENABLE_ASSISTANT) set(BUILD_WIZARD 1) endif() if(ENABLE_LIME AND ENABLE_LIME_X3DH) message(WARNING "Lime V1 and V2 cannot be used simultaneously!") endif() if(ENABLE_LIME_X3DH) find_package(lime REQUIRED CONFIG) set(HAVE_LIME_X3DH TRUE) endif() if(ENABLE_LIME) #bzrtp is only required for LIME find_package(bzrtp REQUIRED CONFIG) set(HAVE_LIME 1) endif() if(ENABLE_CXX_WRAPPER OR ENABLE_CSHARP_WRAPPER OR ENABLE_JAVA_WRAPPER OR ENABLE_SWIFT_WRAPPER OR ENABLE_DOC) find_package(PythonInterp REQUIRED) check_python_module(pystache) check_python_module(six) if(ENABLE_DOC) check_python_module(sphinx) check_python_module(javasphinx) check_python_module(sphinx_csharp) #check_python_module(swift_domain) endif() endif() if(UNIX AND NOT APPLE) include(CheckIncludeFiles) check_include_files(libudev.h HAVE_LIBUDEV_H) endif() if(MSVC AND NOT CMAKE_SYSTEM_NAME STREQUAL "WindowsPhone" AND NOT CMAKE_SYSTEM_NAME STREQUAL "WindowsStore") find_library(LIBGCC NAMES gcc) find_library(LIBMINGWEX NAMES mingwex) endif() if(NOT WIN32) find_package(Iconv QUIET) endif() if(ANDROID) find_package(CpuFeatures REQUIRED) if(CMAKE_ANDROID_NDK_VERSION VERSION_LESS 19) find_library(ANDROID_SUPPORT_LIBRARY NAMES support) endif() endif() set(LINPHONE_LDFLAGS "${BELLESIP_LDFLAGS} ${MEDIASTREAMER2_LDFLAGS}") # include_directories must be called only UNDER THIS LINE in order to use our # projects submodules first (we do NOT want to have system headers in first position) include_directories( coreapi include src ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_BINARY_DIR}/coreapi/ ) set(LINPHONE_INCLUDE_DIRS ${BELLESIP_INCLUDE_DIRS} ${MEDIASTREAMER2_INCLUDE_DIRS} ${BCTOOLBOX_CORE_INCLUDE_DIRS} ${BELR_INCLUDE_DIRS} ${LIBXSD_INCLUDE_DIRS} ) if(ANDROID) include_directories(${CMAKE_CURRENT_BINARY_DIR}/java) endif() if(ENABLE_TUNNEL) list(APPEND LINPHONE_INCLUDE_DIRS ${TUNNEL_INCLUDE_DIRS}) endif() list(APPEND LINPHONE_INCLUDE_DIRS ${XML2_INCLUDE_DIRS}) list(APPEND LINPHONE_INCLUDE_DIRS ${SQLITE3_INCLUDE_DIRS}) if(ZLIB_FOUND) list(APPEND LINPHONE_INCLUDE_DIRS ${ZLIB_INCLUDE_DIRS}) set(HAVE_ZLIB 1) endif() if(INTL_FOUND) set(HAVE_INTL 1) list(APPEND LINPHONE_INCLUDE_DIRS ${INTL_INCLUDE_DIRS}) endif() if(MSVC) include_directories(${MSVC_INCLUDE_DIR}) endif() set(LINPHONE_CPPFLAGS ${BELCARD_CPPFLAGS} ${BELLESIP_CPPFLAGS} ${MEDIASTREAMER2_CPPFLAGS} ${BCTOOLBOX_CPPFLAGS} ${BELR_CPPFLAGS}) if(ENABLE_STATIC) list(APPEND LINPHONE_CPPFLAGS "-DLINPHONE_STATIC") endif() if(LINPHONE_CPPFLAGS) list(REMOVE_DUPLICATES LINPHONE_CPPFLAGS) add_definitions(${LINPHONE_CPPFLAGS}) endif() if(ENABLE_DEBUG_LOGS) add_definitions("-DDEBUG_LOGS") endif() # Enable stdint.h limit macros on C++ files. (Windows only.) if(MSVC) add_definitions("-D__STDC_LIMIT_MACROS") endif() set(STRICT_OPTIONS_CPP ) set(STRICT_OPTIONS_C ) set(STRICT_OPTIONS_CXX ) set(STRICT_OPTIONS_OBJC ) if(MSVC) list(APPEND STRICT_OPTIONS_CPP "/wd4995") # Disable "name was marked as #pragma deprecated" warnings list(APPEND STRICT_OPTIONS_CPP "/wd4996") # Disable deprecated function warnings list(APPEND STRICT_OPTIONS_CPP "/wd4800") # Disable warning for cast from bool_t to bool if(ENABLE_STRICT) list(APPEND STRICT_OPTIONS_CPP "/WX") endif() else() list(APPEND STRICT_OPTIONS_CPP "-Wall" "-Wconversion" "-Werror=return-type" "-Winit-self" "-Wno-error=deprecated-declarations" "-Wpointer-arith" "-Wuninitialized" "-Wunused" ) list(APPEND STRICT_OPTIONS_CXX "-Wnon-virtual-dtor" "-Woverloaded-virtual" ) CHECK_CXX_COMPILER_FLAG("-Wsuggest-override" SUGGEST_OVERRIDE) if (SUGGEST_OVERRIDE) list(APPEND STRICT_OPTIONS_CXX "-Wsuggest-override" "-Wno-error=suggest-override" ) endif () list(APPEND STRICT_OPTIONS_C "-Wstrict-prototypes" "-Werror=strict-prototypes") if(CMAKE_C_COMPILER_ID STREQUAL "GNU") list(APPEND STRICT_OPTIONS_C "-fno-inline-small-functions") endif() if(CMAKE_C_COMPILER_ID MATCHES "Clang") list(APPEND STRICT_OPTIONS_CPP "-Qunused-arguments" "-Wno-array-bounds") endif() if(CMAKE_CXX_COMPILER_ID MATCHES "Clang") list(APPEND STRICT_OPTIONS_CXX "-x c++") endif() if(APPLE) list(APPEND STRICT_OPTIONS_CPP "-Wno-error=unknown-warning-option" "-Qunused-arguments" "-Wno-tautological-compare" "-Wno-unused-function" "-Wno-array-bounds") endif() if(ENABLE_STRICT) list(APPEND STRICT_OPTIONS_C "-Werror" "-Wextra" "-Wno-unused-parameter" "-Wno-missing-field-initializers" "-fno-strict-aliasing") list(APPEND STRICT_OPTIONS_CPP "-Werror" "-Wextra" "-Wno-unused-parameter" "-Wno-missing-field-initializers" "-fno-strict-aliasing") endif() endif() if(STRICT_OPTIONS_CPP) list(REMOVE_DUPLICATES STRICT_OPTIONS_CPP) endif() if(STRICT_OPTIONS_C) list(REMOVE_DUPLICATES STRICT_OPTIONS_C) endif() set(GETTEXT_PACKAGE "liblinphone") if(ENABLE_RELATIVE_PREFIX) set(LINPHONE_DATA_DIR ".") else() set(LINPHONE_DATA_DIR "${CMAKE_INSTALL_PREFIX}") endif() set(LINPHONE_PLUGINS_DIR "${LINPHONE_DATA_DIR}/${CMAKE_INSTALL_LIBDIR}/liblinphone/plugins") if(WIN32) set(LINPHONE_CONFIG_DIR "Linphone") endif() set(PACKAGE_LOCALE_DIR "${LINPHONE_DATA_DIR}/${CMAKE_INSTALL_DATADIR}/locale") set(PACKAGE_DATA_DIR "${LINPHONE_DATA_DIR}/${CMAKE_INSTALL_DATADIR}") set(PACKAGE_GRAMMAR_DIR "${LINPHONE_DATA_DIR}/${CMAKE_INSTALL_DATADIR}/belr/grammars") set(PACKAGE_SOUND_DIR "${LINPHONE_DATA_DIR}/${CMAKE_INSTALL_DATADIR}/sounds/linphone") set(PACKAGE_RING_DIR "${PACKAGE_SOUND_DIR}/rings") set(PACKAGE_FREEDESKTOP_DIR "${PACKAGE_DATA_DIR}/applications") configure_file(${CMAKE_CURRENT_SOURCE_DIR}/config.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config.h) set_source_files_properties(${CMAKE_CURRENT_BINARY_DIR}/config.h PROPERTIES GENERATED ON) add_definitions(-DHAVE_CONFIG_H) if(ENABLE_VIDEO) add_definitions(-DVIDEO_ENABLED) endif() if(LINPHONE_BUILDER_GROUP_EXTERNAL_SOURCE_PATH_BUILDERS) set(EXPORT_TARGETS_NAME "LinphoneBuilder") else() set(EXPORT_TARGETS_NAME "Linphone") endif() add_subdirectory(include) add_subdirectory(java) if(ENABLE_JAVA_WRAPPER) add_subdirectory(wrappers/java) endif() add_subdirectory(coreapi) add_subdirectory(src) add_subdirectory(share) if(ENABLE_CONSOLE_UI) add_subdirectory(console) endif() if(ENABLE_DAEMON) add_subdirectory(daemon) endif() if(ENABLE_TOOLS) add_subdirectory(tools) endif() if(ENABLE_UNIT_TESTS) add_subdirectory(tester) endif() if(ENABLE_CXX_WRAPPER) add_subdirectory(wrappers/cpp) endif() if(ENABLE_CSHARP_WRAPPER) add_subdirectory(wrappers/csharp) endif() if(ENABLE_SWIFT_WRAPPER) add_subdirectory(wrappers/swift) endif() include(CMakePackageConfigHelpers) set(CONFIG_PACKAGE_LOCATION "${CMAKE_INSTALL_DATADIR}/Linphone/cmake") write_basic_package_version_file( "${CMAKE_CURRENT_BINARY_DIR}/LinphoneConfigVersion.cmake" VERSION ${LINPHONE_VERSION} COMPATIBILITY AnyNewerVersion ) export(EXPORT ${EXPORT_TARGETS_NAME}Targets FILE "${CMAKE_CURRENT_BINARY_DIR}/LinphoneTargets.cmake" ) configure_package_config_file(cmake/LinphoneConfig.cmake.in "${CMAKE_CURRENT_BINARY_DIR}/LinphoneConfig.cmake" INSTALL_DESTINATION ${CONFIG_PACKAGE_LOCATION} NO_SET_AND_CHECK_MACRO ) install(EXPORT ${EXPORT_TARGETS_NAME}Targets FILE LinphoneTargets.cmake DESTINATION ${CONFIG_PACKAGE_LOCATION} ) install(FILES "${CMAKE_CURRENT_BINARY_DIR}/LinphoneConfig.cmake" "${CMAKE_CURRENT_BINARY_DIR}/LinphoneConfigVersion.cmake" DESTINATION ${CONFIG_PACKAGE_LOCATION} ) if (ENABLE_PACKAGE_SOURCE) add_subdirectory(build) endif() liblinphone-4.4.21/LICENSE.txt000066400000000000000000000773311376572216100157550ustar00rootroot00000000000000 GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The GNU General Public License is a free, copyleft license for software and other kinds of works. The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things. To protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others. For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it. For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions. Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users. Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free. The precise terms and conditions for copying, distribution and modification follow. TERMS AND CONDITIONS 0. Definitions. "This License" refers to version 3 of the GNU General Public License. "Copyright" also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. "The Program" refers to any copyrightable work licensed under this License. Each licensee is addressed as "you". "Licensees" and "recipients" may be individuals or organizations. To "modify" a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a "modified version" of the earlier work or a work "based on" the earlier work. A "covered work" means either the unmodified Program or a work based on the Program. To "propagate" a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. To "convey" a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. An interactive user interface displays "Appropriate Legal Notices" to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. 1. Source Code. The "source code" for a work means the preferred form of the work for making modifications to it. "Object code" means any non-source form of a work. A "Standard Interface" means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. The "System Libraries" of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A "Major Component", in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. The "Corresponding Source" for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. The Corresponding Source for a work in source code form is that same work. 2. Basic Permissions. All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. 3. Protecting Users' Legal Rights From Anti-Circumvention Law. No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. 4. Conveying Verbatim Copies. You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. 5. Conveying Modified Source Versions. You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: a) The work must carry prominent notices stating that you modified it, and giving a relevant date. b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to "keep intact all notices". c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an "aggregate" if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. 6. Conveying Non-Source Forms. You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. A "User Product" is either (1) a "consumer product", which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, "normally used" refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. "Installation Information" for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. 7. Additional Terms. "Additional permissions" are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or d) Limiting the use for publicity purposes of names of licensors or authors of the material; or e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. All other non-permissive additional terms are considered "further restrictions" within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. 8. Termination. You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. 9. Acceptance Not Required for Having Copies. You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. 10. Automatic Licensing of Downstream Recipients. Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. An "entity transaction" is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. 11. Patents. A "contributor" is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's "contributor version". A contributor's "essential patent claims" are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, "control" includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. In the following three paragraphs, a "patent license" is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To "grant" such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. "Knowingly relying" means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. A patent license is "discriminatory" if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. 12. No Surrender of Others' Freedom. If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. 13. Use with the GNU Affero General Public License. Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such. 14. Revised Versions of this License. The Free Software Foundation may publish revised and/or new versions of the GNU General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation. If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. 15. Disclaimer of Warranty. THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. Limitation of Liability. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 17. Interpretation of Sections 15 and 16. If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. END OF TERMS AND CONDITIONS liblinphone-4.4.21/NEWS000066400000000000000000000406331376572216100146240ustar00rootroot00000000000000-- DEPRECATED ON 2019-10-14 -- use CHANGELOG.md to track changes. linphone-3.12.0 -- July 21st, 2017 * C++ and C# wrappers. * Account Creator plugin API. * Xamarin support. linphone-3.11.1 -- March 2nd, 2017 * Bugfix in Autotools scripts linphone-3.11.0 -- February 24th, 2017 * Security vulnerability fix concering TLS. The common name of certificats delivered by SIP proxies were not compared with their hostname. * Improvement of H.264 hardware encoder/decoder on MacOSX. * Fix a bug in P2P presence system. Unchecking `Allow this contact to see my presence` checkbox in contact editing view had no any effect. Then it was not possible to hide presence informations to a contact once it had been allowed to see it. * Performance improvments of the presence list feature. * IM Encription Engine: abstraction for messaging encription protocols. linphone-3.10.2 -- August 30th, 2016 * Fixing linphone python version compilation - fixing vcards linphone-3.10.0 -- August 8th, 2016 * Adding lime_experimental_feature : set to 1 in .linphonerc [GtkUi] to show the Lime menu in the graphical user interface. Caution : Experimental. * Video conference support through a conference server (SDK only) * Disable dummy STUN packets sending when ICE is activated. * Signal AVPF support as AVP : Enable rtcp feedback on RTP/AVP by default * Adding linphone daemon * gtk - Show links to files received in chat by file transfer * gtk - Debug window now stores “scroll to end” preference * gtk - Added button to take screenshot of video call   * Fix - gtk : Fixed issue busy presence not displayed in red * Fix 0002832: Date/time of calls not shown in call history on Windows. * Fix 0002690: Bad SDP when no audio codec has been enabled * Fix 0000750: DTMF RFC2833 event always goes up in the same LinphoneCoreListener linphone-3.9.1 -- November 16th, 2015 * Fix crash when recording video calls with the VP8 codec * Fix H.264 codec support in Mac OS X package * Fix translation of account assistant * Bug fixes linphone-3.9.0 -- November 2nd, 2015 * Video recording of calls in MKV format * Clickable URLs in chat view * Add buttons to change the record and playback volumes during a call * Add button to start chatting without having to create a contact first * Some icon changes * Call logs now stored in database * Bug fixes linphone-3.8.5 -- June 30th, 2015 * Fix bug about status icon on MacOSX. Attention request worked only once * Fix crash at the end of the audio assistant * Fix crash when configuring a remote provisioning * Fix regression in the codec view. Codec which are not usable because bandwidth limits are to low were not greyed anymore * Fix language selection on Windows and MacOSX * Add translation for 'Arabic' and 'Turkish' items in the language selection list linphone-3.8.4 -- June 9th, 2015 * Add a built-in XMLRPC client. Linphone does not depend on libsoup anymore linphone-3.8.3 -- June 4th, 2015 * Fix status icons on all platforms (Windows, MacOS, non-KDE Linux desktop environment) linphone-3.8.2 -- May 7th, 2015 Application level improvements: * add support of the StatusNotifierItem standard to display a status icon on KDE5 * auto-answering can be set through the preferences panel * bug fixes Liblinphone level improvements: * fix audio bug with opus codec * fix ICE corner case not properly handled and resulting bad final ice status * update SO version to 7 (it should have been done in 3.8.0) * bug fixes linphone-3.8.1 -- March 31th, 2015 Application level improvements: * Auto-answer ability * Improvement of UI appearance on Mac OSX * Bug fixes linphone-3.8.0 -- March 11th, 2015 Application level improvements: * The video window has now controls in order to switch fullscreen mode and terminate call. * The out of call video preview feature (to test camera) is moved into the settings and is no longer linked to the in-call video preview feature. * Add an assistant to help users to set audio/video parameters * Some ergonomics improvments (checkbox to set random port for UDP and TCP, ...) * Lots of updated translations. Arabic translation has been added * Experimental feature: play an MKV file by drag-and-dropping it on the video call window Liblinphone level improvements: * Support for RTP/AVPF (RFC4585) for video streams, allowing fast transmission error recovery with VP8 codec only. * API enhancements, most objects can be ref-counted. * Add some getter funtctions to the call information API * Add a function in the API to accept early-media calls * Add a function to set the SIP transport timeout * Add a function to change adaptive rate algorithm at runtime * Add support of file transfer * Call video recording feature, in mkv format (H264 streams only for the moment) * Call playing feature: play an MKV file and send the audio/video stream through a call * Local player API. Play WAV and MKV file and display video on a specified window display * A wrapper for Python has been made * Support of Wake Locks on Android * Support of multicast IP addresses * Support of incoming UPDATEs within dialog (RFC3311) * Support of SRTP by using packages from GNU/Linux distributions linphone-3.7.0 -- February 20th, 2014 Application level improvements: * It is now possible to configure multiple proxy accounts with different transports (UDP, TCP, TLS) * can work with IPv6 and IPv4 simultaneously * User can choose video rendering method on Linux * Video HD formats support added, leveraging on multiple cores for encoding if available * Keyboard can be used for DTMF input * Faster and higly responsive UI thanks to fully asynchronous operation of the liblinphone. * Addon of opus codec * Possibility to specify a remote provisioning http URI for configuration * LDAP search integration for Linux and MacOSX * is-composing notification in chat area Liblinphone level improvements thanks to new "belle-sip" SIP stack: * multiple SIP transports simultaneously now allowed * IP dual stack: can use IPv6 and IPv4 simultaneously * fully asynchronous behavior: no more lengthly DNS or connections * +sip.instance parameter (RFC5626) * alias parameter (RFC5923) * better management of network disconnections * SIP/TLS handled through lightweighted polarssl library (instead of openssl) * SIP transaction state machines improved (RFC6026) * Privacy API (RFC3323, RFC3325) * Full support of rich presence in (RFC4480) * Better handling of sips scheme in URIs. * Messaging: support of is-composing (RFC3994) * Call transfer fixes in error cases * Add API for managing SIP SUBSCRIBES/NOTIFY/PUBLISH (linphonecore/event.h) * bugfixes Requires: mediastreamer2 = 2.10.0, ortp = 0.23.0, belle-sip = 1.3.0 linphone-3.6.1 -- June 17, 2013 * fix memory leak with some video cameras on windows. Requires: mediastreamer2 = 2.9.1 and ortp = 0.22.0 linphone-3.6.0 -- May 27, 2013 UI: * new friend list and chat messaging UI * enhanced call history * call and conference audio recording * persistent chat history * DSCP settings for SIP and RTP * display of call statistics (when clicking on the quality indicator bar) core: * ICE for efficient RTP exchange * fix bug in zRTP support (upgrade required) * call recording * uPnP * call statistics * adaptive bitrate control improvements * faster call quality indicator feedback * DSCP settings for SIP and RTP * detailed call statistics feedback API Requires: mediastreamer2 = 2.9.0 and ortp = 0.22.0 linphone-3.5.2 -- February 22, 2012 * updated oRTP to 0.20.0 * updated mediastreamer2 to 2.8.2 * added ZRTP media encryption * added SILK audio codec linphone-3.5.1 -- February 17, 2012 * gtk - implement friend search by typing into the friendlist, and friend sorting linphone-3.5.0 -- December 22, 2011 * added VP-8 video codec * added G722 audio codec * added SIP/TCP and SIP/TLS * added SRTP media encryption * Audio conferencing * UI: call history tab, menu simplified * UI: cosmetics for incall views * UI: integration with libnotify * UI: show registered SIP accounts * Fixes for MacOS X, and uses GtkQuartz engine linphone-3.4.3 -- March 28, 2011 * Fully ported to mac os x with gtk-osx (menu integration, bundle generation with "make bundle", sound I/O improved) but still audio only * Fix stupid warning "no response" that sometimes arrived at end of calls * limit the size of the log window (to prevent memory drain) * limit the size of the SDP message by removing unnecessary information (for well known codecs, for H264). This is to prevent SIP messages from being discarded by routers on the internet when they exceeds in size the internet MTU. * other sip bugfixes Requires mediastreamer-2.7.3 linphone-3.4.2 -- March 3rd, 2011 * fix problems with webcams on windows Requires mediastreamer-2.7.2 linphone-3.4.1 -- February 17th, 2011 * bugfixes * gtk executable is renamed "linphone" (was linphone-3 before) Requires mediastreamer-2.7.1 linphone-3.4.0 -- February 7th, 2011 * implement multiple calls feature: - call hold (with possibility to play a music file) - call resume - acceptance of 2nd call while putting the others on hold - creation of another outgoing call while already in call - blind call transfer - attended call transfer **CAUTION**: LinphoneCoreVTable has changed: pay attention to this when upgrading an old application to a newer liblinphone. * improve bandwidth management (one b=AS line is used for audio+video) * improvements in the echo limiter performance * implement a echo calibration feature (see linphone_core_start_echo_calibration()). * stun support bugfixes * possibility to use two video windows, one for local preview, one for remote video (linphonec only) * optimize by not re-creating streams when SDP is unchanged during a reinvite * support for sending early media * doxygen doc and javadoc improvements * based on mediastreamer-2.7.0, please refer to mediastreamer NEWS for changes. linphone-3.3.2 -- July 1st, 2010 * fix crash when setting firewall address in gtk interface * fix crash while closing video window on windows * fix un-sent BYE message in some rare cases. Requires: mediastreamer2-2.6.0 ortp-0.16.3 linphone-3.3.1 -- June 3, 2010 * fix bugs when carrying non ascii displaynames in SIP messages * fix crash when codecs are incompatible * fix bug with streams not restarted in case of reinvites Requires: mediastreamer2-2.5.0 ortp-0.16.3 linphone-3.3.0 -- May 19, 2010 * liblinphone is ported to iphoneOS and Google Android * Internal refactoring of liblinphone (code factorisation, encapsulation of signaling) * enhancements made to presence support (SIP/SIMPLE) * new icons * new tabbed ui * be nat friendly using OPTIONS request and using received,rport from responses. * use stun guessed ports even if symmetric is detected (works with freeboxes) * improve bitrate usage of speex codec * allow speex to run with vbr (variable bit rate) mode * add speex/32000 (ultra wide band speex codec) * answer empty SIP INFO requests * reverse order of call logs * optimize video display * manual or automatic network connectivity management (so that REGISTERs are only sent when network is up or refreshed when it comes back) linphone-3.2.1 -- October 5, 2009 * improve graphics and behaviour of mute button * updated translations * windows installer installs reg keys to indicate windows to start linphone clicking of sip uris * workaround a bug Gtk-macos X with modal popup windows, preventing to answer calls linphone-3.2.0 -- September 17, 2009 * new in-call layout * new idle view with two buttons * ability to dial the number from dialpad * improve local IP address detection when having multiple networks (ex: VPNs) * use proxy's received and rport params from via in Contact header when possible * port to mac os X leopard (using gtk/x11), audio only for now * DevC++ support now deprecated, use msys/mingw (see README.mingw for details) * add an option to ask linphone to place a call, whenever an instance is already running or not: should be useful for starting calls from a web browser recognizing the 'sip:' uri scheme. * french and italian translation updated * don't show ffmpeg codecs when encoder are disabled in ffmpeg library. * bugfixes in: - video4linux2 support - alsa support - socket leak in mtu discovery linphone-3.1.2 -- May 5, 2009 * make it work with lastest ffmpeg swscale * improve theora packer * update theora default settings to match performance of 1.0 release. * fix a random crash during video resizing on linux with SDL. linphone-3.1.1 -- April 14, 2009 * fix crash when opening property box, in some rare case * windows version uses the new libmsdscap plugin (video capture using directshow) * improved translations linphone-3.1.0 -- March 16, 2009 * linphone can now send large video pictures: up to SVGA, configurable via the user interface * automatic rescaling of the video windows to the video size of incoming stream * improved webcam support on windows * plenty of user interface cosmetic improvements * set a user friendly gtk theme by default on windows * linphonec can compile and work without libreadline * enable translations on windows * enable lookups of SRV records * new 'linphonecsh' program to send commands to a linphonec running as daemon using unix sockets or tcp. * bugfixing as usual linphone-3.0.0 -- October 13, 2008 * new graphical interface based on Glade/Gtk * systray icon * STUN working for RTP * fully ported to windows * accurate bandwidth management (to let video occupy all remaning bandwidth) * new H264 plugin based on x264 (msx264) * automatic call hangup upon media transmission faillure linphone-2.1.1 -- February 13, 2008 * fix interroperability bug with Asterisk about a BYE not sent by linphone. * fix alsa support weakness (capture underruns not recovered) linphone-2.1.0 -- January 25, 2008 * 4CIF support * enable resizing of video output * hu translation added linphone-2.0.1 -- November 30, 2007 * fix interop issue with asterisk * answer OPTIONS and other SIP messages * allow usage of ALSA user pcm devices with the sound->alsadev config item. linphone-2.0.0 -- November 15, 2007 * port to libeXosip2-3.0.x with libosip2-3.0.x * implements early media * implements incoming re-INVITE * presence support improvements * ipv6 working on windows * implements SDP over 200ok/ACK * add experimental snow codec support * answers to VFU request in SIP-INFO by sending an I-frame. * ffmpeg rate control enabled, improved mpeg4 quality for low bandwidths * separate video grabbing and display in linphonec linphone-1.7.1 -- April 16, 2007 * cz translation * compilation bugfixes (when video support is disabled) * fix IM icons path bug linphone-1.7.0 -- April 11, 2007 * new splash screen when no webcam is detected * new friend commands for linphonec * gnome interface becomes gtk-only * fix issue with codec bitrate settings when no bandwidth limits are given * open rtp sockets before sending SDP offer or answer (so that we don't miss the first I-frame) linphone-1.6.0 -- January 23, 2007 * Video4Linux V2 support with mjpeg cameras * use MPEG4 config string provided in the SDP (if any) * fix bug when choosing an invalid ring sound file * fix bug when using quickcam driver with CIF size * reduce audio bandwidth usage for <128kbit/s connections with video linphone-1.5.1 -- November 14, 2006 * fix translations linphone-1.5.0 -- October 11, 2006 * compliant theora support (using Luca Barbato's draft) * mpeg4 support (compliant with RFC3016) * controls to display and modify video codec list (gnome interface) * banwidth usage improvements * splash screen when no webcam is detected linphone-1.4.1 -- September 18, 2006 * fixes crash when attempting to make two simultaneous calls * fixes crash when having no soundcard * require theora>=1.0.0-alpha7 * do not allow resizing of the gnome interface * do not change mixer settings at startup linphone-1.4.0 -- September 11, 2006 * no more glib dependency at all * new mediastreamer2 framework for audio/video streaming * stable video support with H.263-1998 * echo cancelation * experimental theora support * jack support unported to mediastreamer2, sorry : volunteer needed * video resolution and framerate set according to bandwidth constraints. * linphonec new "soundcard" command to list and choose sound devices. * new download and upload bandwidth parameters used to find suitable video/audio codec choice and parameters. * new 'play' and 'record' functions to linphonec to play and record wav files * arts sound backend liblinphone-4.4.21/README.md000066400000000000000000000111641376572216100154010ustar00rootroot00000000000000[![pipeline status](https://gitlab.linphone.org/BC/public/linphone/badges/master/pipeline.svg)](https://gitlab.linphone.org/BC/public/linphone/commits/master) Liblinphone =========== Liblinphone is a high-level SIP library integrating all calling and instant messaging features into an unified easy-to-use API. It is the cross-platform VoIP library on which the *Linphone[1]* application is based on, and that anyone can use to add audio and video calls or instant messaging capabilities to an application. # License Copyright © Belledonne Communications Liblinphone is dual licensed, and is available either : - under a [GNU/GPLv3 license](https://www.gnu.org/licenses/gpl-3.0.en.html), for free (open source). Please make sure that you understand and agree with the terms of this license before using it (see LICENSE.txt file for details). - under a proprietary license, for a fee, to be used in closed source applications. Contact [Belledonne Communications](https://www.linphone.org/contact) for any question about costs and services. # Documentation - Supported features and RFCs : https://www.linphone.org/technical-corner/liblinphone/features - Multi-language API documentation : https://www.linphone.org/snapshots/docs/liblinphone/multilang/ - Liblinphone developer guide on Linphone public wiki: https://wiki.linphone.org/xwiki/wiki/public/view/Lib/ # Building liblinphone The *linphone-sdk[7]* git project is the **recommended** way to build liblinphone, as it bundles all required an optional dependencies to build liblinphone as git submodules. It has a top-level CMake build script that makes life easier. Here the main dependencies listed: * **BcToolbox[2]:** portability layer * **BelleSIP[3]:** SIP stack * **Mediastreamer2[4]:** multimedia engine * **Belcard[5]:** VCard4 support * **Belr** generic parsing engine for ABNF defined languages. * **libxml2** * **zlib** * **libsqlite3:** user data storage (can be disabled) * **python interpreter** and **pystache**, **six** python module (needed for C++/C#/Java wrappers and API documentation) * **doxygen** and **dot** (needed for C++ wrapper and API documentation) * **Bzrtp[6]:** zrtp stack used to secure calls * For API documentation generation: **sphinx**, **javasphinx**, **sphinx_csharp** python modules are needed. ## Build instructions (when used standalone, outside of linphone-sdk) cmake . -DCMAKE_INSTALL_PREFIX= -DCMAKE_PREFIX_PATH= make make install ## Supported build options * **`CMAKE_INSTALL_PREFIX=`** : install prefix * **`CMAKE_PREFIX_PATH=`** : column-separated list of prefixes where to search for dependencies * **`ENABLE_SHARED=NO`** : do not build the shared library * **`ENABLE_STATIC=NO`** : do not build the static library * **`ENABLE_STRICT=NO`** : build without strict compilation flags (-Wall -Werror) * **`ENABLE_DOC=YES`** : Make the reference documentation of liblinphone to generated * **`ENABLE_UNIT_TESTS=NO`** : do not build testing binaries * **`ENABLE_VCARD=NO`** : disable VCard4 support * **`ENABLE_TOOLS=NO`** : do not build tool binaries * **`ENABLE_LIME=NO`** : disable Linphone Instant Messaging Encryption ## Note for packagers Our CMake scripts may automatically add some paths into research paths of generated binaries. To ensure that the installed binaries are striped of any rpath, use `-DCMAKE_SKIP_INSTALL_RPATH=ON` while you invoke cmake. Rpm packaging liblinphone can be generated with cmake3 using the following commands: ``` mkdir WORK cd WORK cmake3 ../ make package_source rpmbuild -ta --clean --rmsource --rmspec liblinphone--.tar.gz ``` # Credits Belledonne Communications SARL, all rights reserved. # License This software is distributed under GNU GPLv3. Please read COPYING file for full license text. ------------------------------ - [1] Linphone: https://linphone.org/technical-corner/linphone - [2] bctoolbox: https://gitlab.linphone.org/BC/public/bctoolbox *or* - [3] belle-sip: https://gitlab.linphone.org/BC/public/belle-sip *or* - [4] mediastreamer2: https://gitlab.linphone.org/BC/public/mediastreamer2 *or* - [5] belcard: https://gitlab.linphone.org/BC/public/belcard *or* - [6] bzrtp: https://gitlab.linphone.org/BC/public/bzrtp *or* - [7] linphone-sdk https://gitlab.linphone.org/BC/public/linphone-sdk liblinphone-4.4.21/autogen.sh000077500000000000000000000047161376572216100161300ustar00rootroot00000000000000#!/bin/sh ## ## Copyright (c) 2010-2019 Belledonne Communications SARL. ## ## This file is part of Liblinphone. ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see . ## srcdir=`dirname $0` test -z "$srcdir" && srcdir=. THEDIR=`pwd` cd $srcdir #AM_VERSION="1.10" if ! type aclocal-$AM_VERSION 1>/dev/null 2>&1; then # automake-1.10 (recommended) is not available on Fedora 8 AUTOMAKE=automake ACLOCAL=aclocal else ACLOCAL=aclocal-${AM_VERSION} AUTOMAKE=automake-${AM_VERSION} fi LIBTOOLIZE="libtoolize" for lt in glibtoolize libtoolize15 libtoolize14 libtoolize13 ; do if test -x /usr/bin/$lt ; then LIBTOOLIZE=$lt ; break fi if test -x /usr/local/bin/$lt ; then LIBTOOLIZE=$lt ; break fi if test -x /opt/local/bin/$lt ; then LIBTOOLIZE=$lt ; break fi done if test -d /opt/local/share/aclocal ; then ACLOCAL_ARGS="-I /opt/local/share/aclocal" fi if test -d /share/aclocal ; then ACLOCAL_ARGS="$ACLOCAL_ARGS -I /share/aclocal" fi INTLTOOLIZE=$(which intltoolize) #workaround for mingw bug in intltoolize script. if test "$INTLTOOLIZE" = "/bin/intltoolize" ; then INTLTOOLIZE=/usr/bin/intltoolize fi echo "Generating build scripts in linphone..." set -x $LIBTOOLIZE --copy --force $INTLTOOLIZE -c --force --automake $ACLOCAL -I m4 $ACLOCAL_ARGS autoheader $AUTOMAKE --force-missing --add-missing --copy autoconf set +x #install git pre-commit hooks if possible if [ -d .git/hooks ] && [ ! -f .git/hooks/pre-commit ]; then cp .git-pre-commit .git/hooks/pre-commit chmod +x .git/hooks/pre-commit fi if [ "$srcdir" = "." ]; then if [ -x oRTP/autogen.sh ]; then echo "Generating build scripts in oRTP..." ( cd oRTP && ./autogen.sh ) fi if [ -x mediastreamer2/autogen.sh ]; then echo "Generating build scripts in mediastreamer2..." ( cd mediastreamer2 && ./autogen.sh ) fi fi cd $THEDIR liblinphone-4.4.21/build/000077500000000000000000000000001376572216100152165ustar00rootroot00000000000000liblinphone-4.4.21/build/CMakeLists.txt000066400000000000000000000025451376572216100177640ustar00rootroot00000000000000############################################################################ # CMakeLists.txt # Copyright (C) 2010-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. # ############################################################################ cmake_minimum_required(VERSION 3.11) # we need CMake 3.11 for defining 'package_source' target as custom target if(NOT CPACK_PACKAGE_NAME) set(CPACK_PACKAGE_NAME "liblinphone") endif() set(CPACK_SOURCE_IGNORE_FILES "${CMAKE_BINARY_DIR}" "^${PROJECT_SOURCE_DIR}/.git*" ) set(BC_SPECFILE_NAME "liblinphone.spec") bc_make_package_source_target() liblinphone-4.4.21/build/Makefile.am000066400000000000000000000000201376572216100172420ustar00rootroot00000000000000SUBDIRS=macos liblinphone-4.4.21/build/macos/000077500000000000000000000000001376572216100163205ustar00rootroot00000000000000liblinphone-4.4.21/build/macos/Info-linphone.plist.in000066400000000000000000000022221376572216100225050ustar00rootroot00000000000000 CFBundleDevelopmentRegion English CFBundleExecutable Linphone CFBundleGetInfoString @PACKAGE_VERSION@, (C) 2011 The linphone team http://www.linphone.org CFBundleIconFile linphone.icns CFBundleIdentifier org.linphone.linphone CFBundleInfoDictionaryVersion 6.0 CFBundlePackageType APPL CFBundleShortVersionString @PACKAGE_VERSION@ CFBundleSignature ???? CFBundleVersion @PACKAGE_VERSION@ NSHumanReadableCopyright Copyright 2011 Belledonne Communications LSMinimumSystemVersion 10.7 NSAppSleepDisabled YES liblinphone-4.4.21/build/macos/Makefile.am000066400000000000000000000002021376572216100203460ustar00rootroot00000000000000EXTRA_DIST= \ linphone.bundle \ environment.sh \ Info-linphone.plist.in \ pkg-scripts/postinstall \ pkg-distribution.xml.in liblinphone-4.4.21/build/macos/environment.sh000066400000000000000000000031171376572216100212220ustar00rootroot00000000000000## ## Copyright (c) 2010-2019 Belledonne Communications SARL. ## ## This file is part of Liblinphone. ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see . ## #export EXTRA_ARGS="--workdir $bundle_res" export LINPHONE_WORKDIR="$bundle_res" export GIO_EXTRA_MODULES="$bundle_lib/gio/modules" export PANGO_LIBDIR="$bundle_lib" export PANGO_SYSCONFDIR="$bundle_etc" export GDK_PIXBUF_MODULE_FILE="$bundle_lib/gdk-pixbuf-2.0/2.10.0/loaders.cache" #this is very important not to force a shared library path so that native frameworks can find their dependencies by themselves, #and not be forced to use the few libraries we have in the bundle that have the same name as native libs (ex: libiconv) export DYLD_LIBRARY_PATH= #the fucking script of the gtk-mac-bundler resets LANG due to obscure bugs. Set it back. LANG=`defaults read .GlobalPreferences AppleLocale` case "$LANG" in *.UTF-8) ;; *) if test -d /usr/share/locale/${LANG}.UTF-8 ; then LANG=${LANG}.UTF-8 fi ;; esac export LANG echo "LANG is $LANG" liblinphone-4.4.21/build/macos/libiconv-macos.patch000066400000000000000000000013341376572216100222470ustar00rootroot00000000000000--- libiconv-1.14.orig/lib/iconv.c 2013-03-14 16:30:50.000000000 +0100 +++ libiconv-1.14/lib/iconv.c 2013-03-15 10:24:38.000000000 +0100 @@ -607,4 +607,23 @@ strong_alias (libiconv_close, iconv_close) #endif +#undef iconv_open +#undef iconv +#undef iconv_close + +LIBICONV_DLL_EXPORTED iconv_t iconv_open (const char* tocode, const char* fromcode){ + return libiconv_open(tocode,fromcode); +} + +LIBICONV_DLL_EXPORTED size_t iconv (iconv_t icd, + ICONV_CONST char* * inbuf, size_t *inbytesleft, + char* * outbuf, size_t *outbytesleft){ + return libiconv(icd,inbuf,inbytesleft,outbuf,outbytesleft); +} + +LIBICONV_DLL_EXPORTED int iconv_close (iconv_t icd){ + return libiconv_close(icd); +} + + #endif liblinphone-4.4.21/build/macos/linphone.bundle000066400000000000000000000170401376572216100213310ustar00rootroot00000000000000 /opt/local ${env:LINPHONE_INSTALL_PREFIX} ${env:MS2_PLUGINS_INSTALL_PREFIX} ${env:LINPHONE_ADDITIONAL_DEPENDENCIES_PREFIX} ${env:BUNDLE_PREFIX} gtk+-2.0 ${project}/Info-linphone.plist ${prefix:linphone}/bin/linphone ${prefix:ms2plugins}/lib/mediastreamer/plugins/*.*.so ${prefix:linphone}/lib/*.*.dylib ${prefix}/lib/${gtkdir}/modules/*.so ${prefix}/share/mime/mime.cache ${prefix}/lib/${gtkdir}/${pkg:${gtk}:gtk_binary_version}/engines/*.so ${prefix}/lib/${gtkdir}/${pkg:${gtk}:gtk_binary_version}/printbackends/*.so ${prefix}/lib/gdk-pixbuf-2.0/${pkg:${gtk}:gtk_binary_version}/loaders/libpixbufloader-bmp.so ${prefix}/lib/gdk-pixbuf-2.0/${pkg:${gtk}:gtk_binary_version}/loaders/libpixbufloader-gif.so ${prefix}/lib/gdk-pixbuf-2.0/${pkg:${gtk}:gtk_binary_version}/loaders/libpixbufloader-icns.so ${prefix}/lib/gdk-pixbuf-2.0/${pkg:${gtk}:gtk_binary_version}/loaders/libpixbufloader-ico.so ${prefix}/lib/gdk-pixbuf-2.0/${pkg:${gtk}:gtk_binary_version}/loaders/libpixbufloader-jpeg.so ${prefix}/lib/gdk-pixbuf-2.0/${pkg:${gtk}:gtk_binary_version}/loaders/libpixbufloader-png.so ${prefix}/share/locale ${prefix:linphone}/share/locale ${prefix}/share/locale ${prefix}/share/locale ${prefix:linphone}/share/linphone ${prefix:linphone}/share/pixmaps/linphone ${prefix:linphone}/share/images ${prefix:linphone}/share/icons ${prefix}/share/themes ${project}/../../pixmaps/linphone.icns ${project}/environment.sh ${project}/../../gtk/gtkrc.mac ${prefix:linphone}/share/sounds/linphone/rings/oldphone-mono.wav ${prefix:linphone}/share/sounds/linphone/toy-mono.wav ${prefix:linphone}/share/sounds/linphone/ringback.wav ${prefix:linphone}/share/sounds/linphone/incoming_chat.wav liblinphone-4.4.21/build/macos/pkg-distribution.xml.in000066400000000000000000000013701376572216100227460ustar00rootroot00000000000000 Linphone linphone.pkg liblinphone-4.4.21/build/macos/pkg-scripts/000077500000000000000000000000001376572216100205665ustar00rootroot00000000000000liblinphone-4.4.21/build/macos/pkg-scripts/postinstall000077500000000000000000000006161376572216100230730ustar00rootroot00000000000000#!/bin/bash CURL=/usr/bin/curl BUNZIP2=/usr/bin/bunzip2 VERSION=1.4.0 BASENAME=libopenh264-${VERSION}-osx64 FILENAME=${BASENAME}.dylib.bz2 TMPDIR=/tmp/linphone_installer mkdir ${TMPDIR} cd ${TMPDIR} ${CURL} http://ciscobinary.openh264.org/${FILENAME} > ${FILENAME} ${BUNZIP2} ${FILENAME} cp ${BASENAME}.dylib /Applications/Linphone.app/Contents/Resources/lib/libopenh264.0.dylib rm -r ${TMPDIR} liblinphone-4.4.21/build/macos/quartz-theme-gtkrc.patch000066400000000000000000000011701376572216100230760ustar00rootroot00000000000000--- /opt/local/share/themes/Quartz/gtk-2.0/gtkrc 2015-03-25 15:29:53.000000000 +0100 +++ gtkrc 2015-10-29 13:43:15.000000000 +0100 @@ -12,7 +12,7 @@ gtk-menu-images = 0 gtk-toolbar-style = 0 gtk-enable-mnemonics = 0 -gtk-icon-sizes = "gtk-small-toolbar=16,16:gtk-large-toolbar=22,22" +gtk-icon-sizes = "gtk-menu=12,12:gtk-button=16,16:gtk-small-toolbar=16,16:gtk-large-toolbar=22,22" gtk-toolbar-icon-size = large-toolbar gtk-error-bell = 0 gtk-show-input-method-menu = 0 @@ -82,7 +82,7 @@ engine "quartz" { - buttontype = "aqua" + buttontype = "textured" } } liblinphone-4.4.21/build/osx/000077500000000000000000000000001376572216100160275ustar00rootroot00000000000000liblinphone-4.4.21/build/osx/Info.plist.in000066400000000000000000000026071376572216100204110ustar00rootroot00000000000000 CFBundleDevelopmentRegion English CFBundleExecutable ${MACOSX_FRAMEWORK_NAME} CFBundleGetInfoString ${MACOSX_BUNDLE_INFO_STRING} CFBundleIconFile ${MACOSX_BUNDLE_ICON_FILE} CFBundleIdentifier ${MACOSX_FRAMEWORK_IDENTIFIER} LSMinimumSystemVersion ${MIN_OS} MinimumOSVersion ${MIN_OS} CFBundleInfoDictionaryVersion 6.0 CFBundleLongVersionString ${MACOSX_BUNDLE_LONG_VERSION_STRING} CFBundleName ${MACOSX_BUNDLE_BUNDLE_NAME} CFBundlePackageType FMWK CFBundleShortVersionString ${LINPHONE_VERSION} CFBundleSignature ???? CFBundleVersion ${LINPHONE_VERSION} CSResourcesFileMapped NSHumanReadableCopyright ${MACOSX_BUNDLE_COPYRIGHT} NSPrincipalClass NSApplication NSHighResolutionCapable True liblinphone-4.4.21/build/rpm/000077500000000000000000000000001376572216100160145ustar00rootroot00000000000000liblinphone-4.4.21/build/rpm/liblinphone.spec.cmake000077500000000000000000000072011376572216100222550ustar00rootroot00000000000000# -*- rpm-spec -*- %define _prefix @CMAKE_INSTALL_PREFIX@ %define pkg_prefix @BC_PACKAGE_NAME_PREFIX@ %define package_name @CPACK_PACKAGE_NAME@-${FULL_VERSION} # re-define some directories for older RPMBuild versions which don't. This messes up the doc/ dir # taken from https://fedoraproject.org/wiki/Packaging:RPMMacros?rd=Packaging/RPMMacros %define _datarootdir %{_prefix}/share %define _datadir %{_datarootdir} %define _docdir %{_datadir}/doc Name: @CPACK_PACKAGE_NAME@ Version: ${RPM_VERSION} Release: ${RPM_RELEASE}%{?dist} Summary: Phone anywhere in the whole world by using the Internet Group: Applications/Communications License: GPL URL: http://www.linphone.org Source0: %{package_name}.tar.gz BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-buildroot Requires: %{pkg_prefix}bctoolbox Requires: %{pkg_prefix}ortp Requires: %{pkg_prefix}mediastreamer Requires: %{pkg_prefix}belle-sip Requires: %{pkg_prefix}belr %if @ENABLE_VCARD@ Requires: %{pkg_prefix}belcard %endif %description liblinphone is the voip sdk used by Linphone %package devel Summary: Development libraries for liblinphone Group: Development/Libraries Requires: %{name} = %{version}-%{release} %description devel This package contains header files and development libraries needed to develop programs using the liblinphone library. %if 0%{?rhel} && 0%{?rhel} <= 7 %global cmake_name cmake3 %define ctest_name ctest3 %else %global cmake_name cmake %define ctest_name ctest %endif # This is for debian builds where debug_package has to be manually specified, whereas in centos it does not %define custom_debug_package %{!?_enable_debug_packages:%debug_package}%{?_enable_debug_package:%{nil}} %custom_debug_package %prep %setup -n %{package_name} %build %{expand:%%%cmake_name} . -DCMAKE_BUILD_TYPE=@CMAKE_BUILD_TYPE@ -DCMAKE_PREFIX_PATH:PATH=%{_prefix} @RPM_ALL_CMAKE_OPTIONS@ make %{?_smp_mflags} %install make install DESTDIR=%{buildroot} mkdir -p $RPM_BUILD_ROOT/lib/systemd/system mkdir -p $RPM_BUILD_ROOT/etc/sysconfig install -p -m 0644 build/rpm/lp-autoanswer.service $RPM_BUILD_ROOT/lib/systemd/system install -p -m 0644 build/rpm/lp-autoanswer.conf $RPM_BUILD_ROOT/etc/sysconfig mv $RPM_BUILD_ROOT/etc/sysconfig/lp-autoanswer.conf $RPM_BUILD_ROOT/etc/sysconfig/lp-autoanswer # Dirty workaround to give exec rights for all shared libraries. Debian packaging needs this # TODO : set CMAKE_INSTALL_SO_NO_EXE for a cleaner workaround chmod +x `find %{buildroot} *.so.*` %check #%{ctest_name} -V %{?_smp_mflags} %clean rm -rf $RPM_BUILD_ROOT %post -p /sbin/ldconfig %postun -p /sbin/ldconfig %files %defattr(-,root,root) %doc CHANGELOG.md LICENSE.txt README.md %if @ENABLE_DAEMON@ || @ENABLE_CONSOLE_UI@ || @ENABLE_TOOLS@ %{_bindir}/* %endif %{_libdir}/*.so.* #%{_mandir}/* %{_datadir}/linphone %{_datadir}/sounds/linphone %{_datadir}/belr/grammars/* /etc/sysconfig/lp-autoanswer /lib/systemd/system/lp-autoanswer.service %files devel %defattr(-,root,root) %{_includedir}/linphone %if @ENABLE_CXX_WRAPPER@ %{_includedir}/linphone++ %endif %if @ENABLE_STATIC@ %{_libdir}/*.a %endif %if @ENABLE_SHARED@ %{_libdir}/*.so %endif %if @ENABLE_DOC@ %{_docdir}/linphone*/html %{_docdir}/linphone*/xml %endif %{_datadir}/Linphone/cmake/*.cmake %{_datadir}/LinphoneCxx/cmake/*.cmake %changelog * Tue Nov 27 2018 ronan.abhamon - Do not set CMAKE_INSTALL_LIBDIR and never with _libdir! * Thu Jul 13 2017 jehan.monnier - cmake port * Mon Aug 19 2013 jehan.monnier - Initial RPM release. liblinphone-4.4.21/build/rpm/lp-autoanswer.conf000066400000000000000000000000241376572216100214600ustar00rootroot00000000000000OPTIONS=--verbose" liblinphone-4.4.21/build/rpm/lp-autoanswer.service000066400000000000000000000004001376572216100221710ustar00rootroot00000000000000[Unit] Description=Linphone autoanswer service After=network.target [Service] Type=simple EnvironmentFile=/etc/sysconfig/lp-autoanswer ExecStart=/opt/com.belledonne-communications/linphone/bin/lp-auto-answer $OPTIONS [Install] WantedBy=multi-user.target liblinphone-4.4.21/cmake/000077500000000000000000000000001376572216100151775ustar00rootroot00000000000000liblinphone-4.4.21/cmake/FindCpuFeatures.cmake000066400000000000000000000026331376572216100212340ustar00rootroot00000000000000############################################################################ # FindCpuFeatures.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 Android cpufeatures include file and library # # CPUFEATURES_FOUND - system has libcpufeatures # CPUFEATURES_LIBRARIES - The libraries needed to use libcpufeatures find_library(CPUFEATURES_LIBRARIES NAMES cpufeatures ) include(FindPackageHandleStandardArgs) find_package_handle_standard_args(CpuFeatures DEFAULT_MSG CPUFEATURES_LIBRARIES ) mark_as_advanced(CPUFEATURES_LIBRARIES) liblinphone-4.4.21/cmake/FindIconv.cmake000066400000000000000000000034311376572216100200610ustar00rootroot00000000000000############################################################################ # FindIconv.cmake # 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 iconv include file and library # # ICONV_FOUND - system has libiconv # ICONV_INCLUDE_DIRS - the libiconv include directory # ICONV_LIBRARIES - The libraries needed to use libiconv if(APPLE AND NOT IOS) set(ICONV_HINTS "${CMAKE_OSX_SYSROOT}/usr" "/usr") endif() if(ICONV_HINTS) set(ICONV_LIBRARIES_HINTS "${ICONV_HINTS}/lib") endif() find_path(ICONV_INCLUDE_DIRS NAMES iconv.h HINTS "${ICONV_HINTS}" PATH_SUFFIXES include ) if(ICONV_INCLUDE_DIRS) set(HAVE_ICONV_H 1) endif() find_library(ICONV_LIBRARIES NAMES iconv HINTS "${ICONV_LIBRARIES_HINTS}" ) include(FindPackageHandleStandardArgs) find_package_handle_standard_args(Iconv DEFAULT_MSG ICONV_INCLUDE_DIRS ICONV_LIBRARIES HAVE_ICONV_H ) mark_as_advanced(ICONV_INCLUDE_DIRS ICONV_LIBRARIES HAVE_ICONV_H) liblinphone-4.4.21/cmake/FindLibXsd.cmake000066400000000000000000000033221376572216100201670ustar00rootroot00000000000000############################################################################ # FindLibXsd.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 libxsd library # # LIBXSD_FOUND - system has libxsd # LIBXSD_INCLUDE_DIRS - the libxsd include directory # LIBXSD_LIBRARIES - The libraries needed to use libxsd find_package(XercesC) find_path(LIBXSD_INCLUDE_DIRS NAMES xsd/cxx/config.hxx PATH_SUFFIXES include ) if(LIBXSD_INCLUDE_DIRS) list(APPEND LIBXSD_INCLUDE_DIRS ${XercesC_INCLUDE_DIRS}) endif() if (APPLE AND NOT IOS) set(LIBXSD_LIBRARIES ${XercesC_LIBRARIES} "-framework CoreServices") else() set(LIBXSD_LIBRARIES ${XercesC_LIBRARIES}) endif() include(FindPackageHandleStandardArgs) find_package_handle_standard_args(LibXsd DEFAULT_MSG LIBXSD_INCLUDE_DIRS LIBXSD_LIBRARIES ) mark_as_advanced(LIBXSD_INCLUDE_DIRS LIBXSD_LIBRARIES) liblinphone-4.4.21/cmake/FindNotify.cmake000066400000000000000000000034011376572216100202500ustar00rootroot00000000000000############################################################################ # FindNotify.cmake # 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 notify include file and library # # NOTIFY_FOUND - system has libnotify # NOTIFY_INCLUDE_DIRS - the libnotify include directory # NOTIFY_LIBRARIES - The libraries needed to use libnotify set(_NOTIFY_ROOT_PATHS ${CMAKE_INSTALL_PREFIX} ) find_path(NOTIFY_INCLUDE_DIRS NAMES libnotify/notify.h HINTS _NOTIFY_ROOT_PATHS PATH_SUFFIXES include ) if(NOTIFY_INCLUDE_DIRS) set(HAVE_LIBNOTIFY_NOTIFY_H 1) endif() find_library(NOTIFY_LIBRARIES NAMES notify HINTS ${_NOTIFY_ROOT_PATHS} PATH_SUFFIXES bin lib ) include(FindPackageHandleStandardArgs) find_package_handle_standard_args(Notify DEFAULT_MSG NOTIFY_INCLUDE_DIRS NOTIFY_LIBRARIES HAVE_LIBNOTIFY_NOTIFY_H ) mark_as_advanced(NOTIFY_INCLUDE_DIRS NOTIFY_LIBRARIES HAVE_LIBNOTIFY_NOTIFY_H) liblinphone-4.4.21/cmake/FindSoci.cmake000066400000000000000000000055211376572216100177020ustar00rootroot00000000000000############################################################################### # CMake module to search for SOCI library # # WARNING: This module is experimental work in progress. # # This module defines: # SOCI_INCLUDE_DIRS = include dirs to be used when using the soci library # SOCI_LIBRARIES = full path to the soci library # SOCI_VERSION = the soci version found (not yet. soci does not provide that info.) # SOCI_FOUND = true if soci was found # # For each component you specify in find_package(), the following variables are set. # # SOCI_${COMPONENT}_PLUGIN = full path to the soci plugin # SOCI_${COMPONENT}_FOUND # # Copyright (c) 2011 Michael Jansen # # Redistribution and use is allowed according to the terms of the BSD license. # For details see the accompanying COPYING-CMAKE-SCRIPTS file. # ############################################################################### # ### Global Configuration Section # SET(_SOCI_ALL_PLUGINS mysql sqlite3) SET(_SOCI_REQUIRED_VARS SOCI_INCLUDE_DIRS SOCI_LIBRARIES) SET(_SOCI_VERSION "_4_0") # ### FIRST STEP: Find the soci headers. # FIND_PATH(SOCI_INCLUDE_DIRS soci/soci.h DOC "Soci (http://soci.sourceforge.net) include directory") MARK_AS_ADVANCED(SOCI_INCLUDE_DIRS) # ### SECOND STEP: Find the soci core library. Respect LIB_SUFFIX # FIND_LIBRARY(SOCI_LIBRARIES NAMES soci_core soci_core${_SOCI_VERSION} PATH_SUFFIXES Frameworks lib lib64) MARK_AS_ADVANCED(SOCI_LIBRARIES) GET_FILENAME_COMPONENT(SOCI_LIBRARY_DIR ${SOCI_LIBRARIES} PATH) MARK_AS_ADVANCED(SOCI_LIBRARY_DIR) # ### THIRD STEP: Find all installed plugins if the library was found # IF(SOCI_INCLUDE_DIRS AND SOCI_LIBRARIES) MESSAGE(STATUS "Soci found: Looking for plugins") FOREACH(plugin IN LISTS _SOCI_ALL_PLUGINS) FIND_LIBRARY( SOCI_${plugin}_PLUGIN NAMES soci_${plugin} soci_${plugin}${_SOCI_VERSION} PATH_SUFFIXES Frameworks lib lib64) MARK_AS_ADVANCED(SOCI_${plugin}_PLUGIN) IF(SOCI_${plugin}_PLUGIN) MESSAGE(STATUS " * Plugin ${plugin} found ${SOCI_${plugin}_PLUGIN}.") SET(SOCI_${plugin}_FOUND True) ELSE() MESSAGE(STATUS " * Plugin ${plugin} not found.") SET(SOCI_${plugin}_FOUND False) ENDIF() ENDFOREACH() # ### FOURTH CHECK: Check if the required components were all found # FOREACH(component ${Soci_FIND_COMPONENTS}) IF(NOT SOCI_${component}_FOUND) MESSAGE(SEND_ERROR "Required component ${component} not found. It seems that Soci was built without support of ${component}, consider rebuilding it.") ENDIF() ENDFOREACH() ENDIF() # ### ADHERE TO STANDARDS # include(FindPackageHandleStandardArgs) FIND_PACKAGE_HANDLE_STANDARD_ARGS(Soci DEFAULT_MSG ${_SOCI_REQUIRED_VARS}) liblinphone-4.4.21/cmake/FindSqlite3.cmake000066400000000000000000000034531376572216100203330ustar00rootroot00000000000000############################################################################ # FindSqlite3.cmake # 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 sqlite3 include file and library # # SQLITE3_FOUND - system has sqlite3 # SQLITE3_INCLUDE_DIRS - the sqlite3 include directory # SQLITE3_LIBRARIES - The libraries needed to use sqlite3 if(APPLE AND NOT IOS) set(SQLITE3_HINTS "/usr") endif() if(SQLITE3_HINTS) set(SQLITE3_LIBRARIES_HINTS "${SQLITE3_HINTS}/lib") endif() find_path(SQLITE3_INCLUDE_DIRS NAMES sqlite3.h HINTS "${SQLITE3_HINTS}" PATH_SUFFIXES include ) if(SQLITE3_INCLUDE_DIRS) set(HAVE_SQLITE3_H 1) endif() find_library(SQLITE3_LIBRARIES NAMES sqlite3 HINTS "${SQLITE3_LIBRARIES_HINTS}" ) include(FindPackageHandleStandardArgs) find_package_handle_standard_args(Sqlite3 DEFAULT_MSG SQLITE3_INCLUDE_DIRS SQLITE3_LIBRARIES HAVE_SQLITE3_H ) mark_as_advanced(SQLITE3_INCLUDE_DIRS SQLITE3_LIBRARIES HAVE_SQLITE3_H) liblinphone-4.4.21/cmake/FindSupport.cmake000066400000000000000000000025571376572216100204670ustar00rootroot00000000000000############################################################################ # FindSupport.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 Android support include file and library # # SUPPORT_FOUND - system has libsupport # SUPPORT_LIBRARIES - The libraries needed to use libsupport find_library(SUPPORT_LIBRARIES NAMES support ) include(FindPackageHandleStandardArgs) find_package_handle_standard_args(Support DEFAULT_MSG SUPPORT_LIBRARIES ) mark_as_advanced(SUPPORT_LIBRARIES) liblinphone-4.4.21/cmake/FindXML2.cmake000066400000000000000000000033541376572216100175310ustar00rootroot00000000000000############################################################################ # FindXML2.txt # Copyright (C) 2015 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 libxml2 include file and library # # XML2_FOUND - system has libxml2 # XML2_INCLUDE_DIRS - the libxml2 include directory # XML2_LIBRARIES - The libraries needed to use libxml2 if(APPLE AND NOT IOS) set(XML2_HINTS "/usr") endif() if(XML2_HINTS) set(XML2_LIBRARIES_HINTS "${XML2_HINTS}/lib") endif() find_path(XML2_INCLUDE_DIRS NAMES libxml/xmlreader.h HINTS "${XML2_HINTS}" PATH_SUFFIXES include/libxml2 ) if(XML2_INCLUDE_DIRS) set(HAVE_LIBXML_XMLREADER_H 1) endif() find_library(XML2_LIBRARIES NAMES xml2 HINTS "${XML2_LIBRARIES_HINTS}" ) include(FindPackageHandleStandardArgs) find_package_handle_standard_args(XML2 DEFAULT_MSG XML2_INCLUDE_DIRS XML2_LIBRARIES ) mark_as_advanced(XML2_INCLUDE_DIRS XML2_LIBRARIES) liblinphone-4.4.21/cmake/LinphoneConfig.cmake.in000066400000000000000000000044361376572216100215170ustar00rootroot00000000000000############################################################################ # LinphoneConfig.cmake # Copyright (C) 2015 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. # ############################################################################ # # Config file for the belle-sip package. # It defines the following variables: # # LINPHONE_FOUND - system has linphone # LINPHONE_INCLUDE_DIRS - the linphone include directory # LINPHONE_CPPFLAGS - The compilation flags needed to use linphone # LINPHONE_LDFLAGS - The linking flags needed to use linphone @PACKAGE_INIT@ include("${CMAKE_CURRENT_LIST_DIR}/LinphoneTargets.cmake") set(LINPHONE_TARGETNAME linphone) if(@ENABLE_SHARED@) set(LINPHONE_LIBRARIES ${LINPHONE_TARGETNAME}) else() set(LINPHONE_TARGETNAME linphone-static) if(TARGET ${LINPHONE_TARGETNAME}) if(LINPHONE_BUILDER_GROUP_EXTERNAL_SOURCE_PATH_BUILDERS) set(LINPHONE_LIBRARIES ${LINPHONE_TARGETNAME}) else() get_target_property(LINPHONE_LIBRARIES ${LINPHONE_TARGETNAME} LOCATION) endif() get_target_property(LINPHONE_LINK_LIBRARIES ${LINPHONE_TARGETNAME} INTERFACE_LINK_LIBRARIES) if(LINPHONE_LINK_LIBRARIES) list(APPEND LINPHONE_LIBRARIES ${LINPHONE_LINK_LIBRARIES}) endif() endif() endif() get_target_property(LINPHONE_INCLUDE_DIRS ${LINPHONE_TARGETNAME} INTERFACE_INCLUDE_DIRECTORIES) set_target_properties(${LINPHONE_TARGETNAME} PROPERTIES INTERFACE_LINK_LIBRARIES "@INTERFACE_LIBS@") set(LINPHONE_CPPFLAGS @LINPHONE_CPPFLAGS@) set(LINPHONE_LDFLAGS "@LINPHONE_LDFLAGS@") set(LINPHONE_FOUND 1) liblinphone-4.4.21/cmake/Tools.cmake000066400000000000000000000025371376572216100173100ustar00rootroot00000000000000############################################################################ # LinphoneUtils.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. # ############################################################################ function(check_python_module module_name) execute_process(COMMAND ${PYTHON_EXECUTABLE} -c "import ${module_name}" RESULT_VARIABLE result OUTPUT_QUIET ERROR_QUIET) if(result EQUAL 0) message(STATUS "'${module_name}' python module found") else() message(FATAL_ERROR "'${module_name}' python module not found") endif() endfunction() liblinphone-4.4.21/config.h.cmake000066400000000000000000000040601376572216100166140ustar00rootroot00000000000000/*************************************************************************** * config.h.cmake * 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. * ****************************************************************************/ #define LINPHONE_MAJOR_VERSION ${LINPHONE_MAJOR_VERSION} #define LINPHONE_MINOR_VERSION ${LINPHONE_MINOR_VERSION} #define LINPHONE_MICRO_VERSION ${LINPHONE_MICRO_VERSION} #define LINPHONE_VERSION "${LINPHONE_VERSION}" #define LIBLINPHONE_VERSION "${LINPHONE_VERSION}" #define LINPHONE_ALL_LANGS "${LINPHONE_ALL_LANGS}" #define LINPHONE_PLUGINS_DIR "${LINPHONE_PLUGINS_DIR}" #define LINPHONE_CONFIG_DIR "${LINPHONE_CONFIG_DIR}" #define GETTEXT_PACKAGE "${GETTEXT_PACKAGE}" #define PACKAGE_LOCALE_DIR "${PACKAGE_LOCALE_DIR}" #define PACKAGE_DATA_DIR "${PACKAGE_DATA_DIR}" #define PACKAGE_GRAMMAR_DIR "${PACKAGE_GRAMMAR_DIR}" #define PACKAGE_SOUND_DIR "${PACKAGE_SOUND_DIR}" #define PACKAGE_RING_DIR "${PACKAGE_RING_DIR}" #cmakedefine BUILD_WIZARD #cmakedefine HAVE_NOTIFY4 #cmakedefine HAVE_ZLIB 1 #cmakedefine HAVE_CU_GET_SUITE 1 #cmakedefine HAVE_CU_CURSES 1 #cmakedefine HAVE_LIBUDEV_H 0 #cmakedefine HAVE_LIME #cmakedefine HAVE_LIME_X3DH #cmakedefine HAVE_ADVANCED_IM #cmakedefine HAVE_DB_STORAGE #cmakedefine ENABLE_UPDATE_CHECK 1 #cmakedefine HAVE_GETIFADDRS liblinphone-4.4.21/config.rpath000077500000000000000000000374441376572216100164430ustar00rootroot00000000000000#! /bin/sh # Output a system dependent set of variables, describing how to set the # run time search path of shared libraries in an executable. # # Copyright 1996-2006 Free Software Foundation, Inc. # Taken from GNU libtool, 2001 # Originally by Gordon Matzigkeit , 1996 # # This file is free software; the Free Software Foundation gives # unlimited permission to copy and/or distribute it, with or without # modifications, as long as this notice is preserved. # # The first argument passed to this file is the canonical host specification, # CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM # or # CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM # The environment variables CC, GCC, LDFLAGS, LD, with_gnu_ld # should be set by the caller. # # The set of defined variables is at the end of this script. # Known limitations: # - On IRIX 6.5 with CC="cc", the run time search patch must not be longer # than 256 bytes, otherwise the compiler driver will dump core. The only # known workaround is to choose shorter directory names for the build # directory and/or the installation directory. # All known linkers require a `.a' archive for static linking (except MSVC, # which needs '.lib'). libext=a shrext=.so host="$1" host_cpu=`echo "$host" | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\1/'` host_vendor=`echo "$host" | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\2/'` host_os=`echo "$host" | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\3/'` # Code taken from libtool.m4's _LT_CC_BASENAME. for cc_temp in $CC""; do case $cc_temp in compile | *[\\/]compile | ccache | *[\\/]ccache ) ;; distcc | *[\\/]distcc | purify | *[\\/]purify ) ;; \-*) ;; *) break;; esac done cc_basename=`echo "$cc_temp" | sed -e 's%^.*/%%'` # Code taken from libtool.m4's AC_LIBTOOL_PROG_COMPILER_PIC. wl= if test "$GCC" = yes; then wl='-Wl,' else case "$host_os" in aix*) wl='-Wl,' ;; darwin*) case $cc_basename in xlc*) wl='-Wl,' ;; esac ;; mingw* | pw32* | os2*) ;; hpux9* | hpux10* | hpux11*) wl='-Wl,' ;; irix5* | irix6* | nonstopux*) wl='-Wl,' ;; newsos6) ;; linux*) case $cc_basename in icc* | ecc*) wl='-Wl,' ;; pgcc | pgf77 | pgf90) wl='-Wl,' ;; ccc*) wl='-Wl,' ;; como) wl='-lopt=' ;; *) case `$CC -V 2>&1 | sed 5q` in *Sun\ C*) wl='-Wl,' ;; esac ;; esac ;; osf3* | osf4* | osf5*) wl='-Wl,' ;; sco3.2v5*) ;; solaris*) wl='-Wl,' ;; sunos4*) wl='-Qoption ld ' ;; sysv4 | sysv4.2uw2* | sysv4.3* | sysv5*) wl='-Wl,' ;; sysv4*MP*) ;; unicos*) wl='-Wl,' ;; uts4*) ;; esac fi # Code taken from libtool.m4's AC_LIBTOOL_PROG_LD_SHLIBS. hardcode_libdir_flag_spec= hardcode_libdir_separator= hardcode_direct=no hardcode_minus_L=no case "$host_os" in cygwin* | mingw* | pw32*) # FIXME: the MSVC++ port hasn't been tested in a loooong time # When not using gcc, we currently assume that we are using # Microsoft Visual C++. if test "$GCC" != yes; then with_gnu_ld=no fi ;; interix*) # we just hope/assume this is gcc and not c89 (= MSVC++) with_gnu_ld=yes ;; openbsd*) with_gnu_ld=no ;; esac ld_shlibs=yes if test "$with_gnu_ld" = yes; then # Set some defaults for GNU ld with shared library support. These # are reset later if shared libraries are not supported. Putting them # here allows them to be overridden if necessary. # Unlike libtool, we use -rpath here, not --rpath, since the documented # option of GNU ld is called -rpath, not --rpath. hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir' case "$host_os" in aix3* | aix4* | aix5*) # On AIX/PPC, the GNU linker is very broken if test "$host_cpu" != ia64; then ld_shlibs=no fi ;; amigaos*) hardcode_libdir_flag_spec='-L$libdir' hardcode_minus_L=yes # Samuel A. Falvo II reports # that the semantics of dynamic libraries on AmigaOS, at least up # to version 4, is to share data among multiple programs linked # with the same dynamic library. Since this doesn't match the # behavior of shared libraries on other platforms, we cannot use # them. ld_shlibs=no ;; beos*) if $LD --help 2>&1 | grep ': supported targets:.* elf' > /dev/null; then : else ld_shlibs=no fi ;; cygwin* | mingw* | pw32*) # hardcode_libdir_flag_spec is actually meaningless, as there is # no search path for DLLs. hardcode_libdir_flag_spec='-L$libdir' if $LD --help 2>&1 | grep 'auto-import' > /dev/null; then : else ld_shlibs=no fi ;; interix3*) hardcode_direct=no hardcode_libdir_flag_spec='${wl}-rpath,$libdir' ;; linux*) if $LD --help 2>&1 | grep ': supported targets:.* elf' > /dev/null; then : else ld_shlibs=no fi ;; netbsd*) ;; solaris*) if $LD -v 2>&1 | grep 'BFD 2\.8' > /dev/null; then ld_shlibs=no elif $LD --help 2>&1 | grep ': supported targets:.* elf' > /dev/null; then : else ld_shlibs=no fi ;; sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX*) case `$LD -v 2>&1` in *\ [01].* | *\ 2.[0-9].* | *\ 2.1[0-5].*) ld_shlibs=no ;; *) if $LD --help 2>&1 | grep ': supported targets:.* elf' > /dev/null; then hardcode_libdir_flag_spec='`test -z "$SCOABSPATH" && echo ${wl}-rpath,$libdir`' else ld_shlibs=no fi ;; esac ;; sunos4*) hardcode_direct=yes ;; *) if $LD --help 2>&1 | grep ': supported targets:.* elf' > /dev/null; then : else ld_shlibs=no fi ;; esac if test "$ld_shlibs" = no; then hardcode_libdir_flag_spec= fi else case "$host_os" in aix3*) # Note: this linker hardcodes the directories in LIBPATH if there # are no directories specified by -L. hardcode_minus_L=yes if test "$GCC" = yes; then # Neither direct hardcoding nor static linking is supported with a # broken collect2. hardcode_direct=unsupported fi ;; aix4* | aix5*) if test "$host_cpu" = ia64; then # On IA64, the linker does run time linking by default, so we don't # have to do anything special. aix_use_runtimelinking=no else aix_use_runtimelinking=no # Test if we are trying to use run time linking or normal # AIX style linking. If -brtl is somewhere in LDFLAGS, we # need to do runtime linking. case $host_os in aix4.[23]|aix4.[23].*|aix5*) for ld_flag in $LDFLAGS; do if (test $ld_flag = "-brtl" || test $ld_flag = "-Wl,-brtl"); then aix_use_runtimelinking=yes break fi done ;; esac fi hardcode_direct=yes hardcode_libdir_separator=':' if test "$GCC" = yes; then case $host_os in aix4.[012]|aix4.[012].*) collect2name=`${CC} -print-prog-name=collect2` if test -f "$collect2name" && \ strings "$collect2name" | grep resolve_lib_name >/dev/null then # We have reworked collect2 hardcode_direct=yes else # We have old collect2 hardcode_direct=unsupported hardcode_minus_L=yes hardcode_libdir_flag_spec='-L$libdir' hardcode_libdir_separator= fi ;; esac fi # Begin _LT_AC_SYS_LIBPATH_AIX. echo 'int main () { return 0; }' > conftest.c ${CC} ${LDFLAGS} conftest.c -o conftest aix_libpath=`dump -H conftest 2>/dev/null | sed -n -e '/Import File Strings/,/^$/ { /^0/ { s/^0 *\(.*\)$/\1/; p; } }'` if test -z "$aix_libpath"; then aix_libpath=`dump -HX64 conftest 2>/dev/null | sed -n -e '/Import File Strings/,/^$/ { /^0/ { s/^0 *\(.*\)$/\1/; p; } }'` fi if test -z "$aix_libpath"; then aix_libpath="/usr/lib:/lib" fi rm -f conftest.c conftest # End _LT_AC_SYS_LIBPATH_AIX. if test "$aix_use_runtimelinking" = yes; then hardcode_libdir_flag_spec='${wl}-blibpath:$libdir:'"$aix_libpath" else if test "$host_cpu" = ia64; then hardcode_libdir_flag_spec='${wl}-R $libdir:/usr/lib:/lib' else hardcode_libdir_flag_spec='${wl}-blibpath:$libdir:'"$aix_libpath" fi fi ;; amigaos*) hardcode_libdir_flag_spec='-L$libdir' hardcode_minus_L=yes # see comment about different semantics on the GNU ld section ld_shlibs=no ;; bsdi[45]*) ;; cygwin* | mingw* | pw32*) # When not using gcc, we currently assume that we are using # Microsoft Visual C++. # hardcode_libdir_flag_spec is actually meaningless, as there is # no search path for DLLs. hardcode_libdir_flag_spec=' ' libext=lib ;; darwin* | rhapsody*) hardcode_direct=no if test "$GCC" = yes ; then : else case $cc_basename in xlc*) ;; *) ld_shlibs=no ;; esac fi ;; dgux*) hardcode_libdir_flag_spec='-L$libdir' ;; freebsd1*) ld_shlibs=no ;; freebsd2.2*) hardcode_libdir_flag_spec='-R$libdir' hardcode_direct=yes ;; freebsd2*) hardcode_direct=yes hardcode_minus_L=yes ;; freebsd* | kfreebsd*-gnu | dragonfly*) hardcode_libdir_flag_spec='-R$libdir' hardcode_direct=yes ;; hpux9*) hardcode_libdir_flag_spec='${wl}+b ${wl}$libdir' hardcode_libdir_separator=: hardcode_direct=yes # hardcode_minus_L: Not really in the search PATH, # but as the default location of the library. hardcode_minus_L=yes ;; hpux10*) if test "$with_gnu_ld" = no; then hardcode_libdir_flag_spec='${wl}+b ${wl}$libdir' hardcode_libdir_separator=: hardcode_direct=yes # hardcode_minus_L: Not really in the search PATH, # but as the default location of the library. hardcode_minus_L=yes fi ;; hpux11*) if test "$with_gnu_ld" = no; then hardcode_libdir_flag_spec='${wl}+b ${wl}$libdir' hardcode_libdir_separator=: case $host_cpu in hppa*64*|ia64*) hardcode_direct=no ;; *) hardcode_direct=yes # hardcode_minus_L: Not really in the search PATH, # but as the default location of the library. hardcode_minus_L=yes ;; esac fi ;; irix5* | irix6* | nonstopux*) hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir' hardcode_libdir_separator=: ;; netbsd*) hardcode_libdir_flag_spec='-R$libdir' hardcode_direct=yes ;; newsos6) hardcode_direct=yes hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir' hardcode_libdir_separator=: ;; openbsd*) hardcode_direct=yes if test -z "`echo __ELF__ | $CC -E - | grep __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then hardcode_libdir_flag_spec='${wl}-rpath,$libdir' else case "$host_os" in openbsd[01].* | openbsd2.[0-7] | openbsd2.[0-7].*) hardcode_libdir_flag_spec='-R$libdir' ;; *) hardcode_libdir_flag_spec='${wl}-rpath,$libdir' ;; esac fi ;; os2*) hardcode_libdir_flag_spec='-L$libdir' hardcode_minus_L=yes ;; osf3*) hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir' hardcode_libdir_separator=: ;; osf4* | osf5*) if test "$GCC" = yes; then hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir' else # Both cc and cxx compiler support -rpath directly hardcode_libdir_flag_spec='-rpath $libdir' fi hardcode_libdir_separator=: ;; solaris*) hardcode_libdir_flag_spec='-R$libdir' ;; sunos4*) hardcode_libdir_flag_spec='-L$libdir' hardcode_direct=yes hardcode_minus_L=yes ;; sysv4) case $host_vendor in sni) hardcode_direct=yes # is this really true??? ;; siemens) hardcode_direct=no ;; motorola) hardcode_direct=no #Motorola manual says yes, but my tests say they lie ;; esac ;; sysv4.3*) ;; sysv4*MP*) if test -d /usr/nec; then ld_shlibs=yes fi ;; sysv4*uw2* | sysv5OpenUNIX* | sysv5UnixWare7.[01].[10]* | unixware7*) ;; sysv5* | sco3.2v5* | sco5v6*) hardcode_libdir_flag_spec='`test -z "$SCOABSPATH" && echo ${wl}-R,$libdir`' hardcode_libdir_separator=':' ;; uts4*) hardcode_libdir_flag_spec='-L$libdir' ;; *) ld_shlibs=no ;; esac fi # Check dynamic linker characteristics # Code taken from libtool.m4's AC_LIBTOOL_SYS_DYNAMIC_LINKER. libname_spec='lib$name' case "$host_os" in aix3*) ;; aix4* | aix5*) ;; amigaos*) ;; beos*) ;; bsdi[45]*) ;; cygwin* | mingw* | pw32*) shrext=.dll ;; darwin* | rhapsody*) shrext=.dylib ;; dgux*) ;; freebsd1*) ;; kfreebsd*-gnu) ;; freebsd* | dragonfly*) ;; gnu*) ;; hpux9* | hpux10* | hpux11*) case $host_cpu in ia64*) shrext=.so ;; hppa*64*) shrext=.sl ;; *) shrext=.sl ;; esac ;; interix3*) ;; irix5* | irix6* | nonstopux*) case "$host_os" in irix5* | nonstopux*) libsuff= shlibsuff= ;; *) case $LD in *-32|*"-32 "|*-melf32bsmip|*"-melf32bsmip ") libsuff= shlibsuff= ;; *-n32|*"-n32 "|*-melf32bmipn32|*"-melf32bmipn32 ") libsuff=32 shlibsuff=N32 ;; *-64|*"-64 "|*-melf64bmip|*"-melf64bmip ") libsuff=64 shlibsuff=64 ;; *) libsuff= shlibsuff= ;; esac ;; esac ;; linux*oldld* | linux*aout* | linux*coff*) ;; linux*) ;; knetbsd*-gnu) ;; netbsd*) ;; newsos6) ;; nto-qnx*) ;; openbsd*) ;; os2*) libname_spec='$name' shrext=.dll ;; osf3* | osf4* | osf5*) ;; solaris*) ;; sunos4*) ;; sysv4 | sysv4.3*) ;; sysv4*MP*) ;; sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX* | sysv4*uw2*) ;; uts4*) ;; esac sed_quote_subst='s/\(["`$\\]\)/\\\1/g' escaped_wl=`echo "X$wl" | sed -e 's/^X//' -e "$sed_quote_subst"` shlibext=`echo "$shrext" | sed -e 's,^\.,,'` escaped_hardcode_libdir_flag_spec=`echo "X$hardcode_libdir_flag_spec" | sed -e 's/^X//' -e "$sed_quote_subst"` LC_ALL=C sed -e 's/^\([a-zA-Z0-9_]*\)=/acl_cv_\1=/' <" would do.. Unfortunately the -s switch would confuse people, I'd rather change its semantic to "source file" where file wold contain a list of commands, so ./linephone -s myfriend.sip would call yourfriend... - implement "smart" command completion liblinphone-4.4.21/console/commands.c000066400000000000000000002306011376572216100175300ustar00rootroot00000000000000/**************************************************************************** * * $Id: commands.c,v 1.39 2008/07/03 15:08:34 smorlat Exp $ * * Copyright (C) 2006-2009 Sandro Santilli * Copyright (C) 2004 Simon MORLAT * **************************************************************************** * * 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 #include #include #ifndef _WIN32_WCE #include #endif /*_WIN32_WCE*/ #include #include #include #include "linphonec.h" #include #ifndef _WIN32 #include #include #endif #define AUDIO 0 #define VIDEO 1 /*************************************************************************** * * Forward declarations * ***************************************************************************/ extern char *lpc_strip_blanks(char *input); /* Command handlers */ static int lpc_cmd_help(LinphoneCore *, char *); static int lpc_cmd_proxy(LinphoneCore *, char *); static int lpc_cmd_call(LinphoneCore *, char *); static int lpc_cmd_calls(LinphoneCore *, char *); static int lpc_cmd_chat(LinphoneCore *, char *); static int lpc_cmd_answer(LinphoneCore *, char *); static int lpc_cmd_autoanswer(LinphoneCore *, char *); static int lpc_cmd_terminate(LinphoneCore *, char *); static int lpc_cmd_redirect(LinphoneCore *, char *); static int lpc_cmd_call_logs(LinphoneCore *, char *); static int lpc_cmd_ipv6(LinphoneCore *, char *); static int lpc_cmd_transfer(LinphoneCore *, char *); static int lpc_cmd_quit(LinphoneCore *, char *); static int lpc_cmd_nat(LinphoneCore *, char *); static int lpc_cmd_stun(LinphoneCore *, char *); static int lpc_cmd_firewall(LinphoneCore *, char *); static int lpc_cmd_friend(LinphoneCore *, char*); static int lpc_cmd_soundcard(LinphoneCore *, char *); static int lpc_cmd_webcam(LinphoneCore *, char *); static int lpc_cmd_staticpic(LinphoneCore *, char *); static int lpc_cmd_play(LinphoneCore *, char *); static int lpc_cmd_record(LinphoneCore *, char *); static int lpc_cmd_register(LinphoneCore *, char *); static int lpc_cmd_unregister(LinphoneCore *, char *); static int lpc_cmd_duration(LinphoneCore *lc, char *args); static int lpc_cmd_status(LinphoneCore *lc, char *args); static int lpc_cmd_ports(LinphoneCore *lc, char *args); static int lpc_cmd_param(LinphoneCore *lc, char *args); static int lpc_cmd_speak(LinphoneCore *lc, char *args); static int lpc_cmd_acodec(LinphoneCore *lc, char *args); static int lpc_cmd_vcodec(LinphoneCore *lc, char *args); static int lpc_cmd_codec(int type, LinphoneCore *lc, char *args); static int lpc_cmd_echocancellation(LinphoneCore *lc, char *args); static int lpc_cmd_echolimiter(LinphoneCore *lc, char *args); static int lpc_cmd_pause(LinphoneCore *lc, char *args); static int lpc_cmd_resume(LinphoneCore *lc, char *args); static int lpc_cmd_mute_mic(LinphoneCore *lc, char *args); static int lpc_cmd_unmute_mic(LinphoneCore *lc, char *args); static int lpc_cmd_playback_gain(LinphoneCore *lc, char *args); static int lpc_cmd_rtp_no_xmit_on_audio_mute(LinphoneCore *lc, char *args); #ifdef VIDEO_ENABLED static int lpc_cmd_camera(LinphoneCore *lc, char *args); static int lpc_cmd_video_window(LinphoneCore *lc, char *args); static int lpc_cmd_preview_window(LinphoneCore *lc, char *args); static int lpc_cmd_snapshot(LinphoneCore *lc, char *args); static int lpc_cmd_preview_snapshot(LinphoneCore *lc, char *args); static int lpc_cmd_vfureq(LinphoneCore *lc, char *arg); #endif static int lpc_cmd_states(LinphoneCore *lc, char *args); static int lpc_cmd_identify(LinphoneCore *lc, char *args); static int lpc_cmd_ringback(LinphoneCore *lc, char *args); static int lpc_cmd_conference(LinphoneCore *lc, char *args); static int lpc_cmd_zrtp_verified(LinphoneCore *lc, char *args); static int lpc_cmd_zrtp_unverified(LinphoneCore *lc, char *args); /* Command handler helpers */ static void linphonec_proxy_add(LinphoneCore *lc); static void linphonec_proxy_display(LinphoneProxyConfig *lc); static void linphonec_proxy_list(LinphoneCore *lc); static void linphonec_proxy_remove(LinphoneCore *lc, int index); static int linphonec_proxy_use(LinphoneCore *lc, int index); static void linphonec_proxy_show(LinphoneCore *lc,int index); static void linphonec_friend_display(LinphoneFriend *fr); static int linphonec_friend_list(LinphoneCore *lc, char *arg); static void linphonec_display_command_help(LPC_COMMAND *cmd); static int linphonec_friend_call(LinphoneCore *lc, unsigned int num); #ifndef _WIN32 static int linphonec_friend_add(LinphoneCore *lc, const char *name, const char *addr); #endif static int linphonec_friend_delete(LinphoneCore *lc, int num); static int linphonec_friend_delete(LinphoneCore *lc, int num); static void linphonec_codec_list(int type, LinphoneCore *lc); static void linphonec_codec_enable(int type, LinphoneCore *lc, int index); static void linphonec_codec_disable(int type, LinphoneCore *lc, int index); static void lpc_display_call_states(LinphoneCore *lc); /* Command table management */ static LPC_COMMAND *lpc_find_command(const char *name); void linphonec_out(const char *fmt,...); VideoParams lpc_video_params={-1,-1,-1,-1,NULL,TRUE,FALSE}; VideoParams lpc_preview_params={-1,-1,-1,-1,NULL,TRUE,FALSE}; /*************************************************************************** * * Global variables * ***************************************************************************/ /* * Commands table. */ static LPC_COMMAND commands[] = { { "help", lpc_cmd_help, "Print commands help.", "'help '\t: displays specific help for command.\n" "'help advanced'\t: shows advanced commands.\n" }, { "answer", lpc_cmd_answer, "Answer a call", "'answer' : Answer the current incoming call\n" "'answer ' : Answer the call with given id\n" }, { "autoanswer", lpc_cmd_autoanswer, "Show/set auto-answer mode", "'autoanswer' \t: show current autoanswer mode\n" "'autoanswer enable'\t: enable autoanswer mode\n" "'autoanswer disable'\t: disable autoanswer mode\n" }, { "call", lpc_cmd_call, "Call a SIP uri or number", #ifdef VIDEO_ENABLED "'call [options]' \t: initiate a call to the specified destination.\n" "Options can be:\n" "--audio-only : initiate the call without video.\n" "--early-media : sends audio and video stream immediately when remote proposes early media.\n" #else "'call ' \t: initiate a call to the specified destination.\n" #endif }, { "calls", lpc_cmd_calls, "Show all the current calls with their id and status.", NULL }, { "call-logs", lpc_cmd_call_logs, "Calls history", NULL }, #ifdef VIDEO_ENABLED { "camera", lpc_cmd_camera, "Send camera output for current call.", "'camera on'\t: allow sending of local camera video to remote end.\n" "'camera off'\t: disable sending of local camera's video to remote end.\n" }, #endif { "chat", lpc_cmd_chat, "Chat with a SIP uri", "'chat \"message\"' " ": send a chat message \"message\" to the specified destination." }, { "conference", lpc_cmd_conference, "Create and manage an audio conference.", "'conference add : join the call with id 'call id' into the audio conference." "'conference rm : remove the call with id 'call id' from the audio conference." }, { "duration", lpc_cmd_duration, "Print duration in seconds of the last call.", NULL }, { "firewall", lpc_cmd_firewall, "Set firewall policy", "'firewall' : show current firewall policy.\n" "'firewall none' : use direct connection.\n" "'firewall nat' : use nat address given with the 'nat' command.\n" "'firewall stun' : use stun server given with the 'stun' command.\n" "'firewall ice' : use ice.\n" "'firewall upnp' : use uPnP IGD.\n" }, { "friend", lpc_cmd_friend, "Manage friends", "'friend list []' : list friends.\n" "'friend call ' : call a friend.\n" "'friend add ' : add friend, must be quoted to include\n" " spaces, has \"sip:\" added if it isn't\n" " there. Don't use '<' '>' around .\n" "'friend delete ' : remove friend, 'all' removes all\n" }, { "ipv6", lpc_cmd_ipv6, "Use IPV6", "'ipv6 status' : show ipv6 usage status.\n" "'ipv6 enable' : enable the use of the ipv6 network.\n" "'ipv6 disable' : do not use ipv6 network." }, { "mute", lpc_cmd_mute_mic, "Mute microphone and suspend voice transmission.", NULL }, { "nat", lpc_cmd_nat, "Set nat address", "'nat' : show nat settings.\n" "'nat ' : set nat address.\n" }, { "pause", lpc_cmd_pause, "pause a call", "'pause' : pause the current call\n" }, { "play", lpc_cmd_play, "play a wav file", "This command has two roles:\n" "Plays a file instead of capturing from soundcard - only available in file mode (see 'help soundcard')\n" "Specifies a wav file to be played to play music to far end when putting it on hold (pause)\n" "'play ' : play a wav file." }, { "playbackgain", lpc_cmd_playback_gain, "Adjust playback gain.", NULL }, { "proxy", lpc_cmd_proxy, "Manage proxies", "'proxy list' : list all proxy setups.\n" "'proxy add' : add a new proxy setup.\n" "'proxy remove ' : remove proxy setup with number index.\n" "'proxy use ' : use proxy with number index as default proxy.\n" "'proxy unuse' : don't use a default proxy.\n" "'proxy show ' : show configuration and status of the proxy numbered by index.\n" "'proxy show default' : show configuration and status of the default proxy.\n" }, { "record", lpc_cmd_record, "record to a wav file", "This feature is available only in file mode (see 'help soundcard')\n" "'record ' : record into wav file." }, { "resume", lpc_cmd_resume, "resume a call", "'resume' : resume the unique call\n" "'resume ' : hold off the call with given id\n" }, { "soundcard", lpc_cmd_soundcard, "Manage soundcards", "'soundcard list' : list all sound devices.\n" "'soundcard show' : show current sound devices configuration.\n" "'soundcard use ' : select a sound device.\n" "'soundcard use files' : use .wav files instead of soundcard\n" }, { "stun", lpc_cmd_stun, "Set stun server address", "'stun' : show stun settings.\n" "'stun ' : set stun server address.\n" }, { "terminate", lpc_cmd_terminate, "Terminate a call", "'terminate' : Terminate the current call\n" "'terminate ' : Terminate the call with supplied id\n" "'terminate ' : Terminate all the current calls\n" }, { "transfer", lpc_cmd_transfer, "Transfer a call to a specified destination.", "'transfer ' : transfers the current active call to the destination sip-uri\n" "'transfer ': transfers the call with 'id' to the destination sip-uri\n" "'transfer --to-call ': transfers the call with 'id1' to the destination of call 'id2' (attended transfer)\n" }, { "unmute", lpc_cmd_unmute_mic, "Unmute microphone and resume voice transmission.", NULL }, { "webcam", lpc_cmd_webcam, "Manage webcams", "'webcam list' : list all known devices.\n" "'webcam use ' : select a video device.\n" }, { "quit", lpc_cmd_quit, "Exit linphonec", NULL }, { (char *)NULL, (lpc_cmd_handler)NULL, (char *)NULL, (char *)NULL } }; static LPC_COMMAND advanced_commands[] = { { "codec", lpc_cmd_acodec, "Audio codec configuration", "'codec list' : list audio codecs\n" "'codec enable ' : enable available audio codec\n" "'codec disable ' : disable audio codec" }, { "vcodec", lpc_cmd_vcodec, "Video codec configuration", "'vcodec list' : list video codecs\n" "'vcodec enable ' : enable available video codec\n" "'vcodec disable ' : disable video codec" }, { "ec", lpc_cmd_echocancellation, "Echo cancellation", "'ec on [] [] []' : turn EC on with given delay, tail length and framesize\n" "'ec off' : turn echo cancellation (EC) off\n" "'ec show' : show EC status" }, { "el", lpc_cmd_echolimiter, "Echo limiter", "'el on turns on echo limiter (automatic half duplex, for cases where echo canceller cannot work)\n" "'el off' : turn echo limiter off\n" "'el show' : show echo limiter status" }, { "nortp-on-audio-mute", lpc_cmd_rtp_no_xmit_on_audio_mute, "Set the rtp_no_xmit_on_audio_mute configuration parameter", " If set to 1 then rtp transmission will be muted when\n" " audio is muted , otherwise rtp is always sent." }, #ifdef VIDEO_ENABLED { "vwindow", lpc_cmd_video_window, "Control video display window", "'vwindow show': shows video window\n" "'vwindow hide': hides video window\n" "'vwindow pos ': Moves video window to x,y pixel coordinates\n" "'vwindow size ': Resizes video window\n" "'vwindow id ': embeds video display into supplied window id." }, { "pwindow", lpc_cmd_preview_window, "Control local camera video display (preview window)", "'pwindow show': shows the local camera video display\n" "'pwindow hide': hides the local camera video display\n" "'pwindow pos ': Moves preview window to x,y pixel coordinates\n" "'pwindow size ': Resizes preview window\n" "'pwindow id ': embeds preview display into supplied window id.\n" "'pwindow integrated': integrate preview display within the video window of current call.\n" "'pwindow standalone': use standalone window for preview display." }, { "snapshot", lpc_cmd_snapshot, "Take a snapshot of currently received video stream", "'snapshot ': take a snapshot and records it in jpeg format into the supplied path\n" }, { "preview-snapshot", lpc_cmd_preview_snapshot, "Take a snapshot of currently captured video stream", "'preview-snapshot ': take a snapshot and records it in jpeg format into the supplied path\n" }, { "vfureq", lpc_cmd_vfureq, "Request the other side to send VFU for the current call", NULL }, #endif { "states", lpc_cmd_states, "Show internal states of liblinphone, registrations and calls, according to linphonecore.h definitions", "'states global': shows global state of liblinphone \n" "'states calls': shows state of calls\n" "'states proxies': shows state of proxy configurations" }, { "register", lpc_cmd_register, "Register in one line to a proxy" , "register " }, { "unregister", lpc_cmd_unregister, "Unregister from default proxy", NULL }, { "status", lpc_cmd_status, "Print various status information", "'status register' \t: print status concerning registration\n" "'status autoanswer'\t: tell whether autoanswer mode is enabled\n" "'status hook' \t: print hook status\n" }, { "ports", lpc_cmd_ports, "Network ports configuration", "'ports' \t: prints current used ports.\n" "'ports sip '\t: Sets the sip port.\n" }, { "param", lpc_cmd_param, "parameter set or read as normally given in .linphonerc", "'param
[]' \t: reads [sets] given parameter.\n" "NOTES: - changes may become effective after (re)establishing a sip connection.\n" " - upon exit, .linphonerc will reflect the updated state.\n" }, { "speak", lpc_cmd_speak, "Speak a sentence using espeak TTS engine", "This feature is available only in file mode. (see 'help soundcard')\n" "'speak ' : speak a text using the specified espeak voice.\n" "Example for english voice: 'speak default Hello my friend !'" }, { "staticpic", lpc_cmd_staticpic, "Manage static pictures when nowebcam", "'staticpic set' : Set path to picture that should be used.\n" "'staticpic fps' : Get/set frames per seconds for picture emission.\n" }, { "identify", lpc_cmd_identify, "Returns the user-agent string of far end", "'identify' \t: returns remote user-agent string for current call.\n" "'identify ' \t: returns remote user-agent string for call with supplied id.\n" }, { "ringback", lpc_cmd_ringback, "Specifies a ringback tone to be played to remote end during incoming calls", "'ringback '\t: Specifies a ringback tone to be played to remote end during incoming calls\n" "'ringback disable'\t: Disable playing of ringback tone to callers\n" }, { "redirect", lpc_cmd_redirect, "Redirect an incoming call", "'redirect '\t: Redirect the specified call to the \n" "'redirect all '\t: Redirect all pending incoming calls to the \n" }, { "zrtp-set-verified", lpc_cmd_zrtp_verified,"Set ZRTP SAS verified.", "'Set ZRTP SAS verified'\n" }, { "zrtp-set-unverified", lpc_cmd_zrtp_unverified,"Set ZRTP SAS not verified.", "'Set ZRTP SAS not verified'\n" }, { NULL,NULL,NULL,NULL} }; /*************************************************************************** * * Public interface * ***************************************************************************/ /* * Main command dispatcher. * WARNING: modifies second argument! * * Always return 1 currently. */ int linphonec_parse_command_line(LinphoneCore *lc, char *cl) { char *ptr=cl; char *args=NULL; LPC_COMMAND *cmd; /* Isolate first word and args */ while(*ptr && !isspace(*ptr)) ++ptr; if (*ptr) { *ptr='\0'; /* set args to first nonblank */ args=ptr+1; while(*args && isspace(*args)) ++args; } /* Handle DTMF */ if ( isdigit(*cl) || *cl == '#' || *cl == '*' ) { while ( isdigit(*cl) || *cl == '#' || *cl == '*' ) { if (linphone_core_get_current_call(lc)) linphone_call_send_dtmf(linphone_core_get_current_call(lc), *cl); linphone_core_play_dtmf (lc,*cl,100); ms_sleep(1); // be nice ++cl; } // discard spurious trailing chars return 1; } /* Handle other kind of commands */ cmd=lpc_find_command(cl); if ( !cmd ) { linphonec_out("'%s': Cannot understand this.\n", cl); return 1; } if ( ! cmd->func(lc, args) ) { linphonec_out("Syntax error.\n"); linphonec_display_command_help(cmd); } return 1; } /* * Generator function for command completion. * STATE let us know whether to start from scratch; * without any state (STATE==0), then we start at the * top of the list. */ char * linphonec_command_generator(const char *text, int state) { static size_t len; static int index, adv; char *name; if ( ! state ) { index=0; adv=0; len=strlen(text); } /* * Return the next name which partially matches * from the commands list */ if (adv==0){ while ((name=commands[index].name)) { ++index; /* so next call get next command */ if (strncmp(name, text, len) == 0) { return ortp_strdup(name); } } adv=1; index=0; } if (adv==1){ while ((name=advanced_commands[index].name)) { ++index; /* so next call get next command */ if (strncmp(name, text, len) == 0) { return ortp_strdup(name); } } } return NULL; } /*************************************************************************** * * Command handlers * ***************************************************************************/ static int lpc_cmd_help(LinphoneCore *lc, char *arg) { int i=0; LPC_COMMAND *cmd; if (!arg || !*arg) { linphonec_out("Commands are:\n"); linphonec_out("---------------------------\n"); while (commands[i].help) { linphonec_out("%10.10s\t%s\n", commands[i].name, commands[i].help); i++; } linphonec_out("---------------------------\n"); linphonec_out("Type 'help ' for more details or\n"); linphonec_out(" 'help advanced' to list additional commands.\n"); return 1; } if (strcmp(arg,"advanced")==0){ linphonec_out("Advanced commands are:\n"); linphonec_out("---------------------------\n"); i=0; while (advanced_commands[i].help) { linphonec_out("%20.20s\t%s\n", advanced_commands[i].name, advanced_commands[i].help); i++; } linphonec_out("---------------------------\n"); linphonec_out("Type 'help ' for more details.\n"); return 1; } cmd=lpc_find_command(arg); if ( !cmd ) { linphonec_out("No such command.\n"); return 1; } linphonec_display_command_help(cmd); return 1; } static char callee_name[256]={0}; static char caller_name[256]={0}; static int lpc_cmd_call(LinphoneCore *lc, char *args) { if ( ! args || ! *args ) { return 0; } { LinphoneCall *call; LinphoneCallParams *cp=linphone_core_create_call_params (lc, NULL); char *opt1,*opt2; if ( linphone_core_in_call(lc) ) { linphonec_out("Terminate or hold on the current call first.\n"); return 1; } opt1=strstr(args,"--audio-only"); opt2=strstr(args,"--early-media"); if (opt1){ opt1[0]='\0'; while(--opt1 > args && opt1[0]==' ') opt1[0]='\0'; linphone_call_params_enable_video (cp,FALSE); } if (opt2){ opt2[0]='\0'; while(--opt2 > args && opt2[0]==' ') opt2[0]='\0'; linphone_call_params_enable_early_media_sending(cp,TRUE); } if ( NULL == (call=linphone_core_invite_with_params(lc, args,cp)) ) { linphonec_out("Error from linphone_core_invite.\n"); } else { snprintf(callee_name,sizeof(callee_name),"%s",args); } linphone_call_params_unref(cp); } return 1; } static int lpc_cmd_calls(LinphoneCore *lc, char *args){ const bctbx_list_t *calls = linphone_core_get_calls(lc); if(calls) { lpc_display_call_states(lc); }else { linphonec_out("No active call.\n"); } return 1; } static int lpc_cmd_chat(LinphoneCore *lc, char *args) { char *arg1 = args; char *arg2 = NULL; char *ptr = args; LinphoneChatRoom *cr; if (!args) return 0; /* Isolate first and second arg */ while(*ptr && !isspace(*ptr)) ++ptr; if ( *ptr ) { *ptr='\0'; arg2=ptr+1; while(*arg2 && isspace(*arg2)) ++arg2; } else { /* missing one parameter */ return 0; } cr = linphone_core_get_chat_room_from_uri(lc,arg1); linphone_chat_room_send_message(cr,arg2); return 1; } const char *linphonec_get_callee(void){ return callee_name; } const char *linphonec_get_caller(void){ return caller_name; } void linphonec_set_caller(const char *caller){ snprintf(caller_name,sizeof(caller_name)-1,"%s",caller); } static int lpc_cmd_transfer(LinphoneCore *lc, char *args) { if (args){ LinphoneCall *call; LinphoneCall *call2; const char *refer_to=NULL; char arg1[256]={0}; char arg2[266]={0}; int id2=0; int n=sscanf(args,"%255s %265s %d",arg1,arg2,&id2); if (n==1 || isalpha(*arg1)){ call=linphone_core_get_current_call(lc); if (call==NULL && bctbx_list_size(linphone_core_get_calls(lc))==1){ call=(LinphoneCall*)linphone_core_get_calls(lc)->data; } refer_to=args; if (call==NULL){ linphonec_out("No active call, please specify a call id among the ones listed by 'calls' command.\n"); return 0; } linphone_call_transfer(call, refer_to); }else if (n==2){ int id=atoi(arg1); refer_to=args+strlen(arg1)+1; call=linphonec_get_call(id); if (call==NULL) return 0; linphone_call_transfer(call, refer_to); }else if (n==3){ int id=atoi(arg1); call=linphonec_get_call(id); call2=linphonec_get_call(id2); if (call==NULL || call2==NULL) return 0; if (strcmp(arg2,"--to-call")!=0){ return 0; } linphonec_out("Performing attended transfer of call %i to call %i",id,id2); linphone_call_transfer_to_another(call,call2); }else return 0; }else{ linphonec_out("Transfer command requires at least one argument\n"); return 0; } return 1; } static int lpc_cmd_terminate(LinphoneCore *lc, char *args) { if (linphone_core_get_calls(lc)==NULL){ linphonec_out("No active calls\n"); return 1; } if (!args) { if ( -1 == linphone_core_terminate_call(lc, NULL) ){ linphonec_out("Could not stop the active call.\n"); } return 1; } if(strcmp(args,"all")==0){ linphonec_out("We are going to stop all the calls.\n"); linphone_core_terminate_all_calls(lc); return 1; }else{ /*the argument is a linphonec call id */ int id=atoi(args); LinphoneCall *call=linphonec_get_call(id); if (call){ if (linphone_call_terminate(call)==-1){ linphonec_out("Could not stop the call with id %li\n",id); } }else return 0; return 1; } return 0; } static int lpc_cmd_redirect(LinphoneCore *lc, char *args){ const bctbx_list_t *elem; int didit=0; if (!args) return 0; if ((elem=linphone_core_get_calls(lc))==NULL){ linphonec_out("No active calls.\n"); return 1; } if (strncmp(args, "all ", 4) == 0) { while(elem!=NULL){ LinphoneCall *call=(LinphoneCall*)elem->data; if (linphone_call_get_state(call)==LinphoneCallIncomingReceived){ if (linphone_call_redirect(call,args+4) != 0) { linphonec_out("Could not redirect call.\n"); elem=elem->next; } else { didit=1; /*as the redirection closes the call, we need to re-check the call list that is invalidated.*/ elem=linphone_core_get_calls(lc); } }else elem=elem->next; } if (didit==0){ linphonec_out("There is no pending incoming call to redirect.\n"); } } else { char space; int id; int charRead; if ( sscanf(args, "%d%c%n", &id, &space, &charRead) == 2 && space == ' ') { LinphoneCall * call = linphonec_get_call(id); if ( call != NULL ) { if (linphone_call_get_state(call)!=LinphoneCallIncomingReceived) { linphonec_out("The state of the call is not incoming, can't be redirected.\n"); } else if (linphone_call_redirect(call,args+charRead) != 0) { linphonec_out("Could not redirect call.\n"); } } } else return 0; } return 1; } static int lpc_cmd_answer(LinphoneCore *lc, char *args){ if (!args) { int nb=(int)bctbx_list_size(linphone_core_get_calls(lc)); if (nb==1){ //if just one call is present answer the only one in passing NULL to the linphone_core_accept_call ... if ( -1 == linphone_core_accept_call(lc, NULL) ) { linphonec_out("Fail to accept incoming call\n"); } }else if (nb==0){ linphonec_out("There are no calls to answer.\n"); }else{ linphonec_out("Multiple calls in progress, please specify call id.\n"); return 0; } return 1; }else{ int id; if (sscanf(args,"%d",&id)==1){ LinphoneCall *call=linphonec_get_call (id); if (linphone_call_accept(call)==-1){ linphonec_out("Fail to accept call %i\n",id); } }else return 0; return 1; } return 0; } static int lpc_cmd_autoanswer(LinphoneCore *lc, char *args) { if ( ! args ) { if ( linphonec_get_autoanswer() ) { linphonec_out("Auto answer is enabled. Use 'autoanswer disable' to disable.\n"); } else { linphonec_out("Auto answer is disabled. Use 'autoanswer enable' to enable.\n"); } return 1; } if (strstr(args,"enable")){ linphonec_set_autoanswer(TRUE); linphonec_out("Auto answer enabled.\n"); }else if (strstr(args,"disable")){ linphonec_set_autoanswer(FALSE); linphonec_out("Auto answer disabled.\n"); }else return 0; return 1; } static int lpc_cmd_quit(LinphoneCore *lc, char *args) { linphonec_main_loop_exit(); return 1; } static int lpc_cmd_nat(LinphoneCore *lc, char *args) { bool_t use; const char *nat; if ( args ) args=lpc_strip_blanks(args); if ( args && *args ) { linphone_core_set_nat_address(lc, args); /* linphone_core_set_firewall_policy(lc,LINPHONE_POLICY_USE_NAT_ADDRESS); */ } nat = linphone_core_get_nat_address(lc); use = linphone_core_get_firewall_policy(lc)==LinphonePolicyUseNatAddress; linphonec_out("Nat address: %s%s\n", nat ? nat : "unspecified" , use ? "" : " (disabled - use 'firewall nat' to enable)"); return 1; } static int lpc_cmd_stun(LinphoneCore *lc, char *args) { bool_t use; const char *stun; if ( args ) args=lpc_strip_blanks(args); if ( args && *args ) { linphone_core_set_stun_server(lc, args); /* linphone_core_set_firewall_policy(lc,LINPHONE_POLICY_USE_STUN); */ } stun = linphone_core_get_stun_server(lc); use = linphone_core_get_firewall_policy(lc)==LinphonePolicyUseStun; linphonec_out("Stun server: %s%s\n", stun ? stun : "unspecified" , use? "" : " (disabled - use 'firewall stun' to enable)"); return 1; } static int lpc_cmd_firewall(LinphoneCore *lc, char *args) { const char* setting=NULL; if ( args ) args=lpc_strip_blanks(args); if ( args && *args ) { if (strcmp(args,"none")==0) { linphone_core_set_firewall_policy(lc,LinphonePolicyNoFirewall); } else if (strcmp(args,"upnp")==0) { linphone_core_set_firewall_policy(lc,LinphonePolicyUseUpnp); } else if (strcmp(args,"ice")==0) { setting = linphone_core_get_stun_server(lc); if ( ! setting ) { linphonec_out("No stun server address is defined, use 'stun
' first\n"); return 1; } linphone_core_set_firewall_policy(lc,LinphonePolicyUseIce); } else if (strcmp(args,"stun")==0) { setting = linphone_core_get_stun_server(lc); if ( ! setting ) { linphonec_out("No stun server address is defined, use 'stun
' first\n"); return 1; } linphone_core_set_firewall_policy(lc,LinphonePolicyUseStun); } else if (strcmp(args,"nat")==0) { setting = linphone_core_get_nat_address(lc); if ( ! setting ) { linphonec_out("No nat address is defined, use 'nat
' first"); return 1; } linphone_core_set_firewall_policy(lc,LinphonePolicyUseNatAddress); setting = linphone_core_get_nat_address(lc); } } switch(linphone_core_get_firewall_policy(lc)) { case LinphonePolicyNoFirewall: linphonec_out("No firewall\n"); break; case LinphonePolicyUseStun: linphonec_out("Using stun server %s to discover firewall address\n", setting ? setting : linphone_core_get_stun_server(lc)); break; case LinphonePolicyUseNatAddress: linphonec_out("Using supplied nat address %s.\n", setting ? setting : linphone_core_get_nat_address(lc)); break; case LinphonePolicyUseIce: linphonec_out("Using ice with stun server %s to discover firewall address\n", setting ? setting : linphone_core_get_stun_server(lc)); break; case LinphonePolicyUseUpnp: linphonec_out("Using uPnP IGD protocol\n"); break; } return 1; } #ifndef _WIN32 /* Helper function for processing freind names */ static int lpc_friend_name(char **args, char **name) { /* Use space as a terminator unless quoted */ if (('"' == **args) || ('\'' == **args)){ char *end; char delim = **args; (*args)++; end = (*args); while ((delim != *end) && ('\0' != *end)) end++; if ('\0' == *end) { fprintf(stderr, "Mismatched quotes\n"); return 0; } *name = *args; *end = '\0'; *args = ++end; } else { *name = strsep(args, " "); if (NULL == *args) { /* Means there was no separator */ fprintf(stderr, "Either name or address is missing\n"); return 0; } if (NULL == *name) return 0; } return 1; } #endif static int lpc_cmd_friend(LinphoneCore *lc, char *args) { int friend_num; if ( args ) args=lpc_strip_blanks(args); if ( ! args || ! *args ) return 0; if ( !strncmp(args, "list", 4) ) { return linphonec_friend_list(lc, args+4); return 1; } else if ( !strncmp(args, "call", 4) ) { args+=4; if ( ! *args ) return 0; friend_num = (int)strtol(args, NULL, 10); #ifndef _WIN32_WCE if ( errno == ERANGE ) { linphonec_out("Invalid friend number\n"); return 0; } #endif /*_WIN32_WCE*/ linphonec_friend_call(lc, (unsigned int)friend_num); return 1; } else if ( !strncmp(args, "delete", 6) ) { args+=6; if ( ! *args ) return 0; while (*args == ' ') args++; if ( ! *args ) return 0; if (!strncmp(args, "all", 3)) { friend_num = -1; } else { friend_num = (int)strtol(args, NULL, 10); #ifndef _WIN32_WCE if ( errno == ERANGE ) { linphonec_out("Invalid friend number\n"); return 0; } #endif /*_WIN32_WCE*/ } linphonec_friend_delete(lc, friend_num); return 1; } else if ( !strncmp(args, "add", 3) ) { #ifndef _WIN32 char *name; char addr[80]; char *addr_p = addr; char *addr_orig; args+=3; if ( ! *args ) return 0; while (*args == ' ') args++; if ( ! *args ) return 0; if (!lpc_friend_name(&args, &name)) return 0; while (*args == ' ') args++; if ( ! *args ) return 0; if (isdigit(*args)) { strcpy (addr, "sip:"); addr_p = addr + strlen("sip:"); } addr_orig = strsep(&args, " "); if (1 >= strlen(addr_orig)) { fprintf(stderr, "A single-digit address is not valid\n"); return 0; } strcpy(addr_p, addr_orig); linphonec_friend_add(lc, name, addr); #else LinphoneFriend *new_friend; new_friend = linphone_core_create_friend_with_address(lc, args); linphone_core_add_friend(lc, new_friend); #endif return 1; } return 0; } static int lpc_cmd_play(LinphoneCore *lc, char *args){ if ( args ) args=lpc_strip_blanks(args); if ( ! args || ! *args ) return 0; linphone_core_set_play_file(lc,args); return 1; } static int lpc_cmd_record(LinphoneCore *lc, char *args){ if ( args ) args=lpc_strip_blanks(args); if ( ! args || ! *args ) return 0; linphone_core_set_record_file(lc,args); return 1; } /* * Modified input */ static int lpc_cmd_proxy(LinphoneCore *lc, char *args) { char *arg1 = args; char *arg2 = NULL; char *ptr = args; int proxynum; if ( ! arg1 ) return 0; /* Isolate first and second arg */ while(*ptr && !isspace(*ptr)) ++ptr; if ( *ptr ) { *ptr='\0'; arg2=ptr+1; while(*arg2 && isspace(*arg2)) ++arg2; } if (strcmp(arg1,"add")==0) { #ifdef HAVE_READLINE rl_inhibit_completion=1; #endif linphonec_proxy_add(lc); #ifdef HAVE_READLINE rl_inhibit_completion=0; #endif } else if (strcmp(arg1,"list")==0) { linphonec_proxy_list(lc); } else if (strcmp(arg1,"remove")==0) { if (arg2==NULL) return 0; linphonec_proxy_remove(lc,atoi(arg2)); } else if (strcmp(arg1,"use")==0) { if ( arg2 && *arg2 ) { proxynum=atoi(arg2); if ( linphonec_proxy_use(lc, proxynum) ) linphonec_out("Default proxy set to %d.\n", proxynum); } else { proxynum=linphone_core_get_default_proxy(lc, NULL); if ( proxynum == -1 ) linphonec_out("No default proxy.\n"); else linphonec_out("Current default proxy is %d.\n", proxynum); } }else if (strcmp(arg1, "unuse")==0){ linphone_core_set_default_proxy(lc, NULL); linphonec_out("Use no proxy.\n"); } else if (strcmp(arg1, "show")==0) { if (arg2 && *arg2) { if (strstr(arg2,"default")) { proxynum=linphone_core_get_default_proxy(lc, NULL); if ( proxynum < 0 ) { linphonec_out("No default proxy defined\n"); return 1; } linphonec_proxy_show(lc,proxynum); } else { linphonec_proxy_show(lc, atoi(arg2)); } } else return 0; /* syntax error */ } else { return 0; /* syntax error */ } return 1; } static int lpc_cmd_call_logs(LinphoneCore *lc, char *args) { const bctbx_list_t *elem=linphone_core_get_call_logs(lc); for (;elem!=NULL;elem=bctbx_list_next(elem)) { LinphoneCallLog *cl=(LinphoneCallLog*)elem->data; char *str=linphone_call_log_to_str(cl); linphonec_out("%s\n",str); ms_free(str); } return 1; } static int lpc_cmd_ipv6(LinphoneCore *lc, char *arg1) { if ( ! arg1 ) { return 0; /* syntax error */ } if (strcmp(arg1,"status")==0) { linphonec_out("ipv6 use enabled: %s\n",linphone_core_ipv6_enabled(lc) ? "true":"false"); } else if (strcmp(arg1,"enable")==0) { linphone_core_enable_ipv6(lc,TRUE); linphonec_out("ipv6 use enabled.\n"); } else if (strcmp(arg1,"disable")==0) { linphone_core_enable_ipv6(lc,FALSE); linphonec_out("ipv6 use disabled.\n"); } else { return 0; /* syntax error */ } return 1; } static int devname_to_index(LinphoneCore *lc, const char *devname){ const char **p; int i; for(i=0,p=linphone_core_get_sound_devices(lc);*p!=NULL;++p,++i){ if (strcmp(devname,*p)==0) return i; } return -1; } static const char *index_to_devname(LinphoneCore *lc, int index){ const char **p; int i; for(i=0,p=linphone_core_get_sound_devices(lc);*p!=NULL;++p,++i){ if (i==index) return *p; } return NULL; } static int lpc_cmd_soundcard(LinphoneCore *lc, char *args) { int i, index; const char **dev; char *arg1 = args; char *arg2 = NULL; char *ptr = args; if (!args) return 0; /* syntax error */ /* Isolate first and second arg */ while(*ptr && !isspace(*ptr)) ++ptr; if ( *ptr ) { *ptr='\0'; arg2=ptr+1; while(*arg2 && isspace(*arg2)) ++arg2; } if (strcmp(arg1, "list")==0) { dev=linphone_core_get_sound_devices(lc); for(i=0; dev[i]!=NULL; ++i){ linphonec_out("%i: %s\n",i,dev[i]); } return 1; } if (strcmp(arg1, "show")==0) { if (linphone_core_get_use_files(lc)) { linphonec_out("Using files.\n"); } else { linphonec_out("Ringer device: %s\n", linphone_core_get_ringer_device(lc)); linphonec_out("Playback device: %s\n", linphone_core_get_playback_device(lc)); linphonec_out("Capture device: %s\n", linphone_core_get_capture_device(lc)); } return 1; } if (strcmp(arg1, "use")==0 && arg2) { if (strcmp(arg2, "files")==0) { linphonec_out("Using wav files instead of soundcard.\n"); linphone_core_use_files(lc,TRUE); return 1; } linphone_core_use_files(lc,FALSE); dev=linphone_core_get_sound_devices(lc); index=atoi(arg2); /* FIXME: handle not-a-number */ for(i=0;dev[i]!=NULL;i++) { if (i!=index) continue; linphone_core_set_ringer_device(lc,dev[i]); linphone_core_set_playback_device(lc,dev[i]); linphone_core_set_capture_device(lc,dev[i]); linphonec_out("Using sound device %s\n",dev[i]); return 1; } linphonec_out("No such sound device\n"); return 1; } if (strcmp(arg1, "capture")==0) { const char *devname=linphone_core_get_capture_device(lc); if (!arg2){ linphonec_out("Using capture device #%i (%s)\n", devname_to_index(lc,devname),devname); }else{ index=atoi(arg2); /* FIXME: handle not-a-number */ devname=index_to_devname(lc,index); if (devname!=NULL){ linphone_core_set_capture_device(lc,devname); linphonec_out("Using capture sound device %s\n",devname); return 1; } linphonec_out("No such sound device\n"); } return 1; } if (strcmp(arg1, "playback")==0) { const char *devname=linphone_core_get_playback_device(lc); if (!arg2){ linphonec_out("Using playback device #%i (%s)\n", devname_to_index(lc,devname),devname); }else{ index=atoi(arg2); /* FIXME: handle not-a-number */ devname=index_to_devname(lc,index); if (devname!=NULL){ linphone_core_set_playback_device(lc,devname); linphonec_out("Using playback sound device %s\n",devname); return 1; } linphonec_out("No such sound device\n"); } return 1; } if (strcmp(arg1, "ring")==0) { const char *devname=linphone_core_get_ringer_device(lc); if (!arg2){ linphonec_out("Using ring device #%i (%s)\n", devname_to_index(lc,devname),devname); }else{ index=atoi(arg2); /* FIXME: handle not-a-number */ devname=index_to_devname(lc,index); if (devname!=NULL){ linphone_core_set_ringer_device(lc,devname); linphonec_out("Using ring sound device %s\n",devname); return 1; } linphonec_out("No such sound device\n"); } return 1; } return 0; /* syntax error */ } static int lpc_cmd_webcam(LinphoneCore *lc, char *args) { int i, index; const char **dev; char *arg1 = args; char *arg2 = NULL; char *ptr = args; if (!args) return 0; /* syntax error */ /* Isolate first and second arg */ while(*ptr && !isspace(*ptr)) ++ptr; if ( *ptr ) { *ptr='\0'; arg2=ptr+1; while(*arg2 && isspace(*arg2)) ++arg2; } if (strcmp(arg1, "list")==0) { dev=linphone_core_get_video_devices(lc); for(i=0; dev[i]!=NULL; ++i){ linphonec_out("%i: %s\n",i,dev[i]); } return 1; } if (strcmp(arg1, "use")==0 && arg2) { dev=linphone_core_get_video_devices(lc); index=atoi(arg2); /* FIXME: handle not-a-number */ for(i=0;dev[i]!=NULL;i++) { if (i!=index) continue; linphone_core_set_video_device(lc, dev[i]); linphonec_out("Using video device %s\n",dev[i]); return 1; } linphonec_out("No such video device\n"); return 1; } return 0; /* syntax error */ } static int lpc_cmd_staticpic(LinphoneCore *lc, char *args) { char *arg1 = args; char *arg2 = NULL; char *ptr = args; if (!args) return 0; /* Syntax error */ /* Isolate first and second arg */ while(*ptr && !isspace(*ptr)) ++ptr; if ( *ptr ) { *ptr='\0'; arg2=ptr+1; while(*arg2 && isspace(*arg2)) ++arg2; } if (strcmp(arg1, "set")==0 && arg2) { linphone_core_set_static_picture(lc, arg2); return 1; } if (strcmp(arg1, "fps")==0) { if (arg2) { float fps = (float)atof(arg2); /* FIXME: Handle not-a-float */ linphone_core_set_static_picture_fps(lc, fps); return 1; } else { float fps; fps = linphone_core_get_static_picture_fps(lc); linphonec_out("Current FPS %f\n", fps); return 1; } } return 0; /* Syntax error */ } static int lpc_cmd_pause(LinphoneCore *lc, char *args){ if(linphone_core_in_call(lc)) { linphone_call_pause(linphone_core_get_current_call(lc)); return 1; } linphonec_out("you can only pause when a call is in process\n"); return 0; } static int lpc_cmd_resume(LinphoneCore *lc, char *args){ if(linphone_core_in_call(lc)) { linphonec_out("There is already a call in process pause or stop it first"); return 1; } if (args) { int id; int n = sscanf(args, "%d", &id); if (n == 1){ LinphoneCall *call=linphonec_get_call(id); if (call){ if(linphone_call_resume(call)==-1){ linphonec_out("There was a problem to resume the call check the remote address you gave %s\n",args); } } return 1; }else return 0; } else { const bctbx_list_t *calls = linphone_core_get_calls(lc); int nbcalls=(int)bctbx_list_size(calls); if( nbcalls == 1) { if(linphone_call_resume(calls->data) < 0) { linphonec_out("There was a problem to resume the unique call.\n"); } return 1; }else if (nbcalls==0){ linphonec_out("There is no calls at this time.\n"); return 1; }else{ linphonec_out("There are %i calls at this time, please specify call id as given with 'calls' command.\n"); } } return 0; } static int lpc_cmd_conference(LinphoneCore *lc, char *args){ int id; char subcommand[32]={0}; int n; if (args==NULL) return 0; n=sscanf(args, "%31s %d", subcommand,&id); if (n == 2){ LinphoneCall *call=linphonec_get_call(id); if (call==NULL) return 1; if (strcmp(subcommand,"add")==0){ linphone_core_add_to_conference(lc,call); return 1; }else if (strcmp(subcommand,"rm")==0){ linphone_core_remove_from_conference(lc,call); return 1; }else if (strcmp(subcommand,"enter")==0){ linphone_core_enter_conference(lc); return 1; }else if (strcmp(subcommand,"leave")==0){ linphone_core_leave_conference(lc); return 1; } } return 0; } /*************************************************************************** * * Commands helper functions * ***************************************************************************/ static void linphonec_proxy_add(LinphoneCore *lc) { bool_t enable_register=FALSE; LinphoneProxyConfig *cfg; linphonec_out("Adding new proxy setup. Hit ^D to abort.\n"); /* * SIP Proxy address */ while (1) { char *input=linphonec_readline("Enter proxy sip address: "); char *clean; if ( ! input ) { linphonec_out("Aborted.\n"); return; } /* Strip blanks */ clean=lpc_strip_blanks(input); if ( ! *clean ) { free(input); continue; } cfg=linphone_core_create_proxy_config(lc); if (linphone_proxy_config_set_server_addr(cfg,clean)<0) { linphonec_out("Invalid sip address (sip:sip.domain.tld).\n"); free(input); linphone_proxy_config_destroy(cfg); continue; } free(input); break; } /* * SIP Proxy identity */ while (1) { char *input=linphonec_readline("Your identity for this proxy: "); char *clean; if ( ! input ) { linphonec_out("Aborted.\n"); linphone_proxy_config_destroy(cfg); return; } /* Strip blanks */ clean=lpc_strip_blanks(input); if ( ! *clean ) { free(input); continue; } linphone_proxy_config_set_identity(cfg, clean); if ( ! linphone_proxy_config_get_identity (cfg)) { linphonec_out("Invalid identity (sip:name@sip.domain.tld).\n"); free(input); continue; } free(input); break; } /* * SIP Proxy enable register */ while (1) { char *input=linphonec_readline("Do you want to register on this proxy (yes/no): "); char *clean; if ( ! input ) { linphonec_out("Aborted.\n"); linphone_proxy_config_destroy(cfg); return; } /* Strip blanks */ clean=lpc_strip_blanks(input); if ( ! *clean ) { free(input); continue; } if ( ! strcmp(clean, "yes") ) enable_register=TRUE; else if ( ! strcmp(clean, "no") ) enable_register=FALSE; else { linphonec_out("Please answer with 'yes' or 'no'\n"); free(input); continue; } linphone_proxy_config_enableregister(cfg, enable_register); free(input); break; } /* * SIP Proxy registration expiration */ if ( enable_register==TRUE ) { int expires=0; while (1) { char *input=linphonec_readline("Specify register expiration time" " in seconds (default is 600): "); if ( ! input ) { linphonec_out("Aborted.\n"); linphone_proxy_config_destroy(cfg); return; } expires=atoi(input); if (expires==0) expires=600; linphone_proxy_config_set_expires(cfg, expires); linphonec_out("Expiration: %d seconds\n", linphone_proxy_config_get_expires (cfg)); free(input); break; } } /* * SIP proxy route */ while (1) { char *input=linphonec_readline("Specify route if needed: "); char *clean; if ( ! input ) { linphonec_out("Aborted.\n"); linphone_proxy_config_destroy(cfg); return; } /* Strip blanks */ clean=lpc_strip_blanks(input); if ( ! *clean ) { free(input); linphonec_out("No route specified.\n"); break; } linphone_proxy_config_set_route(cfg, clean); if ( ! linphone_proxy_config_get_route(cfg) ) { linphonec_out("Invalid route.\n"); free(input); continue; } free(input); break; } /* * Final confirmation */ while (1) { char *input; char *clean; linphonec_out("--------------------------------------------\n"); linphonec_proxy_display(cfg); linphonec_out("--------------------------------------------\n"); input=linphonec_readline("Accept the above proxy configuration (yes/no) ?: "); if ( ! input ) { linphonec_out("Aborted.\n"); linphone_proxy_config_destroy(cfg); return; } /* Strip blanks */ clean=lpc_strip_blanks(input); if ( ! *clean ) { free(input); continue; } if ( ! strcmp(clean, "yes") ) break; else if ( ! strcmp(clean, "no") ) { linphonec_out("Declined.\n"); linphone_proxy_config_destroy(cfg); free(input); return; } linphonec_out("Please answer with 'yes' or 'no'\n"); free(input); continue; } linphone_core_add_proxy_config(lc,cfg); /* automatically set the last entered proxy as the default one */ linphone_core_set_default_proxy(lc,cfg); linphonec_out("Proxy added.\n"); } static void linphonec_proxy_display(LinphoneProxyConfig *cfg) { const char *route=linphone_proxy_config_get_route(cfg); const char *identity=linphone_proxy_config_get_identity(cfg); linphonec_out("sip address: %s\nroute: %s\nidentity: %s\nregister: %s\nexpires: %i\nregistered: %s\n", linphone_proxy_config_get_addr(cfg), (route!=NULL)? route:"", (identity!=NULL)?identity:"", linphone_proxy_config_register_enabled (cfg)?"yes":"no", linphone_proxy_config_get_expires (cfg), linphone_proxy_config_is_registered(cfg) ? "yes" : "no"); } static void linphonec_proxy_show(LinphoneCore *lc, int index) { const bctbx_list_t *elem; int i; for(elem=linphone_core_get_proxy_config_list(lc),i=0;elem!=NULL;elem=elem->next,++i){ if (index==i){ LinphoneProxyConfig *cfg=(LinphoneProxyConfig *)elem->data; linphonec_proxy_display(cfg); return; } } linphonec_out("No proxy with index %i\n", index); } static void linphonec_proxy_list(LinphoneCore *lc) { const bctbx_list_t *proxies; int n; int def=linphone_core_get_default_proxy(lc,NULL); proxies=linphone_core_get_proxy_config_list(lc); for(n=0;proxies!=NULL;proxies=bctbx_list_next(proxies),n++){ if (n==def) linphonec_out("****** Proxy %i - this is the default one - *******\n",n); else linphonec_out("****** Proxy %i *******\n",n); linphonec_proxy_display((LinphoneProxyConfig*)proxies->data); } if ( ! n ) linphonec_out("No proxies defined\n"); } static void linphonec_proxy_remove(LinphoneCore *lc, int index) { const bctbx_list_t *proxies; LinphoneProxyConfig *cfg; proxies=linphone_core_get_proxy_config_list(lc); cfg=(LinphoneProxyConfig*)bctbx_list_nth_data(proxies,index); if (cfg==NULL){ linphonec_out("No such proxy.\n"); return; } linphone_core_remove_proxy_config(lc,cfg); linphonec_out("Proxy %s removed.\n", linphone_proxy_config_get_addr(cfg)); } static int linphonec_proxy_use(LinphoneCore *lc, int index) { const bctbx_list_t *proxies; LinphoneProxyConfig *cfg; proxies=linphone_core_get_proxy_config_list(lc); cfg=(LinphoneProxyConfig*)bctbx_list_nth_data(proxies,index); if (cfg==NULL){ linphonec_out("No such proxy (try 'proxy list')."); return 0; } linphone_core_set_default_proxy(lc,cfg); return 1; } static void linphonec_friend_display(LinphoneFriend *fr) { const LinphoneAddress *addr = linphone_friend_get_address(fr); char *str = NULL; linphonec_out("name: %s\n", linphone_friend_get_name(fr)); if (addr) str = linphone_address_as_string_uri_only(addr); linphonec_out("address: %s\n", str); if (str) ms_free(str); } static int linphonec_friend_list(LinphoneCore *lc, char *pat) { const bctbx_list_t *friend; int n; if (pat) { pat=lpc_strip_blanks(pat); if (!*pat) pat = NULL; } friend = linphone_core_get_friend_list(lc); for(n=0; friend!=NULL; friend=bctbx_list_next(friend), ++n ) { if ( pat ) { const char *name = linphone_friend_get_name((LinphoneFriend *)friend->data); if (name && !strstr(name, pat)) continue; } linphonec_out("****** Friend %i *******\n",n); linphonec_friend_display((LinphoneFriend*)friend->data); } return 1; } static int linphonec_friend_call(LinphoneCore *lc, unsigned int num) { const bctbx_list_t *friend = linphone_core_get_friend_list(lc); unsigned int n; char *addr_str; for(n=0; friend!=NULL; friend=bctbx_list_next(friend), ++n ) { if ( n == num ) { int ret; const LinphoneAddress *addr = linphone_friend_get_address((LinphoneFriend*)friend->data); if (addr) { addr_str = linphone_address_as_string(addr); ret=lpc_cmd_call(lc, addr_str); ms_free(addr_str); return ret; } else { linphonec_out("Friend %u does not have an address\n", num); } } } linphonec_out("No such friend %u\n", num); return 1; } #ifndef _WIN32 static int linphonec_friend_add(LinphoneCore *lc, const char *name, const char *addr) { LinphoneFriend *newFriend; char url[PATH_MAX]; snprintf(url, PATH_MAX, "%s <%s>", name, addr); newFriend = linphone_core_create_friend_with_address(lc, url); linphone_core_add_friend(lc, newFriend); return 0; } #endif static int linphonec_friend_delete(LinphoneCore *lc, int num) { const bctbx_list_t *friend = linphone_core_get_friend_list(lc); int n; for(n=0; friend!=NULL; friend=bctbx_list_next(friend), ++n ) { if ( n == num ) { linphone_core_remove_friend(lc, friend->data); return 0; } } if (-1 == num) { int i; for (i = 0 ; i < n ; i++) linphonec_friend_delete(lc, 0); return 0; } linphonec_out("No such friend %i\n", num); return 1; } static void linphonec_display_command_help(LPC_COMMAND *cmd) { if ( cmd->doc ) linphonec_out ("%s\n", cmd->doc); else linphonec_out("%s\n", cmd->help); } static int lpc_cmd_register(LinphoneCore *lc, char *args){ char identity[512]; char proxy[512]; char passwd[512]; LinphoneProxyConfig *cfg; const bctbx_list_t *elem; if (!args) { /* it means that you want to register the default proxy */ LinphoneProxyConfig *cfg = linphone_core_get_default_proxy_config(lc); if (cfg) { if(!linphone_proxy_config_is_registered(cfg)) { linphone_proxy_config_enable_register(cfg,TRUE); linphone_proxy_config_done(cfg); }else{ linphonec_out("default proxy already registered\n"); } }else{ linphonec_out("we do not have a default proxy\n"); return 0; } return 1; } passwd[0]=proxy[0]=identity[0]='\0'; sscanf(args,"%511s %511s %511s",identity,proxy,passwd); if (proxy[0]=='\0' || identity[0]=='\0'){ linphonec_out("Missing parameters, see help register\n"); return 1; } if (passwd[0]!='\0'){ LinphoneAddress *from; LinphoneAuthInfo *info; if ((from=linphone_address_new(identity))!=NULL){ info=linphone_auth_info_new(linphone_address_get_username(from),NULL,passwd,NULL,NULL,linphone_address_get_username(from)); linphone_core_add_auth_info(lc,info); linphone_address_unref(from); linphone_auth_info_unref(info); } } elem=linphone_core_get_proxy_config_list(lc); if (elem) { cfg=(LinphoneProxyConfig*)elem->data; linphone_proxy_config_edit(cfg); } else cfg=linphone_core_create_proxy_config(lc); linphone_proxy_config_set_identity(cfg,identity); linphone_proxy_config_set_server_addr(cfg,proxy); linphone_proxy_config_enable_register(cfg,TRUE); if (elem) linphone_proxy_config_done(cfg); else linphone_core_add_proxy_config(lc,cfg); linphone_core_set_default_proxy(lc,cfg); return 1; } static int lpc_cmd_unregister(LinphoneCore *lc, char *args){ LinphoneProxyConfig *cfg = linphone_core_get_default_proxy_config(lc); if (cfg && linphone_proxy_config_is_registered(cfg)) { linphone_proxy_config_edit(cfg); linphone_proxy_config_enable_register(cfg,FALSE); linphone_proxy_config_done(cfg); }else{ linphonec_out("unregistered\n"); } return 1; } static int lpc_cmd_duration(LinphoneCore *lc, char *args){ LinphoneCallLog *cl; const bctbx_list_t *elem=linphone_core_get_call_logs(lc); for(;elem!=NULL;elem=elem->next){ if (elem->next==NULL){ cl=(LinphoneCallLog*)elem->data; linphonec_out("%i seconds\n",linphone_call_log_get_duration(cl)); } } return 1; } static int lpc_cmd_status(LinphoneCore *lc, char *args) { LinphoneProxyConfig *cfg; if ( ! args ) return 0; cfg = linphone_core_get_default_proxy_config(lc); if (strstr(args,"register")) { if (cfg) { if (linphone_proxy_config_is_registered(cfg)){ linphonec_out("registered, identity=%s duration=%i\n", linphone_proxy_config_get_identity(cfg), linphone_proxy_config_get_expires(cfg)); }else if (linphone_proxy_config_register_enabled(cfg)){ linphonec_out("registered=-1\n"); }else linphonec_out("registered=0\n"); } else linphonec_out("registered=0\n"); } else if (strstr(args,"autoanswer")) { if (cfg && linphone_proxy_config_is_registered(cfg)) linphonec_out("autoanswer=%i\n",linphonec_get_autoanswer()); else linphonec_out("unregistered\n"); } else if (strstr(args,"hook")) { LinphoneCall *call=linphone_core_get_current_call (lc); LinphoneCallState call_state=LinphoneCallIdle; if (call) call_state=linphone_call_get_state(call); switch(call_state){ case LinphoneCallOutgoingInit: linphonec_out("hook=outgoing_init sip:%s\n",linphonec_get_callee()); break; case LinphoneCallOutgoingProgress: linphonec_out("hook=dialing sip:%s\n",linphonec_get_callee()); break; case LinphoneCallOutgoingRinging: linphonec_out("hook=ringing sip:%s\n",linphonec_get_callee()); break; case LinphoneCallPaused: linphonec_out("hook=paused sip:%s\n",linphonec_get_callee()); break; case LinphoneCallIdle: linphonec_out("hook=on-hook\n"); break; case LinphoneCallStreamsRunning: case LinphoneCallConnected: if (linphone_call_get_dir(call)==LinphoneCallOutgoing){ linphonec_out("Call out, hook=%s duration=%i, muted=%s rtp-xmit-muted=%s\n", linphonec_get_callee(), linphone_core_get_current_call_duration(lc), linphone_core_mic_enabled(lc) ? "no" : "yes", linphone_core_is_rtp_muted(lc) ? "yes" : "no"); }else{ linphonec_out("hook=answered duration=%i %s\n" , linphone_core_get_current_call_duration(lc), linphonec_get_caller()); } break; case LinphoneCallIncomingReceived: linphonec_out("Incoming call from %s\n",linphonec_get_caller()); break; default: break; } } else return 0; return 1; } static int lpc_cmd_ports(LinphoneCore *lc, char *args) { int port; if ( ! args ){ linphonec_out("sip port = %i\naudio rtp port = %i\nvideo rtp port = %i\n", linphone_core_get_sip_port(lc), linphone_core_get_audio_port(lc), linphone_core_get_video_port(lc)); return 1; } if (sscanf(args,"sip %i",&port)==1){ linphonec_out("Setting sip port to %i\n",port); linphone_core_set_sip_port(lc,port); }else return 0; return 1; } static int lpc_cmd_param(LinphoneCore *lc, char *args) { char section[20], param[20], value[50]; const char *string; if (args == NULL) { return 0; } switch (sscanf(args,"%19s %19s %49s",section,param,value)) { // case 1 might show all current settings under a section case 2: string = lp_config_get_string(linphone_core_get_config(lc), section, param, "(undef)"); linphonec_out("current value: %s\n", string); break; case 3: if (lp_config_get_string(linphone_core_get_config(lc), section, param, NULL) != NULL) { lp_config_set_string(linphone_core_get_config(lc), section, param, value); // no indication of existence linphonec_out("updated value: %s\n", value); } else { linphonec_out("only update of existing variables are allowed\n"); } break; default: return 0; } return 1; } static int lpc_cmd_speak(LinphoneCore *lc, char *args){ #ifndef _WIN32 char voice[64]; char *sentence; char cl[256]; char wavfile[128]="/tmp/linphonec-espeak-XXXXXX"; int status; FILE *file; if (!args) return 0; memset(voice,0,sizeof(voice)); sscanf(args,"%63s",voice); sentence=args+strlen(voice); #ifdef __APPLE__ mktemp(wavfile); #else if (mkstemp(wavfile)==-1){ ms_error("Could not create temporary filename: %s", strerror(errno)); linphonec_out("An error occured, please consult logs for details."); return 1; } #endif snprintf(cl,sizeof(cl),"espeak -v %s -s 100 -w %s --stdin",voice,wavfile); file=popen(cl,"w"); if (file==NULL){ ms_error("Could not open pipe to espeak !"); return 1; } fprintf(file,"%s",sentence); status=pclose(file); if (WEXITSTATUS(status)==0){ linphone_core_set_play_file(lc,wavfile); }else{ linphonec_out("espeak command failed."); } #else linphonec_out("Sorry, this command is not implemented in windows version."); #endif return 1; } static int lpc_cmd_acodec(LinphoneCore *lc, char *args){ return lpc_cmd_codec(AUDIO, lc, args); } static int lpc_cmd_vcodec(LinphoneCore *lc, char *args){ return lpc_cmd_codec(VIDEO, lc, args); } static int lpc_cmd_codec(int type, LinphoneCore *lc, char *args){ char *arg1 = args; char *arg2 = NULL; char *ptr = args; if (!args) return 0; /* Isolate first and second arg */ while(*ptr && !isspace(*ptr)) ++ptr; if ( *ptr ) { *ptr='\0'; arg2=ptr+1; while(*arg2 && isspace(*arg2)) ++arg2; } if (strcmp(arg1,"enable")==0) { #ifdef HAVE_READLINE rl_inhibit_completion=1; #endif if (!strcmp(arg2,"all")) linphonec_codec_enable(type,lc,-1); else linphonec_codec_enable(type,lc,atoi(arg2)); #ifdef HAVE_READLINE rl_inhibit_completion=0; #endif } else if (strcmp(arg1,"list")==0) { linphonec_codec_list(type,lc); } else if (strcmp(arg1,"disable")==0) { if (!strcmp(arg2,"all")) linphonec_codec_disable(type,lc,-1); else linphonec_codec_disable(type,lc,atoi(arg2)); } else { return 0; /* syntax error */ } return 1; } static void linphonec_codec_list(int type, LinphoneCore *lc){ PayloadType *pt; int index=0; const bctbx_list_t *node=NULL; if (type == AUDIO) { node=linphone_core_get_audio_codecs(lc); } else if(type==VIDEO) { node=linphone_core_get_video_codecs(lc); } for(;node!=NULL;node=bctbx_list_next(node)){ pt=(PayloadType*)(node->data); linphonec_out("%2d: %s (%d) %s\n", index, pt->mime_type, pt->clock_rate, linphone_core_payload_type_enabled(lc,pt) ? "enabled" : "disabled"); index++; } } static void linphonec_codec_enable(int type, LinphoneCore *lc, int sel_index){ PayloadType *pt; int index=0; const bctbx_list_t *node=NULL; if (type == AUDIO) { node=linphone_core_get_audio_codecs(lc); } else if(type==VIDEO) { node=linphone_core_get_video_codecs(lc); } for(;node!=NULL;node=bctbx_list_next(node)){ if (index == sel_index || sel_index == -1) { pt=(PayloadType*)(node->data); linphone_core_enable_payload_type (lc,pt,TRUE); linphonec_out("%2d: %s (%d) %s\n", index, pt->mime_type, pt->clock_rate, "enabled"); } index++; } } static void linphonec_codec_disable(int type, LinphoneCore *lc, int sel_index){ PayloadType *pt; int index=0; const bctbx_list_t *node=NULL; if (type == AUDIO) { node=linphone_core_get_audio_codecs(lc); } else if(type==VIDEO) { node=linphone_core_get_video_codecs(lc); } for(;node!=NULL;node=bctbx_list_next(node)){ if (index == sel_index || sel_index == -1) { pt=(PayloadType*)(node->data); linphone_core_enable_payload_type (lc,pt,FALSE); linphonec_out("%2d: %s (%d) %s\n", index, pt->mime_type, pt->clock_rate, "disabled"); } index++; } } static int lpc_cmd_echocancellation(LinphoneCore *lc, char *args){ char *arg1 = args; char *arg2 = NULL; char *ptr = args; LpConfig *config=linphone_core_get_config(lc); if (!args) return 0; /* Isolate first and second arg */ while(*ptr && !isspace(*ptr)) ++ptr; if ( *ptr ) { *ptr='\0'; arg2=ptr+1; while(*arg2 && isspace(*arg2)) ++arg2; } if (strcmp(arg1,"on")==0){ int delay, tail_len, frame_size; int n; linphone_core_enable_echo_cancellation(lc,1); if (arg2 != 0) { n = sscanf(arg2, "%d %d %d", &delay, &tail_len, &frame_size); if (n == 1) { lp_config_set_int(config,"sound","ec_delay",delay); } else if (n == 2) { lp_config_set_int(config,"sound","ec_delay",delay); lp_config_set_int(config,"sound","ec_tail_len",tail_len); } else if (n == 3) { lp_config_set_int(config,"sound","ec_delay",delay); lp_config_set_int(config,"sound","ec_tail_len",tail_len); lp_config_set_int(config,"sound","ec_framesize",frame_size); } } } else if (strcmp(arg1,"off")==0){ linphone_core_enable_echo_cancellation(lc,0); } else if (strcmp(arg1,"show")==0){ linphonec_out("echo cancellation is %s; delay %d, tail length %d, frame size %d\n", linphone_core_echo_cancellation_enabled(lc) ? "on" : "off", lp_config_get_int(config,"sound","ec_delay",0), lp_config_get_int(config,"sound","ec_tail_len",0), lp_config_get_int(config,"sound","ec_framesize",0)); } else { return 0; } return 1; } static int lpc_cmd_echolimiter(LinphoneCore *lc, char *args){ if (args){ if (strcmp(args,"on")==0){ linphone_core_enable_echo_limiter (lc,TRUE); }else if (strcmp(args,"off")==0){ linphone_core_enable_echo_limiter (lc,FALSE); } } linphonec_out("Echo limiter is now %s.\n",linphone_core_echo_limiter_enabled (lc) ? "on":"off"); return 1; } static int lpc_cmd_mute_mic(LinphoneCore *lc, char *args) { linphone_core_enable_mic(lc, 0); return 1; } static int lpc_cmd_unmute_mic(LinphoneCore *lc, char *args){ linphone_core_enable_mic(lc, 1); return 1; } static int lpc_cmd_playback_gain(LinphoneCore *lc, char *args) { if (args){ linphone_core_set_playback_gain_db(lc, (float)atof(args)); return 1; } return 0; } static int lpc_cmd_rtp_no_xmit_on_audio_mute(LinphoneCore *lc, char *args) { bool_t rtp_xmit_off=FALSE; char *status; if(args){ if(strstr(args,"1"))rtp_xmit_off=TRUE; if(linphone_core_get_current_call (lc)==NULL) linphone_core_set_rtp_no_xmit_on_audio_mute(lc,rtp_xmit_off); else linphonec_out("nortp-on-audio-mute: call in progress - cannot change state\n"); } rtp_xmit_off=linphone_core_get_rtp_no_xmit_on_audio_mute(lc); if (rtp_xmit_off) status="off"; else status="on"; linphonec_out("rtp transmit %s when audio muted\n",status); return 1; } #ifdef VIDEO_ENABLED static int _lpc_cmd_video_window(LinphoneCore *lc, char *args, bool_t is_preview){ char subcommand[64]; int a,b; int err; VideoParams *params=is_preview ? &lpc_preview_params : &lpc_video_params; if (!args) return 0; err=sscanf(args,"%63s %d %d",subcommand,&a,&b); if (err>=1){ if (strcmp(subcommand,"pos")==0){ if (err<3) return 0; params->x=a; params->y=b; params->refresh=TRUE; }else if (strcmp(subcommand,"size")==0){ if (err<3) return 0; params->w=a; params->h=b; params->refresh=TRUE; }else if (strcmp(subcommand,"show")==0){ params->show=TRUE; params->refresh=TRUE; if (is_preview) linphone_core_enable_video_preview (lc,TRUE); }else if (strcmp(subcommand,"hide")==0){ params->show=FALSE; params->refresh=TRUE; if (is_preview) linphone_core_enable_video_preview (lc,FALSE); }else if (strcmp(subcommand,"id")==0){ if (err == 1){ linphonec_out("vwindow id: 0x%p\n",is_preview ? linphone_core_get_native_preview_window_id (lc) : linphone_core_get_native_video_window_id (lc)); return 1; } else if (err != 2) return 0; params->wid=(void *)(long)a; if (is_preview) linphone_core_set_native_preview_window_id(lc, (void *)(long)a); else linphone_core_set_native_video_window_id(lc, (void *)(long)a); }else if (is_preview==TRUE){ if (strcmp(subcommand,"integrated")==0){ linphone_core_use_preview_window (lc,FALSE); }else if (strcmp(subcommand,"standalone")==0){ linphone_core_use_preview_window(lc,TRUE); }else return 0; }else return 0; } return 1; } static int lpc_cmd_video_window(LinphoneCore *lc, char *args){ return _lpc_cmd_video_window(lc, args, FALSE); } static int lpc_cmd_preview_window(LinphoneCore *lc, char *args){ return _lpc_cmd_video_window(lc, args, TRUE); } #endif static void lpc_display_global_state(LinphoneCore *lc){ linphonec_out("Global liblinphone state\n%s\n", linphone_global_state_to_string(linphone_core_get_global_state(lc))); } static void lpc_display_call_states(LinphoneCore *lc){ LinphoneCall *call; const bctbx_list_t *elem; char *tmp; linphonec_out("Call states\n" "Id | Destination | State | Flags |\n" "------------------------------------------------------------------------\n"); elem=linphone_core_get_calls(lc); if (elem==NULL){ linphonec_out("(empty)\n"); }else{ for(;elem!=NULL;elem=elem->next){ const char *flag; bool_t in_conference; call=(LinphoneCall*)elem->data; in_conference=(linphone_call_get_conference(call) != NULL); tmp=linphone_call_get_remote_address_as_string (call); flag=in_conference ? "conferencing" : ""; flag=linphone_call_has_transfer_pending(call) ? "transfer pending" : flag; linphonec_out("%-2i | %-35s | %-15s | %s\n",VOIDPTR_TO_INT(linphone_call_get_user_data(call)), tmp,linphone_call_state_to_string(linphone_call_get_state(call))+strlen("LinphoneCall"),flag); ms_free(tmp); } } } static void lpc_display_proxy_states(LinphoneCore *lc){ const bctbx_list_t *elem; linphonec_out("Proxy registration states\n" " Identity | State\n" "------------------------------------------------------------\n"); elem=linphone_core_get_proxy_config_list (lc); if (elem==NULL) linphonec_out("(empty)\n"); else { for(;elem!=NULL;elem=elem->next){ LinphoneProxyConfig *cfg=(LinphoneProxyConfig*)elem->data; linphonec_out("%-40s | %s\n",linphone_proxy_config_get_identity (cfg), linphone_registration_state_to_string(linphone_proxy_config_get_state(cfg))); } } } static int lpc_cmd_states(LinphoneCore *lc, char *args){ if (args==NULL) { lpc_display_global_state(lc); lpc_display_call_states(lc); lpc_display_proxy_states(lc); return 1; } if (strcmp(args,"global")==0){ lpc_display_global_state(lc); return 1; } if (strcmp(args,"proxies")==0){ lpc_display_proxy_states(lc); return 1; } if (strcmp(args,"calls")==0){ lpc_display_call_states(lc); return 1; } return 0; } #ifdef VIDEO_ENABLED static int lpc_cmd_camera(LinphoneCore *lc, char *args){ LinphoneCall *call=linphone_core_get_current_call(lc); bool_t activated=FALSE; if (linphone_core_video_enabled (lc)==FALSE){ linphonec_out("Video is disabled, re-run linphonec with -V option."); return 1; } if (args){ if (strcmp(args,"on")==0) activated=TRUE; else if (strcmp(args,"off")==0) activated=FALSE; else return 0; } if (call==NULL){ if (args){ linphonec_camera_enabled=activated; } if (linphonec_camera_enabled){ linphonec_out("Camera is enabled. Video stream will be setup immediately for outgoing and incoming calls.\n"); }else{ linphonec_out("Camera is disabled. Calls will be established with audio-only, with the possibility to later add video using 'camera on'.\n"); } }else{ const LinphoneCallParams *cp=linphone_call_get_current_params (call); if (args){ linphone_call_enable_camera(call,activated); if (linphone_call_get_state(call)==LinphoneCallStreamsRunning){ if ((activated && !linphone_call_params_video_enabled (cp))){ /*update the call to add the video stream*/ LinphoneCallParams *ncp=linphone_call_params_copy(cp); linphone_call_params_enable_video(ncp,TRUE); linphone_call_update(call,ncp); linphone_call_params_unref (ncp); linphonec_out("Trying to bring up video stream...\n"); } } } if (linphone_call_camera_enabled (call)) linphonec_out("Camera is allowed for current call.\n"); else linphonec_out("Camera is dis-allowed for current call.\n"); } return 1; } static int lpc_cmd_snapshot(LinphoneCore *lc, char *args){ LinphoneCall *call; if (!args) return 0; call=linphone_core_get_current_call(lc); if (call!=NULL){ linphone_call_take_video_snapshot(call,args); linphonec_out("Taking video snapshot in file %s\n", args); }else linphonec_out("There is no active call.\n"); return 1; } static int lpc_cmd_preview_snapshot(LinphoneCore *lc, char *args){ LinphoneCall *call; if (!args) return 0; call=linphone_core_get_current_call(lc); if (call!=NULL){ linphone_call_take_preview_snapshot(call,args); linphonec_out("Taking video preview snapshot in file %s\n", args); }else linphonec_out("There is no active call.\n"); return 1; } static int lpc_cmd_vfureq(LinphoneCore *lc, char *arg){ LinphoneCall *call; call=linphone_core_get_current_call(lc); if (call!=NULL){ linphone_call_send_vfu_request(call); linphonec_out("VFU request sent\n"); }else linphonec_out("There is no active call.\n"); return 1; } #endif static int lpc_cmd_identify(LinphoneCore *lc, char *args){ LinphoneCall *call; const char *remote_ua; if (args==NULL){ call=linphone_core_get_current_call(lc); if (call==NULL) { linphonec_out("There is currently running call. Specify call id.\n"); return 0; } }else{ call=linphonec_get_call(atoi(args)); if (call==NULL){ return 0; } } remote_ua=linphone_call_get_remote_user_agent(call); if (remote_ua){ linphonec_out("Remote user agent string is: %s\n",remote_ua); } return 1; } static int lpc_cmd_ringback(LinphoneCore *lc, char *args){ if (!args) return 0; if (strcmp(args,"disable")==0){ linphone_core_set_remote_ringback_tone(lc,NULL); linphonec_out("Disabling ringback tone.\n"); return 1; } linphone_core_set_remote_ringback_tone (lc,args); linphonec_out("Using %s as ringback tone to be played to callers.",args); return 1; } static int zrtp_set_verified(LinphoneCore *lc, char *args, bool_t verified){ LinphoneCall *call=linphone_core_get_current_call(lc); if (linphone_call_params_get_media_encryption(linphone_call_get_current_params(call))==LinphoneMediaEncryptionZRTP){ linphone_call_set_authentication_token_verified(call,verified); } return 1; } static int lpc_cmd_zrtp_verified(LinphoneCore *lc, char *args){ return zrtp_set_verified(lc,args,TRUE); } static int lpc_cmd_zrtp_unverified(LinphoneCore *lc, char *args){ return zrtp_set_verified(lc,args,FALSE); } /*************************************************************************** * * Command table management funx * ***************************************************************************/ /* * Find a command given its name */ static LPC_COMMAND * lpc_find_command(const char *name) { int i; for (i=0; commands[i].name; ++i) { if (strcmp(name, commands[i].name) == 0) return &commands[i]; } for (i=0; advanced_commands[i].name; ++i) { if (strcmp(name, advanced_commands[i].name) == 0) return &advanced_commands[i]; } return (LPC_COMMAND *)NULL; } /**************************************************************************** * * $Log: commands.c,v $ * Revision 1.39 2008/07/03 15:08:34 smorlat * api cleanups, interface in progress. * * Revision 1.38 2008/06/17 20:38:59 smorlat * added missing file. * * Revision 1.37 2008/04/09 09:26:00 smorlat * merge various patches * H264 support. * * Revision 1.36 2007/08/01 14:47:53 strk * * console/commands.c: Clean up commands 'nat', 'stun' * and 'firewall' to be more intuitive. * * Revision 1.35 2007/06/27 09:01:25 smorlat * logging improvements. * * Revision 1.34 2007/02/20 10:17:13 smorlat * linphonec friends patch2 * * Revision 1.31 2006/09/22 07:22:47 smorlat * linphonecore api changes. * * Revision 1.30 2006/09/08 15:32:57 smorlat * support for using files instead of soundcard (used by linphonec only) * * Revision 1.29 2006/08/28 14:29:07 smorlat * fix bug. * * Revision 1.28 2006/08/21 12:49:59 smorlat * merged several little patches. * * Revision 1.27 2006/07/17 18:45:00 smorlat * support for several event queues in ortp. * glib dependency removed from coreapi/ and console/ * * Revision 1.26 2006/04/14 15:16:36 smorlat * soundcard use did nothing ! * * Revision 1.25 2006/04/06 20:09:33 smorlat * add linphonec command to see and select sound devices. * * Revision 1.24 2006/03/04 11:17:10 smorlat * mediastreamer2 in progress. * * Revision 1.23 2006/02/20 21:14:01 strk * Handled syntax errors with 'friend' command * * Revision 1.22 2006/02/20 10:20:29 strk * Added substring-based filter support for command 'friend list' * * Revision 1.21 2006/02/02 15:39:18 strk * - Added 'friend list' and 'friend call' commands * - Allowed for multiple DTFM send in a single line * - Added status-specific callback (bare version) * * Revision 1.20 2006/01/26 11:54:34 strk * More robust 'nat' command handler (strip blanks in args) * * Revision 1.19 2006/01/26 09:48:05 strk * Added limits.h include * * Revision 1.18 2006/01/26 02:18:05 strk * Added new commands 'nat use' and 'nat unuse'. * These will required a pending patch to linphonecore.c * in order to work. * * Revision 1.17 2006/01/20 14:12:33 strk * Added linphonec_init() and linphonec_finish() functions. * Handled SIGINT and SIGTERM to invoke linphonec_finish(). * Handling of auto-termination (-t) moved to linphonec_finish(). * Reworked main (input read) loop to not rely on 'terminate' * and 'run' variable (dropped). configfile_name allocated on stack * using PATH_MAX limit. Changed print_usage signature to allow * for an exit_status specification. * * Revision 1.16 2006/01/18 09:25:32 strk * Command completion inhibited in proxy addition and auth request prompts. * Avoided use of linphonec_readline's internal filename completion. * * Revision 1.15 2006/01/14 13:29:32 strk * Reworked commands interface to use a table structure, * used by command line parser and help function. * Implemented first level of completion (commands). * Added notification of invalid "answer" and "terminate" * commands (no incoming call, no active call). * Forbidden "call" intialization when a call is already active. * Cleaned up all commands, adding more feedback and error checks. * * Revision 1.14 2006/01/13 13:00:29 strk * Added linphonec.h. Code layout change (added comments, forward decl, * globals on top, copyright notices and Logs). Handled out-of-memory * condition on history management. Removed assumption on sizeof(char). * Fixed bug in authentication prompt (introduced by linphonec_readline). * Added support for multiple authentication requests (up to MAX_PENDING_AUTH). * * ****************************************************************************/ liblinphone-4.4.21/console/example/000077500000000000000000000000001376572216100172145ustar00rootroot00000000000000liblinphone-4.4.21/console/example/linphonec000066400000000000000000000013371376572216100211220ustar00rootroot00000000000000# this file is used to store user config values for linphonec # please modify and copy it to ~/.linphonec # # logfile=~/linphonec.log debuglevel=0 local_addr=192.168.1.10 if_name=eth0 # type of network connection 4 = ethernet con_type=4 # rtp audio_rtp_port=7078 jitt_comp=150 # audio driver_mode=0 rec_lev=100 play_lev=100 source=109 autokill=0 # sip sip_port=5060 use_registrar=0 username=1006 hostname=192.168.1.1 registrar=sip:192.168.1.1:5060 reg_passwd= addr_of_rec=sip:1006@192.168.1.1 reg_expires=900 as_proxy=1 as_redirect=0 as_outbound=1 # codecs audio_codecs=259 371 264 256 # short dial s0=sip:1002@130.83.176.121 s1=sip:1001@192.168.1.1 s2=sip:1002@192.168.1.1 s3=sip:1003@192.168.1.1 s4=sip:1004@192.168.1.1 liblinphone-4.4.21/console/linphonec.c000066400000000000000000001176321376572216100177160ustar00rootroot00000000000000/**************************************************************************** * * $Id: linphonec.c,v 1.57 2007/11/14 13:40:27 smorlat Exp $ * * Copyright (C) 2006 Sandro Santilli * Copyright (C) 2002 Florian Winterstein * Copyright (C) 2000 Simon MORLAT * **************************************************************************** * * 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 #ifndef _WIN32_WCE #include #include #include #endif /*_WIN32_WCE*/ #include #include #include #include #include "linphonec.h" #include #ifdef _WIN32 #include #include #ifndef _WIN32_WCE #include #endif /*_WIN32_WCE*/ #else #include #include #include #include #include #include #endif #if !defined(PATH_MAX) #define PATH_MAX 256 #endif /*PATH_MAX*/ #if defined(_WIN32_WCE) #if !defined(strdup) #define strdup _strdup #endif /*strdup*/ #endif /*_WIN32_WCE*/ #ifndef PACKAGE_DIR #define PACKAGE_DIR "" #endif #ifdef HAVE_X11_XLIB_H #include #endif /*************************************************************************** * * Types * ***************************************************************************/ typedef struct { LinphoneAuthInfo *elem[MAX_PENDING_AUTH]; int nitems; } LPC_AUTH_STACK; /*************************************************************************** * * Forward declarations * ***************************************************************************/ char *lpc_strip_blanks(char *input); static int handle_configfile_migration(void); #if !defined(_WIN32_WCE) static int copy_file(const char *from, const char *to); #endif /*_WIN32_WCE*/ static int linphonec_parse_cmdline(int argc, char **argv); static int linphonec_init(int argc, char **argv); static int linphonec_main_loop (LinphoneCore * opm); static int linphonec_idle_call (void); #ifdef HAVE_READLINE static int linphonec_initialize_readline(void); static int linphonec_finish_readline(void); static char **linephonec_readline_completion(const char *text, int start, int end); #endif /* These are callback for linphone core */ static void linphonec_prompt_for_auth(LinphoneCore *lc, const char *realm, const char *username, const char *domain); static void linphonec_display_refer (LinphoneCore * lc, const char *refer_to); static void linphonec_transfer_state_changed(LinphoneCore *lc, LinphoneCall *call, LinphoneCallState new_call_state); static void linphonec_notify_presence_received(LinphoneCore *lc,LinphoneFriend *fid); static void linphonec_new_unknown_subscriber(LinphoneCore *lc, LinphoneFriend *lf, const char *url); static void linphonec_text_received(LinphoneCore *lc, LinphoneChatRoom *cr, const LinphoneAddress *from, const char *msg); static void linphonec_dtmf_received(LinphoneCore *lc, LinphoneCall *call, int dtmf); static void print_prompt(LinphoneCore *opm); void linphonec_out(const char *fmt,...); /*************************************************************************** * * Global variables * ***************************************************************************/ LinphoneCore *linphonec; FILE *mylogfile; #ifdef HAVE_READLINE static char *histfile_name=NULL; static char last_in_history[256]; #endif //auto answer (-a) option static bool_t auto_answer=FALSE; static bool_t real_early_media_sending=FALSE; static bool_t answer_call=FALSE; static bool_t vcap_enabled=FALSE; static bool_t display_enabled=FALSE; static bool_t preview_enabled=FALSE; static bool_t show_general_state=FALSE; static bool_t unix_socket=FALSE; static bool_t linphonec_running=TRUE; LPC_AUTH_STACK auth_stack; static int trace_level = 0; static char *logfile_name = NULL; static char configfile_name[PATH_MAX]; static char zrtpsecrets[PATH_MAX]; static char usr_certificates_path[PATH_MAX]; static const char *factory_configfile_name=NULL; static char *sip_addr_to_call = NULL; /* for autocall */ static void *window_id = NULL; /* NULL=standalone window, or window id for embedding video */ #if !defined(_WIN32_WCE) static ortp_pipe_t client_sock=ORTP_PIPE_INVALID; #endif /*_WIN32_WCE*/ char prompt[PROMPT_MAX_LEN]; #if !defined(_WIN32_WCE) static ortp_thread_t pipe_reader_th; static bool_t pipe_reader_run=FALSE; #endif /*_WIN32_WCE*/ #if !defined(_WIN32_WCE) static ortp_pipe_t server_sock; #endif /*_WIN32_WCE*/ bool_t linphonec_camera_enabled=TRUE; void linphonec_call_identify(LinphoneCall* call){ static int callid=1; linphone_call_set_user_data (call,INT_TO_VOIDPTR(callid)); callid++; } LinphoneCall *linphonec_get_call(int id){ const MSList *elem=linphone_core_get_calls(linphonec); for (;elem!=NULL;elem=elem->next){ LinphoneCall *call=(LinphoneCall*)elem->data; if (VOIDPTR_TO_INT(linphone_call_get_user_data(call))==id){ return call; } } linphonec_out("Sorry, no call with id %i exists at this time.\n",id); return NULL; } /*************************************************************************** * * Linphone core callbacks * ***************************************************************************/ /* * Linphone core callback */ static void linphonec_display_refer (LinphoneCore * lc, const char *refer_to) { linphonec_out("Receiving out of call refer to %s\n", refer_to); } /* * Linphone core callback */ static void linphonec_prompt_for_auth(LinphoneCore *lc, const char *realm, const char *username, const char *domain) { /* no prompt possible when using pipes or tcp mode*/ if (unix_socket){ linphone_core_abort_authentication(lc,NULL); }else{ LinphoneAuthInfo *pending_auth; if ( auth_stack.nitems+1 > MAX_PENDING_AUTH ) { fprintf(stderr, "Can't accept another authentication request.\n" "Consider incrementing MAX_PENDING_AUTH macro.\n"); return; } pending_auth=linphone_auth_info_new(username,NULL,NULL,NULL,realm,domain); auth_stack.elem[auth_stack.nitems++]=pending_auth; } } /* * Linphone core callback */ static void linphonec_transfer_state_changed(LinphoneCore *lc, LinphoneCall *call, LinphoneCallState new_call_state) { char *remote=linphone_call_get_remote_address_as_string(call); if (new_call_state==LinphoneCallConnected){ linphonec_out("The distant endpoint %s of call %i has been transfered, you can safely close the call.\n", remote,VOIDPTR_TO_INT(linphone_call_get_user_data(call))); } ms_free(remote); } /* * Linphone core callback */ static void linphonec_notify_presence_received(LinphoneCore *lc,LinphoneFriend *fid) { const LinphoneAddress *addr = linphone_friend_get_address(fid); if (addr) { char *tmp=linphone_address_as_string(addr); printf("Friend %s is %s\n", tmp, linphone_online_status_to_string(linphone_friend_get_status(fid))); ms_free(tmp); } // todo: update Friend list state (unimplemented) } /* * Linphone core callback */ static void linphonec_new_unknown_subscriber(LinphoneCore *lc, LinphoneFriend *lf, const char *url) { printf("Friend %s requested subscription " "(accept/deny is not implemented yet)\n", url); // This means that this person wishes to be notified // of your presence information (online, busy, away...). } static void linphonec_call_updated(LinphoneCall *call){ const LinphoneCallParams *cp=linphone_call_get_current_params(call); if (!linphone_call_camera_enabled (call) && linphone_call_params_video_enabled (cp)){ linphonec_out("Far end requests to share video.\nType 'camera on' if you agree.\n"); } } static void linphonec_call_encryption_changed(LinphoneCore *lc, LinphoneCall *call, bool_t encrypted, const char *auth_token) { int id=VOIDPTR_TO_INT(linphone_call_get_user_data(call)); if (!encrypted) { linphonec_out("Call %i is not fully encrypted and auth token is %s.\n", id, (auth_token != NULL) ? auth_token : "absent"); } else { linphonec_out("Call %i is fully encrypted and auth token is %s.\n", id, (auth_token != NULL) ? auth_token : "absent"); } } static void linphonec_call_state_changed(LinphoneCore *lc, LinphoneCall *call, LinphoneCallState st, const char *msg){ char *from=linphone_call_get_remote_address_as_string(call); int id=VOIDPTR_TO_INT(linphone_call_get_user_data(call)); switch(st){ case LinphoneCallEnd: linphonec_out("Call %i with %s ended (%s).\n", id, from, linphone_reason_to_string(linphone_call_get_reason(call))); break; case LinphoneCallResuming: linphonec_out("Resuming call %i with %s.\n", id, from); break; case LinphoneCallStreamsRunning: linphonec_out("Media streams established with %s for call %i (%s).\n", from,id,( linphone_call_params_video_enabled( linphone_call_get_current_params(call)) ? "video":"audio")); break; case LinphoneCallPausing: linphonec_out("Pausing call %i with %s.\n", id, from); break; case LinphoneCallPaused: linphonec_out("Call %i with %s is now paused.\n", id, from); break; case LinphoneCallPausedByRemote: linphonec_out("Call %i has been paused by %s.\n",id,from); break; case LinphoneCallIncomingReceived: linphonec_call_identify(call); linphone_call_enable_camera (call,linphonec_camera_enabled); id=VOIDPTR_TO_INT(linphone_call_get_user_data(call)); linphonec_set_caller(from); linphonec_out("Receiving new incoming call from %s, assigned id %i\n", from,id); if ( auto_answer) { answer_call=TRUE; } else if (real_early_media_sending) { LinphoneCallParams* callparams = linphone_core_create_call_params(lc, call); linphonec_out("Sending early media using real hardware\n"); linphone_call_params_enable_early_media_sending(callparams, TRUE); if (vcap_enabled) linphone_call_params_enable_video(callparams, TRUE); linphone_call_accept_early_media_with_params(call, callparams); linphone_call_params_unref(callparams); } break; case LinphoneCallOutgoingInit: linphonec_call_identify(call); id=VOIDPTR_TO_INT(linphone_call_get_user_data(call)); linphonec_out("Establishing call id to %s, assigned id %i\n", from,id); break; case LinphoneCallUpdatedByRemote: linphonec_call_updated(call); break; case LinphoneCallOutgoingProgress: linphonec_out("Call %i to %s in progress.\n", id, from); break; case LinphoneCallOutgoingRinging: linphonec_out("Call %i to %s ringing.\n", id, from); break; case LinphoneCallConnected: linphonec_out("Call %i with %s connected.\n", id, from); break; case LinphoneCallOutgoingEarlyMedia: linphonec_out("Call %i with %s early media.\n", id, from); break; case LinphoneCallError: linphonec_out("Call %i with %s error.\n", id, from); break; default: break; } ms_free(from); } /* * Linphone core callback */ static void linphonec_text_received(LinphoneCore *lc, LinphoneChatRoom *cr, const LinphoneAddress *from, const char *msg) { linphonec_out("Message received from %s: %s\n", linphone_address_as_string(from), msg); // TODO: provide mechanism for answering.. ('say' command?) } static void linphonec_dtmf_received(LinphoneCore *lc, LinphoneCall *call, int dtmf){ char *from=linphone_call_get_remote_address_as_string(call); fprintf(stdout,"Receiving tone %c from %s\n",dtmf,from); fflush(stdout); ms_free(from); } static char received_prompt[PROMPT_MAX_LEN]; static ms_mutex_t prompt_mutex; static bool_t have_prompt=FALSE; static void *prompt_reader_thread(void *arg){ char *ret; char tmp[PROMPT_MAX_LEN]; while ((ret=fgets(tmp,sizeof(tmp),stdin))!=NULL){ ms_mutex_lock(&prompt_mutex); strcpy(received_prompt,ret); have_prompt=TRUE; ms_mutex_unlock(&prompt_mutex); } return NULL; } static void start_prompt_reader(void){ ortp_thread_t th; ms_mutex_init(&prompt_mutex,NULL); ortp_thread_create(&th,NULL,prompt_reader_thread,NULL); } #if !defined(_WIN32_WCE) static ortp_pipe_t create_server_socket(void){ char path[128]; #ifndef _WIN32 snprintf(path,sizeof(path)-1,"linphonec-%i",getuid()); #else { TCHAR username[128]; DWORD size=sizeof(username)-1; GetUserName(username,&size); snprintf(path,sizeof(path)-1,"linphonec-%s",username); } #endif return ortp_server_pipe_create(path); } static void *pipe_thread(void*p){ char tmp[250]; server_sock=create_server_socket(); if (server_sock==ORTP_PIPE_INVALID) return NULL; while(pipe_reader_run){ while(client_sock!=ORTP_PIPE_INVALID){ /*sleep until the last command is finished*/ #ifndef _WIN32 usleep(20000); #else Sleep(20); #endif } client_sock=ortp_server_pipe_accept_client(server_sock); if (client_sock!=ORTP_PIPE_INVALID){ int len; /*now read from the client */ if ((len=ortp_pipe_read(client_sock,(uint8_t*)tmp,sizeof(tmp)-1))>0){ ortp_mutex_lock(&prompt_mutex); tmp[len]='\0'; strcpy(received_prompt,tmp); printf("Receiving command '%s'\n",received_prompt);fflush(stdout); have_prompt=TRUE; ortp_mutex_unlock(&prompt_mutex); }else{ printf("read nothing\n");fflush(stdout); ortp_server_pipe_close_client(client_sock); client_sock=ORTP_PIPE_INVALID; } }else{ if (pipe_reader_run) fprintf(stderr,"accept() failed: %s\n",strerror(errno)); } } ms_message("Exiting pipe_reader_thread."); fflush(stdout); return NULL; } static void start_pipe_reader(void){ pipe_reader_run=TRUE; ortp_thread_create(&pipe_reader_th,NULL,pipe_thread,NULL); } static void stop_pipe_reader(void){ pipe_reader_run=FALSE; linphonec_command_finished(); ortp_server_pipe_close(server_sock); ortp_thread_join(pipe_reader_th,NULL); } #endif /*_WIN32_WCE*/ #ifdef HAVE_READLINE #define BOOL_HAVE_READLINE 1 #else #define BOOL_HAVE_READLINE 0 #endif char *linphonec_readline(char *prompt){ if (unix_socket || !BOOL_HAVE_READLINE ){ static bool_t prompt_reader_started=FALSE; static bool_t pipe_reader_started=FALSE; if (!prompt_reader_started){ start_prompt_reader(); prompt_reader_started=TRUE; } if (unix_socket && !pipe_reader_started){ #if !defined(_WIN32_WCE) start_pipe_reader(); pipe_reader_started=TRUE; #endif /*_WIN32_WCE*/ } fprintf(stdout,"%s",prompt); fflush(stdout); while(1){ ms_mutex_lock(&prompt_mutex); if (have_prompt){ char *ret=strdup(received_prompt); have_prompt=FALSE; ms_mutex_unlock(&prompt_mutex); return ret; } ms_mutex_unlock(&prompt_mutex); linphonec_idle_call(); #ifdef _WIN32 { MSG msg; Sleep(20); /* Following is to get the video window going as it should. Maybe should we only have this on when the option -V or -D is on? */ if (PeekMessage(&msg, NULL, 0, 0,1)) { TranslateMessage(&msg); DispatchMessage(&msg); } } #else usleep(20000); #endif } }else{ #ifdef HAVE_READLINE char* ret=readline(prompt); return ret; #endif } } void linphonec_out(const char *fmt,...){ char *res; va_list args; va_start (args, fmt); res=ortp_strdup_vprintf(fmt,args); va_end (args); printf("%s",res); fflush(stdout); #if !defined(_WIN32_WCE) if (client_sock!=ORTP_PIPE_INVALID){ if (ortp_pipe_write(client_sock,(uint8_t*)res,(int)strlen(res))==-1){ fprintf(stderr,"Fail to send output via pipe: %s",strerror(errno)); } } #endif /*_WIN32_WCE*/ ortp_free(res); } void linphonec_command_finished(void){ #if !defined(_WIN32_WCE) if (client_sock!=ORTP_PIPE_INVALID){ ortp_server_pipe_close_client(client_sock); client_sock=ORTP_PIPE_INVALID; } #endif /*_WIN32_WCE*/ } void linphonec_set_autoanswer(bool_t enabled){ auto_answer=enabled; } bool_t linphonec_get_autoanswer(void){ return auto_answer; } LinphoneCoreVTable linphonec_vtable={0}; /***************************************************************************/ /* * Main * * Use globals: * * - char *histfile_name * - FILE *mylogfile */ #if defined (_WIN32_WCE) char **convert_args_to_ascii(int argc, _TCHAR **wargv){ int i; char **result=malloc(argc*sizeof(char*)); char argtmp[128]; for(i=0;i 0) { if (logfile_name != NULL) mylogfile = fopen (logfile_name, "w+"); if (mylogfile == NULL) { mylogfile = stdout; fprintf (stderr, "INFO: no logfile, logging to stdout\n"); } linphone_core_enable_logs(mylogfile); } else { linphone_core_disable_logs(); } /* * Initialize auth stack */ auth_stack.nitems=0; /* * Initialize linphone core */ linphonec=linphone_core_new (&linphonec_vtable, configfile_name, factory_configfile_name, NULL); linphone_core_set_user_agent(linphonec,"Linphonec", LINPHONE_VERSION); linphone_core_set_zrtp_secrets_file(linphonec,zrtpsecrets); linphone_core_set_user_certificates_path(linphonec,usr_certificates_path); linphone_core_enable_video_capture(linphonec, vcap_enabled); linphone_core_enable_video_display(linphonec, display_enabled); if (display_enabled && (window_id != NULL)) { printf("Setting window_id: 0x%p\n", window_id); linphone_core_set_native_video_window_id(linphonec,window_id); } linphone_core_enable_video_preview(linphonec,preview_enabled); if (!(vcap_enabled || display_enabled)) printf("Warning: video is disabled in linphonec, use -V or -C or -D to enable.\n"); #ifdef HAVE_READLINE /* * Initialize readline */ linphonec_initialize_readline(); #endif #if !defined(_WIN32_WCE) /* * Initialize signal handlers */ signal(SIGTERM, linphonec_finish); signal(SIGINT, linphonec_finish); #endif /*_WIN32_WCE*/ return 1; } void linphonec_main_loop_exit(void){ linphonec_running=FALSE; } /* * Close linphonec, cleanly terminating * any pending call */ void linphonec_finish(int exit_status) { // Do not allow concurrent destroying to prevent glibc errors static bool_t terminating=FALSE; if (terminating) return; terminating=TRUE; linphonec_out("Terminating...\n"); /* Terminate any pending call */ linphone_core_terminate_all_calls(linphonec); #ifdef HAVE_READLINE linphonec_finish_readline(); #endif #if !defined(_WIN32_WCE) if (pipe_reader_run) stop_pipe_reader(); #endif /*_WIN32_WCE*/ linphone_core_unref (linphonec); if (mylogfile != NULL && mylogfile != stdout) { fclose (mylogfile); mylogfile=stdout; } printf("\n"); exit(exit_status); } /* * This is called from idle_call() whenever * pending_auth != NULL. * * It prompts user for a password. * Hitting ^D (EOF) would make this function * return 0 (Cancel). * Any other input would try to set linphone core * auth_password for the pending_auth, add the auth_info * and return 1. */ int linphonec_prompt_for_auth_final(LinphoneCore *lc) { static int reentrancy=0; char *input, *iptr; char auth_prompt[256]; #ifdef HAVE_READLINE rl_hook_func_t *old_event_hook; #endif LinphoneAuthInfo *pending_auth; if (reentrancy!=0) return 0; reentrancy++; pending_auth=auth_stack.elem[auth_stack.nitems-1]; snprintf(auth_prompt, 256, "Password for %s on %s: ", linphone_auth_info_get_username(pending_auth), linphone_auth_info_get_realm(pending_auth)); printf("\n"); #ifdef HAVE_READLINE /* * Disable event hook to avoid entering an * infinite loop. This would prevent idle_call * from being called during authentication reads. * Note that it might be undesiderable... */ old_event_hook=rl_event_hook; rl_event_hook=NULL; #endif while (1) { input=linphonec_readline(auth_prompt); /* * If EOF (^D) is sent you probably don't want * to provide an auth password... should give up * the operation, but there's no mechanism to * send this info back to caller currently... */ if ( ! input ) { printf("Cancel requested, but not implemented.\n"); continue; } /* Strip blanks */ iptr=lpc_strip_blanks(input); /* * Only blanks, continue asking */ if ( ! *iptr ) { free(input); continue; } /* Something typed, let's try */ break; } /* * No check is done here to ensure password is correct. * I guess password will be asked again later. */ linphone_auth_info_set_passwd(pending_auth, input); linphone_core_add_auth_info(lc, pending_auth); linphone_auth_info_unref(pending_auth); auth_stack.elem[auth_stack.nitems-1]=0; --(auth_stack.nitems); #ifdef HAVE_READLINE /* * Reset line_buffer, to avoid the password * to be used again from outer readline */ rl_line_buffer[0]='\0'; rl_event_hook=old_event_hook; #endif return 1; } void print_usage (int exit_status) { fprintf (stdout, "\n" "usage: linphonec [-c file] [-s sipaddr] [-a] [-V] [-d level ] [-l logfile]\n" "linphonec -v\n" "\n" " -b file specify path of readonly factory configuration file.\n" " -c file specify path of configuration file.\n" " -d level be verbose. 0 is no output. 6 is all output\n" " -l logfile specify the log file for your SIP phone\n" " -s sipaddress specify the sip call to do at startup\n" " -a enable auto answering for incoming calls\n" " --real-early-media enable sending early media using real audio/video (beware of privacy issue)\n" " -V enable video features globally (disabled by default)\n" " -C enable video capture only (disabled by default)\n" " -D enable video display only (disabled by default)\n" " -S show general state messages (disabled by default)\n" " --wid windowid force embedding of video window into provided windowid (disabled by default)\n" " -v or --version display version and exits.\n" ); exit(exit_status); } #ifdef VIDEO_ENABLED #ifdef HAVE_X11_XLIB_H static void x11_apply_video_params(VideoParams *params, Window window){ XWindowChanges wc; unsigned int flags=0; static Display *display = NULL; const char *dname=getenv("DISPLAY"); if (display==NULL && dname!=NULL){ XInitThreads(); display=XOpenDisplay(dname); } if (display==NULL){ ms_error("Could not open display %s",dname); return; } memset(&wc,0,sizeof(wc)); wc.x=params->x; wc.y=params->y; wc.width=params->w; wc.height=params->h; if (params->x!=-1 ){ flags|=CWX|CWY; } if (params->w!=-1){ flags|=CWWidth|CWHeight; } /*printf("XConfigureWindow x=%i,y=%i,w=%i,h=%i\n", wc.x, wc.y ,wc.width, wc.height);*/ XConfigureWindow(display,window,flags,&wc); if (params->show) XMapWindow(display,window); else XUnmapWindow(display,window); XSync(display,FALSE); } #endif static void lpc_apply_video_params(void){ static void *old_wid=NULL; static void *old_pwid=NULL; void *wid=linphone_core_get_native_video_window_id(linphonec); void *pwid=linphone_core_get_native_preview_window_id(linphonec); if (wid!=NULL && (lpc_video_params.refresh || old_wid!=wid)){ lpc_video_params.refresh=FALSE; #ifdef HAVE_X11_XLIB_H if (lpc_video_params.wid==0){ // do not manage window if embedded x11_apply_video_params(&lpc_video_params,(Window)wid); } else { linphone_core_show_video(linphonec, lpc_video_params.show); } #endif } old_wid=wid; if (pwid!=NULL && (lpc_preview_params.refresh || old_pwid!=pwid)){ lpc_preview_params.refresh=FALSE; #ifdef HAVE_X11_XLIB_H /*printf("wid=%p pwid=%p\n",wid,pwid);*/ if (lpc_preview_params.wid==NULL){ // do not manage window if embedded printf("Refreshing\n"); x11_apply_video_params(&lpc_preview_params,(Window)pwid); } #endif } old_pwid=pwid; } #endif /* * * Called every second from main read loop. * * Will use the following globals: * * - LinphoneCore linphonec * - LPC_AUTH_STACK auth_stack; * */ static int linphonec_idle_call () { LinphoneCore *opm=linphonec; /* Uncomment the following to verify being called */ /* printf(".\n"); */ linphone_core_iterate(opm); if (answer_call){ fprintf (stdout, "-------auto answering to call-------\n" ); linphone_core_accept_call(opm, linphone_core_get_current_call(opm)); answer_call=FALSE; } /* auto call handling */ if (sip_addr_to_call != NULL ) { char buf[512]; snprintf (buf, sizeof(buf),"call %s", sip_addr_to_call); sip_addr_to_call=NULL; linphonec_parse_command_line(linphonec, buf); } if ( auth_stack.nitems ) { /* * Inhibit command completion * during password prompts */ #ifdef HAVE_READLINE rl_inhibit_completion=1; #endif linphonec_prompt_for_auth_final(opm); #ifdef HAVE_READLINE rl_inhibit_completion=0; #endif } #ifdef VIDEO_ENABLED lpc_apply_video_params(); #endif return 0; } #ifdef HAVE_READLINE /* * Use globals: * * - char *histfile_name (also sets this) * - char *last_in_history (allocates it) */ static int linphonec_initialize_readline() { /*rl_bind_key('\t', rl_insert);*/ /* Allow conditional parsing of ~/.inputrc */ rl_readline_name = "linphonec"; /* Call idle_call() every second */ rl_set_keyboard_input_timeout(LPC_READLINE_TIMEOUT); rl_event_hook=linphonec_idle_call; /* Set history file and read it */ histfile_name = ms_strdup_printf ("%s/.linphonec_history", getenv("HOME")); read_history(histfile_name); /* Initialized last_in_history cache*/ last_in_history[0] = '\0'; /* Register a completion function */ rl_attempted_completion_function = linephonec_readline_completion; /* printf("Readline initialized.\n"); */ setlinebuf(stdout); return 0; } /* * Uses globals: * * - char *histfile_name (writes history to file and frees it) * - char *last_in_history (frees it) * */ static int linphonec_finish_readline() { stifle_history(HISTSIZE); write_history(histfile_name); free(histfile_name); histfile_name=NULL; return 0; } #endif static void print_prompt(LinphoneCore *opm){ #ifdef IDENTITY_AS_PROMPT snprintf(prompt, PROMPT_MAX_LEN, "%s> ", linphone_core_get_primary_contact(opm)); #else snprintf(prompt, PROMPT_MAX_LEN, "linphonec> "); #endif } static int linphonec_main_loop (LinphoneCore * opm) { char *input; print_prompt(opm); while (linphonec_running && (input=linphonec_readline(prompt))) { char *iptr; /* input and input pointer */ size_t input_len; /* Strip blanks */ iptr=lpc_strip_blanks(input); input_len = strlen(iptr); /* * Do nothing but release memory * if only blanks are read */ if ( ! input_len ) { free(input); continue; } #ifdef HAVE_READLINE /* * Only add to history if not already * last item in it, and only if the command * doesn't start with a space (to allow for * hiding passwords) */ if ( iptr == input && strcmp(last_in_history, iptr) ) { strncpy(last_in_history,iptr,sizeof(last_in_history)); last_in_history[sizeof(last_in_history)-1]='\0'; add_history(iptr); } #endif linphonec_parse_command_line(linphonec, iptr); linphonec_command_finished(); free(input); } return 0; } /* * Parse command line switches * * Use globals: * * - int trace_level * - char *logfile_name * - char *configfile_name * - char *sipAddr */ static int linphonec_parse_cmdline(int argc, char **argv) { int arg_num=1; while (arg_num < argc) { int old_arg_num = arg_num; if (strncmp ("-d", argv[arg_num], 2) == 0) { arg_num++; if (arg_num < argc) trace_level = atoi (argv[arg_num]); else trace_level = 1; } else if (strncmp ("-l", argv[arg_num], 2) == 0) { arg_num++; if (arg_num < argc) logfile_name = argv[arg_num]; } else if (strncmp ("-c", argv[arg_num], 2) == 0) { if ( ++arg_num >= argc ) print_usage(EXIT_FAILURE); #ifdef _MSC_VER if (strcmp(argv[arg_num], "NUL") != 0) { #endif #if !defined(_WIN32_WCE) if (bctbx_file_exist(argv[arg_num]) != 0) { fprintf(stderr, "Cannot open config file %s.\n", argv[arg_num]); exit(EXIT_FAILURE); } #endif /*_WIN32_WCE*/ #ifdef _MSC_VER } #endif snprintf(configfile_name, PATH_MAX, "%s", argv[arg_num]); } else if (strncmp ("-b", argv[arg_num], 2) == 0) { if ( ++arg_num >= argc ) print_usage(EXIT_FAILURE); #if !defined(_WIN32_WCE) if (bctbx_file_exist(argv[arg_num])!=0 ) { fprintf (stderr, "Cannot open config file %s.\n", argv[arg_num]); exit(EXIT_FAILURE); } #endif /*_WIN32_WCE*/ factory_configfile_name = argv[arg_num]; } else if (strncmp ("-s", argv[arg_num], 2) == 0) { arg_num++; if (arg_num < argc) sip_addr_to_call = argv[arg_num]; } else if (strncmp ("-a", argv[arg_num], 2) == 0) { auto_answer = TRUE; } else if (strncmp ("--real-early-media", argv[arg_num], strlen("--real-early-media")) == 0) { real_early_media_sending = TRUE; } else if (strncmp ("-C", argv[arg_num], 2) == 0) { vcap_enabled = TRUE; } else if (strncmp ("-D", argv[arg_num], 2) == 0) { display_enabled = TRUE; } else if (strncmp ("-V", argv[arg_num], 2) == 0) { display_enabled = TRUE; vcap_enabled = TRUE; preview_enabled=TRUE; } else if ((strncmp ("-v", argv[arg_num], 2) == 0) || (strncmp ("--version", argv[arg_num], strlen ("--version")) == 0)) { #if !defined(_WIN32_WCE) printf ("version: " LINPHONE_VERSION "\n"); #endif exit (EXIT_SUCCESS); } else if (strncmp ("-S", argv[arg_num], 2) == 0) { show_general_state = TRUE; } else if (strncmp ("--pipe", argv[arg_num], 6) == 0) { unix_socket=1; } else if (strncmp ("--wid", argv[arg_num], 5) == 0) { arg_num++; if (arg_num < argc) { char *tmp; window_id = INT_TO_VOIDPTR((int)strtol( argv[arg_num], &tmp, 0 )); lpc_video_params.wid = window_id; } } else if (old_arg_num == arg_num) { fprintf (stderr, "ERROR: bad arguments\n"); print_usage (EXIT_FAILURE); } arg_num++; } return 1; } /* * Up to version 1.2.1 linphone used ~/.linphonec for * CLI and ~/.gnome2/linphone for GUI as configuration file. * In newer version both interfaces will use ~/.linphonerc. * * This function helps transparently migrating from one * to the other layout using the following heuristic: * * IF new_config EXISTS => do nothing * ELSE IF old_cli_config EXISTS => copy to new_config * ELSE IF old_gui_config EXISTS => copy to new_config * * Returns: * 0 if it did nothing * 1 if it migrated successfully * -1 on error */ static int handle_configfile_migration() { #if !defined(_WIN32_WCE) char *old_cfg_gui; char *old_cfg_cli; char *new_cfg; const char *home = getenv("HOME"); new_cfg = ms_strdup_printf("%s/.linphonerc", home); /* * If the *NEW* configuration already exists * do nothing. */ if (bctbx_file_exist(new_cfg)==0) { free(new_cfg); return 0; } old_cfg_cli = ms_strdup_printf("%s/.linphonec", home); /* * If the *OLD* CLI configurations exist copy it to * the new file and make it a symlink. */ if (bctbx_file_exist(old_cfg_cli)==0) { if ( ! copy_file(old_cfg_cli, new_cfg) ) { free(old_cfg_cli); free(new_cfg); return -1; } printf("%s copied to %s\n", old_cfg_cli, new_cfg); free(old_cfg_cli); free(new_cfg); return 1; } free(old_cfg_cli); old_cfg_gui = ms_strdup_printf("%s/.gnome2/linphone", home); /* * If the *OLD* GUI configurations exist copy it to * the new file and make it a symlink. */ if (bctbx_file_exist(old_cfg_gui)==0) { if ( ! copy_file(old_cfg_gui, new_cfg) ) { exit(EXIT_FAILURE); free(old_cfg_gui); free(new_cfg); return -1; } printf("%s copied to %s\n", old_cfg_gui, new_cfg); free(old_cfg_gui); free(new_cfg); return 1; } free(old_cfg_gui); free(new_cfg); #endif /*_WIN32_WCE*/ return 0; } #if !defined(_WIN32_WCE) /* * Copy file "from" to file "to". * Destination file is truncated if existing. * Return 1 on success, 0 on error (printing an error). */ static int copy_file(const char *from, const char *to) { char message[256]; FILE *in, *out; char buf[256]; size_t n; /* Open "from" file for reading */ in=fopen(from, "r"); if ( in == NULL ) { snprintf(message, 255, "Can't open %s for reading: %s\n", from, strerror(errno)); fprintf(stderr, "%s", message); return 0; } /* Open "to" file for writing (will truncate existing files) */ out=fopen(to, "w"); if ( out == NULL ) { snprintf(message, 255, "Can't open %s for writing: %s\n", to, strerror(errno)); fprintf(stderr, "%s", message); fclose(in); return 0; } /* Copy data from "in" to "out" */ while ( (n=fread(buf, 1, sizeof buf, in)) > 0 ) { if ( ! fwrite(buf, 1, n, out) ) { fclose(in); fclose(out); return 0; } } fclose(in); fclose(out); return 1; } #endif /*_WIN32_WCE*/ #ifdef HAVE_READLINE static char ** linephonec_readline_completion(const char *text, int start, int end) { char **matches = NULL; /* * Prevent readline from falling * back to filename-completion */ rl_attempted_completion_over=1; /* * If this is the start of line we complete with commands */ if ( ! start ) { return rl_completion_matches(text, linphonec_command_generator); } /* * Otherwise, we should peek at command name * or context to implement a smart completion. * For example: "call .." could return * friends' sip-uri as matches */ return matches; } #endif /* * Strip blanks from a string. * Return a pointer into the provided string. * Modifies input adding a NULL at first * of trailing blanks. */ char * lpc_strip_blanks(char *input) { char *iptr; /* Find first non-blank */ while(*input && isspace(*input)) ++input; /* Find last non-blank */ iptr=input+strlen(input); if (iptr > input) { while(isspace(*--iptr)); *(iptr+1)='\0'; } return input; } /**************************************************************************** * * $Log: linphonec.c,v $ * Revision 1.57 2007/11/14 13:40:27 smorlat * fix --disable-video build. * * Revision 1.56 2007/09/26 14:07:27 fixkowalski * - ANSI/C++ compilation issues with non-GCC compilers * - Faster epm-based packaging * - Ability to build & run on FC6's eXosip/osip * * Revision 1.55 2007/09/24 16:01:58 smorlat * fix bugs. * * Revision 1.54 2007/08/22 14:06:11 smorlat * authentication bugs fixed. * * Revision 1.53 2007/02/13 21:31:01 smorlat * added patch for general state. * new doxygen for oRTP * gtk-doc removed. * * Revision 1.52 2007/01/10 14:11:24 smorlat * add --video to linphonec. * * Revision 1.51 2006/08/21 12:49:59 smorlat * merged several little patches. * * Revision 1.50 2006/07/26 08:17:28 smorlat * fix bugs. * * Revision 1.49 2006/07/17 18:45:00 smorlat * support for several event queues in ortp. * glib dependency removed from coreapi/ and console/ * * Revision 1.48 2006/04/09 12:45:32 smorlat * linphonec improvements. * * Revision 1.47 2006/04/04 08:04:34 smorlat * switched to mediastreamer2, most bugs fixed. * * Revision 1.46 2006/03/16 17:17:40 smorlat * fix various bugs. * * Revision 1.45 2006/03/12 21:48:31 smorlat * gcc-2.95 compile error fixed. * mediastreamer2 in progress * * Revision 1.44 2006/03/04 11:17:10 smorlat * mediastreamer2 in progress. * * Revision 1.43 2006/02/13 09:50:50 strk * fixed unused variable warning. * * Revision 1.42 2006/02/02 15:39:18 strk * - Added 'friend list' and 'friend call' commands * - Allowed for multiple DTFM send in a single line * - Added status-specific callback (bare version) * * Revision 1.41 2006/02/02 13:30:05 strk * - Padded vtable with missing callbacks * (fixing a segfault on friends subscription) * - Handled friends notify (bare version) * - Handled text messages receive (bare version) * - Printed message on subscription request (bare version) * * Revision 1.40 2006/01/26 09:48:05 strk * Added limits.h include * * Revision 1.39 2006/01/26 02:11:01 strk * Removed unused variables, fixed copyright date * * Revision 1.38 2006/01/25 18:33:02 strk * Removed the -t swich, terminate_on_close made the default behaviour * * Revision 1.37 2006/01/20 14:12:34 strk * Added linphonec_init() and linphonec_finish() functions. * Handled SIGINT and SIGTERM to invoke linphonec_finish(). * Handling of auto-termination (-t) moved to linphonec_finish(). * Reworked main (input read) loop to not rely on 'terminate' * and 'run' variable (dropped). configfile_name allocated on stack * using PATH_MAX limit. Changed print_usage signature to allow * for an exit_status specification. * * Revision 1.36 2006/01/18 09:25:32 strk * Command completion inhibited in proxy addition and auth request prompts. * Avoided use of readline's internal filename completion. * * Revision 1.35 2006/01/14 13:29:32 strk * Reworked commands interface to use a table structure, * used by command line parser and help function. * Implemented first level of completion (commands). * Added notification of invalid "answer" and "terminate" * commands (no incoming call, no active call). * Forbidden "call" intialization when a call is already active. * Cleaned up all commands, adding more feedback and error checks. * * Revision 1.34 2006/01/13 13:00:29 strk * Added linphonec.h. Code layout change (added comments, forward decl, * globals on top, copyright notices and Logs). Handled out-of-memory * condition on history management. Removed assumption on sizeof(char). * Fixed bug in authentication prompt (introduced by readline). * Added support for multiple authentication requests (up to MAX_PENDING_AUTH). * * ****************************************************************************/ liblinphone-4.4.21/console/linphonec.h000066400000000000000000000123741376572216100177200ustar00rootroot00000000000000/**************************************************************************** * * $Id: linphonec.h,v 1.3 2006/01/20 14:12:34 strk Exp $ * * Copyright (C) 2005 Sandro Santilli * **************************************************************************** * * 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 Library 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * ****************************************************************************/ #ifndef LINPHONEC_H #define LINPHONEC_H 1 #ifdef HAVE_CONFIG_H #include "config.h" #endif #ifdef HAVE_READLINE #ifdef HAVE_READLINE_H #include #else #ifdef HAVE_READLINE_READLINE_H #include #endif #endif #ifdef HAVE_HISTORY_H #include #else #ifdef HAVE_READLINE_HISTORY_H #include #endif #endif #endif #undef PARAMS #define INT_TO_VOIDPTR(i) ((void*)(intptr_t)(i)) #define VOIDPTR_TO_INT(p) ((int)(intptr_t)(p)) /************************************************************************** * * Compile-time defines * **************************************************************************/ #define HISTSIZE 500 /* how many lines of input history */ #define PROMPT_MAX_LEN 256 /* max len of prompt string */ #define LINE_MAX_LEN 256 /* really needed ? */ /* * Define this to have your primary contact * as the prompt string */ /* #define IDENTITY_AS_PROMPT 1 */ /* * Time between calls to linphonec_idle_call during main * input read loop in microseconds. */ #define LPC_READLINE_TIMEOUT 10000 /* * Filename of linphonec history */ #define LPC_HIST_FILE "~/.linphonec_history" /* * Maximum number of pending authentications */ #define MAX_PENDING_AUTH 8 /************************************************************************** * * Types * **************************************************************************/ /* * A structure which contains information on the commands this program * can understand. */ typedef int (*lpc_cmd_handler)(LinphoneCore *, char *); typedef struct { char *name; /* User printable name of the function. */ lpc_cmd_handler func; /* Function to call to do the job. */ char *help; /* Short help for this function. */ char *doc; /* Long description. */ } LPC_COMMAND; typedef struct { int x,y,w,h; void *wid; bool_t show; bool_t refresh; } VideoParams; extern VideoParams lpc_video_params; extern VideoParams lpc_preview_params; /*************************************************************************** * * Forward declarations * ***************************************************************************/ extern int linphonec_parse_command_line(LinphoneCore *lc, char *cl); extern char *linphonec_command_generator(const char *text, int state); void linphonec_main_loop_exit(void); extern void linphonec_finish(int exit_status); extern char *linphonec_readline(char *prompt); void linphonec_set_autoanswer(bool_t enabled); bool_t linphonec_get_autoanswer(void); void linphonec_command_finished(void); void linphonec_set_caller(const char *caller); LinphoneCall *linphonec_get_call(int id); void linphonec_call_identify(LinphoneCall* call); extern bool_t linphonec_camera_enabled; #endif /* def LINPHONEC_H */ /**************************************************************************** * * $Log: linphonec.h,v $ * Revision 1.3 2006/01/20 14:12:34 strk * Added linphonec_init() and linphonec_finish() functions. * Handled SIGINT and SIGTERM to invoke linphonec_finish(). * Handling of auto-termination (-t) moved to linphonec_finish(). * Reworked main (input read) loop to not rely on 'terminate' * and 'run' variable (dropped). configfile_name allocated on stack * using PATH_MAX limit. Changed print_usage signature to allow * for an exit_status specification. * * Revision 1.2 2006/01/14 13:29:32 strk * Reworked commands interface to use a table structure, * used by command line parser and help function. * Implemented first level of completion (commands). * Added notification of invalid "answer" and "terminate" * commands (no incoming call, no active call). * Forbidden "call" intialization when a call is already active. * Cleaned up all commands, adding more feedback and error checks. * * Revision 1.1 2006/01/13 13:00:29 strk * Added linphonec.h. Code layout change (added comments, forward decl, * globals on top, copyright notices and Logs). Handled out-of-memory * condition on history management. Removed assumption on sizeof(char). * Fixed bug in authentication prompt (introduced by readline). * Added support for multiple authentication requests (up to MAX_PENDING_AUTH). * * ****************************************************************************/ liblinphone-4.4.21/console/shell.c000066400000000000000000000234261376572216100170430ustar00rootroot00000000000000/* * Copyright (c) 2010-2019 Belledonne Communications SARL. * * This file is part of Liblinphone. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #ifdef _WIN32 #include #include #include #include #include #else #include #include #include #endif #include "ortp/ortp.h" #include #define DEFAULT_REPLY_SIZE 4096 #define STATUS_REGISTERED (1<<0) #define STATUS_REGISTERING (1<<1) #define STATUS_DIALING (1<<2) #define STATUS_AUTOANSWER (1<<3) #define STATUS_IN_CONNECTED (1<<4) /* incoming call accepted */ #define STATUS_OUT_CONNECTED (1<<5) /*outgoing call accepted */ #define STATUS_IN_COMING (1<<6) /*incoming call pending */ static int make_status_value(const char *status_string){ int ret=0; if (strstr(status_string,"registered, identity=")){ ret|=STATUS_REGISTERED; } if (strstr(status_string,"registered=-1")){ ret|=STATUS_REGISTERING; } if (strstr(status_string,"autoanswer=1")){ ret|=STATUS_AUTOANSWER; } if (strstr(status_string,"dialing")){ ret|=STATUS_DIALING; } if (strstr(status_string,"Call out")){ ret|=STATUS_OUT_CONNECTED; } if (strstr(status_string,"hook=answered")){ ret|=STATUS_IN_CONNECTED; } if (strstr(status_string,"Incoming call from ")){ ret|=STATUS_IN_COMING; } return ret; } static int send_command(const char *command, char *reply, int reply_len, int print_errors){ ortp_pipe_t pp; int i; int err; char path[128]; #ifndef _WIN32 snprintf(path,sizeof(path)-1,"linphonec-%i",getuid()); #else { char username[128]; DWORD size=sizeof(username)-1; GetUserName(username,&size); snprintf(path,sizeof(path)-1,"linphonec-%s",username); } #endif if ((pp=ortp_client_pipe_connect(path))==ORTP_PIPE_INVALID){ if (print_errors) fprintf(stderr,"ERROR: Failed to connect pipe: %s\n",strerror(errno)); return -1; } if (ortp_pipe_write(pp,(uint8_t*)command,strlen(command))==-1){ if (print_errors) fprintf(stderr,"ERROR: Fail to send command to remote linphonec\n"); ortp_client_pipe_close(pp); return -1; } /*wait for replies */ i=0; while ((err=ortp_pipe_read(pp,(uint8_t*)&reply[i],reply_len-i-1))>0){ i+=err; } reply[i]='\0'; ortp_client_pipe_close(pp); return 0; } static void print_usage(void){ fprintf(stderr,"Usage:\nlinphonecsh [arguments]\n" "where action is one of\n" "\tinit\t\t: spawn a linphonec daemon (first step to make other actions)\n" "\t\t\tfollowed by the arguments sent to linphonec\n" "\tgeneric\t\t: sends a generic command to the running linphonec daemon\n" "\t\t\tfollowed by the generic command surrounded by quotes,\n\t\t\t for example \"call sip:joe@example.net\"\n" "\tregister\t: register; arguments are \n\t\t\t--host \n\t\t\t--username \n\t\t\t--password \n" "\tunregister\t: unregister\n" "\tdial\t\t: dial \n" "\tstatus\t\t: can be 'status register', 'status autoanswer' or 'status hook'\n" "\tsoundcard\t: can be 'soundcard capture', 'soundcard playback', 'soundcard ring',\n" "\t\t\t followed by an optional number representing the index of the soundcard,\n" "\t\t\t in which case the soundcard is set instead of just read.\n" "\texit\t\t: make the linphonec daemon to exit.\n" ); exit(-1); } #ifdef _WIN32 static char *argv_to_line(int argc, char *argv[]) { int i; int line_length; char *line; assert( argc>=0 ); if(argc == 0) return NULL; line_length = strlen(argv[0]); for(i=1; i. */ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #include #include "sipomatic.h" #include int run_cond=1; Sipomatic sipomatic; int sipomatic_accept_audio_offer(sdp_context_t *ctx,sdp_payload_t *payload); int sipomatic_accept_video_offer(sdp_context_t *ctx,sdp_payload_t *payload); sdp_handler_t sipomatic_sdp_handler={ sipomatic_accept_audio_offer, /*from remote sdp */ sipomatic_accept_video_offer, NULL, NULL, NULL, NULL, }; void stop_handler(int signum) { run_cond=0; } void sipomatic_process_event(Sipomatic *obj,eXosip_event_t *ev) { Call *call; switch(ev->type){ case EXOSIP_CALL_INVITE: call_new(obj,ev); break; case EXOSIP_CALL_CLOSED: case EXOSIP_CALL_CANCELLED: call=sipomatic_find_call(obj,ev->did); if (call==NULL){ ms_warning("Could not find call with did %i !",ev->did); } call_release(call); call_destroy(call); break; default: break; } eXosip_event_free(ev); } void endoffile_cb(void *ud, MSFilter *f, unsigned int ev,void * arg){ Call*call=(Call*)ud; call->eof=1; } void call_accept(Call *call) { sdp_context_t *ctx; PayloadType *payload; char *hellofile; static int call_count=0; char record_file[250]; osip_message_t *msg=NULL; sprintf(record_file,"/tmp/sipomatic%i.wav",call_count); ctx=call->sdpc; payload=rtp_profile_get_payload(call->profile,call->audio.pt); if (strcmp(payload->mime_type,"telephone-event")==0){ /* telephone-event is not enough to accept a call */ ms_message("Cannot accept call with only telephone-event.\n"); eXosip_call_send_answer(call->did,415,NULL); call->state=CALL_STATE_FINISHED; return; } if (payload->clock_rate==16000){ hellofile=call->root->file_path16000hz; }else hellofile=call->root->file_path8000hz; eXosip_call_build_answer(call->tid,200,&msg); osip_message_set_content_type(msg,"application/sdp"); osip_message_set_body(msg,call->sdpc->answerstr,strlen(call->sdpc->answerstr)); eXosip_call_send_answer(call->tid,200,msg); call->audio_stream=audio_stream_new(call->audio.localport,call->audio.localport+1,call->root->ipv6); audio_stream_start_with_files(call->audio_stream, call->profile, call->audio.remaddr,call->audio.remoteport,call->audio.remoteport+1, call->audio.pt,20,hellofile,record_file); call_count++; #ifdef VIDEO_ENABLED if (call->video.remoteport!=0){ video_stream_send_only_start(call->video_stream,call->profile, call->video.remaddr,call->video.remoteport,call->video.remoteport+1,call->video.pt, 60, ms_web_cam_manager_get_default_cam(ms_web_cam_manager_get())); } #endif call->time=time(NULL); call->state=CALL_STATE_RUNNING; ms_filter_set_notify_callback(call->audio_stream->soundread,endoffile_cb,(void*)call); } PayloadType * sipomatic_payload_is_supported(sdp_payload_t *payload,RtpProfile *local_profile,RtpProfile *dialog_profile) { int localpt; if (payload->a_rtpmap!=NULL){ localpt=rtp_profile_get_payload_number_from_rtpmap(local_profile,payload->a_rtpmap); }else{ localpt=payload->pt; ms_warning("payload has no rtpmap."); } if (localpt>=0){ /* this payload is supported in our local rtp profile, so add it to the dialog rtp profile */ PayloadType *rtppayload; rtppayload=rtp_profile_get_payload(local_profile,localpt); if (rtppayload==NULL) return NULL; /*check if we have the appropriate coder/decoder for this payload */ if (strcmp(rtppayload->mime_type,"telephone-event")!=0) { if (!ms_filter_codec_supported(rtppayload->mime_type)) { ms_message("Codec %s is not supported.", rtppayload->mime_type); return NULL; } } rtppayload=payload_type_clone(rtppayload); rtp_profile_set_payload(dialog_profile,payload->pt,rtppayload); /* add to the rtp payload type some other parameters (bandwidth) */ if (payload->b_as_bandwidth!=0) rtppayload->normal_bitrate=payload->b_as_bandwidth*1000; if (payload->a_fmtp!=NULL) payload_type_set_send_fmtp(rtppayload,payload->a_fmtp); if (strcasecmp(rtppayload->mime_type,"iLBC")==0){ /*default to 30 ms mode */ payload->a_fmtp="ptime=30"; payload_type_set_recv_fmtp(rtppayload,payload->a_fmtp); } return rtppayload; } return NULL; } int sipomatic_accept_audio_offer(sdp_context_t *ctx,sdp_payload_t *payload) { static int audioport=8000; Call *call=(Call*)sdp_context_get_user_pointer(ctx); PayloadType *supported; struct stream_params *params=&call->audio; /* see if this codec is supported in our local rtp profile*/ supported=sipomatic_payload_is_supported(payload,&av_profile,call->profile); if (supported==NULL) { ms_message("Refusing codec %i (%s)",payload->pt,payload->a_rtpmap); return -1; } if (strcmp(supported->mime_type,"telephone-event")==0) return 0; if (params->ncodecs==0 ){ /* this is the first codec we may accept*/ params->localport=payload->localport=audioport; params->remoteport=payload->remoteport; params->line=payload->line; params->pt=payload->pt; /* remember the first payload accepted */ params->remaddr=payload->c_addr; params->ncodecs++; audioport+=4; }else{ /* refuse all other audio lines*/ if(params->line!=payload->line) return -1; } return 0; } int sipomatic_accept_video_offer(sdp_context_t *ctx,sdp_payload_t *payload) { #ifdef VIDEO_ENABLED static int videoport=80000; Call *call=(Call*)sdp_context_get_user_pointer(ctx); PayloadType *supported; struct stream_params *params=&call->video; /* see if this codec is supported in our local rtp profile*/ supported=sipomatic_payload_is_supported(payload,&av_profile,call->profile); if (supported==NULL) { ms_message("Refusing video codec %i (%s)",payload->pt,payload->a_rtpmap); return -1; } if (params->ncodecs==0 ){ /* this is the first codec we may accept*/ params->localport=payload->localport=videoport; params->remoteport=payload->remoteport; params->line=payload->line; params->pt=payload->pt; /* remember the first payload accepted */ params->remaddr=payload->c_addr; params->ncodecs++; videoport+=4; }else{ /* refuse all other video lines*/ if(params->line!=payload->line) return -1; } return 0; #else return -1; #endif } void sipomatic_init(Sipomatic *obj, char *url, bool_t ipv6) { osip_uri_t *uri=NULL; int port=5064; obj->ipv6=ipv6; if (url==NULL){ url=getenv("SIPOMATIC_URL"); if (url==NULL){ if (ipv6) url="sip:robot@[::1]:5064"; else url="sip:robot@127.0.0.1:5064"; } } if (url!=NULL) { osip_uri_init(&uri); if (osip_uri_parse(uri,url)==0){ if (uri->port!=NULL) port=atoi(uri->port); }else{ ms_warning("Invalid identity uri:%s",url); } } ms_message("Starting using url %s",url); ms_mutex_init(&obj->lock,NULL); obj->calls=NULL; obj->acceptance_time=5; obj->max_call_time=300; obj->file_path8000hz=ms_strdup_printf("%s/%s",PACKAGE_SOUND_DIR,ANNOUCE_FILE8000HZ); obj->file_path16000hz=ms_strdup_printf("%s/%s",PACKAGE_SOUND_DIR,ANNOUCE_FILE16000HZ); osip_trace_initialize(OSIP_INFO1,stdout); osip_trace_initialize(OSIP_INFO2,stdout); osip_trace_initialize(OSIP_WARNING,stdout); osip_trace_initialize(OSIP_ERROR,stdout); osip_trace_initialize(OSIP_BUG,stdout); osip_trace_initialize(OSIP_FATAL,stdout); osip_trace_enable_level(OSIP_INFO1); osip_trace_enable_level(OSIP_INFO2); osip_trace_enable_level(OSIP_WARNING); osip_trace_enable_level(OSIP_ERROR); osip_trace_enable_level(OSIP_BUG); osip_trace_enable_level(OSIP_FATAL); eXosip_init(); eXosip_set_user_agent("sipomatic-" LINPHONE_VERSION "/eXosip"); eXosip_listen_addr(IPPROTO_UDP,NULL,port,ipv6 ? AF_INET6 : AF_INET,0); } void sipomatic_uninit(Sipomatic *obj) { ms_mutex_destroy(&obj->lock); eXosip_quit(); } void sipomatic_iterate(Sipomatic *obj) { bctbx_list_t *elem; bctbx_list_t *to_be_destroyed=NULL; Call *call; double elapsed; eXosip_event_t *ev; while((ev=eXosip_event_wait(0,0))!=NULL){ sipomatic_process_event(obj,ev); } elem=obj->calls; while(elem!=NULL){ call=(Call*)elem->data; elapsed=time(NULL)-call->time; switch(call->state){ case CALL_STATE_INIT: if (elapsed>obj->acceptance_time){ call_accept(call); } break; case CALL_STATE_RUNNING: if (elapsed>obj->max_call_time || call->eof){ call_release(call); to_be_destroyed=bctbx_list_append(to_be_destroyed,call); } break; } elem=bctbx_list_next(elem); } for(;to_be_destroyed!=NULL; to_be_destroyed=bctbx_list_next(to_be_destroyed)){ call_destroy((Call*)to_be_destroyed->data); } } Call* sipomatic_find_call(Sipomatic *obj,int did) { bctbx_list_t *it; Call *call=NULL; for (it=obj->calls;it!=NULL;it=bctbx_list_next(it)){ call=(Call*)it->data; if ( call->did==did) return call; } return call; } Call * call_new(Sipomatic *root, eXosip_event_t *ev) { Call *obj; char *sdpans; int status; sdp_message_t *sdp; sdp_context_t *sdpc; sdp=eXosip_get_sdp_info(ev->request); sdpc=sdp_handler_create_context(&sipomatic_sdp_handler,NULL,"sipomatic",NULL); obj=ms_new0(Call,1); obj->profile=rtp_profile_new("remote"); eXosip_call_send_answer(ev->tid,100,NULL); sdp_context_set_user_pointer(sdpc,obj); sdpans=sdp_context_get_answer(sdpc,sdp); if (sdpans!=NULL){ eXosip_call_send_answer(ev->tid,180,NULL); }else{ status=sdp_context_get_status(sdpc); eXosip_call_send_answer(ev->tid,status,NULL); sdp_context_free(sdpc); rtp_profile_destroy(obj->profile); ms_free(obj); return NULL; } obj->sdpc=sdpc; obj->did=ev->did; obj->tid=ev->tid; obj->time=time(NULL); obj->audio_stream=NULL; obj->state=CALL_STATE_INIT; obj->eof=0; obj->root=root; root->calls=bctbx_list_append(root->calls,obj); return obj; } void call_release(Call *call) { eXosip_call_terminate(0,call->did); if (call->audio_stream!=NULL) audio_stream_stop(call->audio_stream); #ifdef VIDEO_ENABLED if (call->video_stream!=NULL) video_stream_send_only_stop(call->video_stream); #endif call->state=CALL_STATE_FINISHED; } void call_destroy(Call *obj) { obj->root->calls=bctbx_list_remove(obj->root->calls,obj); rtp_profile_destroy(obj->profile); sdp_context_free(obj->sdpc); ms_free(obj); } void sipomatic_set_annouce_file(Sipomatic *obj, char *file) { if (obj->file_path8000hz!=NULL){ ms_free(obj->file_path8000hz); } obj->file_path8000hz=ms_strdup(file); } void display_help() { printf("sipomatic [-u sip-url] [-f annouce-file ] [-s port]\n" "sipomatic -h or --help: display this help.\n" "sipomatic -v or --version: display version information.\n" " -u sip-url : specify the sip url sipomatic listens and answers.\n" " -f annouce-file : set the annouce file (16 bit raw format,8000Hz)\n" " -6 enable ipv6 network usage\n"); exit(0); } char *getarg(int argc, char*argv[], int i) { if (i. */ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #include "../coreapi/linphonecore.h" #include "../coreapi/sdphandler.h" #include #undef PACKAGE #undef VERSION #include "mediastreamer2/mediastream.h" #include #include #define ANNOUCE_FILE8000HZ "hello8000.wav" #define ANNOUCE_FILE16000HZ "hello16000.wav" struct _Sipomatic { ms_mutex_t lock; MSList *calls; double acceptance_time; double max_call_time; char *file_path8000hz; char *file_path16000hz; bool_t ipv6; }; typedef struct _Sipomatic Sipomatic; void sipomatic_init(Sipomatic *obj, char *url, bool_t ipv6); void sipomatic_uninit(Sipomatic *obj); void sipomatic_iterate(Sipomatic *obj); #define sipomatic_lock(obj) ms_mutex_lock(&(obj)->lock); #define sipomatic_unlock(obj) ms_mutex_unlock(&(obj)->lock); void sipomatic_set_annouce_file(Sipomatic *obj, char *file); struct stream_params{ int ncodecs; int line; int localport; int remoteport; int pt; char *remaddr; }; struct _Call { Sipomatic *root; sdp_context_t *sdpc; int time; int did; int tid; AudioStream *audio_stream; #ifdef VIDEO_ENABLED VideoStream *video_stream; #endif int state; struct _CallParams *params; int eof; RtpProfile *profile; struct stream_params audio; struct stream_params video; }; #define CALL_STATE_INIT 0 #define CALL_STATE_RUNNING 1 #define CALL_STATE_FINISHED 2 typedef struct _Call Call; Call * call_new(Sipomatic *obj, eXosip_event_t *ev); void call_accept(Call *call); void call_release(Call *call); void call_destroy(Call *call); Call* sipomatic_find_call(Sipomatic *obj,int cid); liblinphone-4.4.21/console/wav2raw.c000066400000000000000000000036011376572216100173160ustar00rootroot00000000000000/* * Copyright (c) 2010-2019 Belledonne Communications SARL. * * This file is part of Liblinphone. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "../config.h" #include #include #include #include #include #include #include #include int main(int argc, char *argv[]) { int ifd,ofd; char *name,*p; char buf[200]; int len; if (argc<2) return -1; name=malloc(strlen(argv[1])+10); sprintf(name,"%s",argv[1]); p=strstr(name,".raw"); if (p!=NULL){ sprintf(p,"%s",".wav\0"); }else{ sprintf(name,"%s%s",argv[1],".raw"); } ifd=open(name,O_RDONLY); if (ifd<0) { perror("Could not open input file"); return -1; } ofd=open(argv[1],O_WRONLY|O_CREAT|O_TRUNC,S_IRUSR|S_IWUSR|S_IRGRP); if (ofd<0) { perror("Could not open output file"); return -1; } len=read(ifd,buf,20); printf("len=%i\n",len); /* erase the wav header */ if (len>0){ memset(buf,0,20); write(ofd,buf,20); }else{ printf("Error while processing %s: %s\n",argv[1],strerror(errno)); return -1; }; while ( (len=read(ifd,buf,200))>0){ #ifdef WORDS_BIGENDIAN for (i=0;i. */ #include "TunnelManager.hh" #include "ortp/rtpsession.h" #include "linphone/core.h" #include "linphone/core_utils.h" #include "private.h" #include "private_functions.h" #ifdef __ANDROID__ #include #endif belledonnecomm::TunnelManager *bcTunnel(const LinphoneTunnel *tunnel); using namespace belledonnecomm; using namespace ::std; void TunnelManager::addServer(const char *ip, int port,unsigned int udpMirrorPort,unsigned int delay) { if (ip == NULL) { ms_warning("Adding tunnel server with empty ip, it will not work!"); return; } addServer(ip,port); mUdpMirrorClients.push_back(UdpMirrorClient(ServerAddr(ip, (int)udpMirrorPort), delay)); } void TunnelManager::addServer(const char *ip, int port) { if (ip == NULL) { ms_warning("Adding tunnel server with empty ip, it will not work!"); return; } if (mUseDualClient) { ms_warning("TunnelManager is configured in dual mode, use addServerPair instead"); return; } mServerAddrs.push_back(ServerAddr(ip,port)); if (mTunnelClient && !mTunnelClient->isDualTunnelClient()) { static_cast(mTunnelClient)->addServer(ip,port); } } void TunnelManager::addServerPair(const char *ip1, int port1, const char *ip2, int port2, unsigned int udpMirrorPort, unsigned int delay) { if (ip1 == NULL || ip2 == NULL) { ms_warning("Adding tunnel server with empty ip, it will not work!"); return; } addServerPair(ip1, port1, ip2, port2); mUdpMirrorClients.push_back(UdpMirrorClient(ServerAddr(ip1, (int)udpMirrorPort), delay)); } void TunnelManager::addServerPair(const char *ip1, int port1, const char *ip2, int port2) { if (ip1 == NULL || ip2 == NULL) { ms_warning("Adding tunnel server with empty ip, it will not work!"); return; } if (!mUseDualClient) { ms_warning("TunnelManager is configured in single mode, use addServer instead"); return; } mDualServerAddrs.push_back(DualServerAddr(ip1, port1, ip2, port2)); if (mTunnelClient && mTunnelClient->isDualTunnelClient()) { static_cast(mTunnelClient)->addServerPair(ip1, port1, ip2, port2); } } void TunnelManager::cleanServers() { mServerAddrs.clear(); mDualServerAddrs.clear(); if (mLongRunningTaskId > 0) { sal_end_background_task(mLongRunningTaskId); mLongRunningTaskId = 0; } for (auto &udpMirrorClient : mUdpMirrorClients) udpMirrorClient.stop(); mUdpMirrorClients.clear(); mCurrentUdpMirrorClient = mUdpMirrorClients.end(); if (mTunnelClient) mTunnelClient->cleanServers(); } void TunnelManager::enableDualMode(bool enable) { mUseDualClient = enable; } bool TunnelManager::isDualModeEnabled() { return mUseDualClient; } void TunnelManager::reconnect(){ if (mTunnelClient) mTunnelClient->reconnect(); } static void sCloseRtpTransport(RtpTransport *t){ DualSocket *ds = (DualSocket *)t->data; TunnelSocket *sendSocket = ds->sendSocket; TunnelSocket *recvSocket = ds->recvSocket; TunnelManager *manager=(TunnelManager*)sendSocket->getUserPointer(); manager->closeRtpTransport(t, sendSocket); manager->closeRtpTransport(t, recvSocket); ms_free(ds); } void TunnelManager::closeRtpTransport(RtpTransport *t, TunnelSocket *s){ mTunnelClient->closeSocket(s); } static RtpTransport *sCreateRtpTransport(void* userData, int port){ return ((TunnelManager *) userData)->createRtpTransport(port); } void sDestroyRtpTransport(RtpTransport *t){ ms_free(t); } RtpTransport *TunnelManager::createRtpTransport(int port){ DualSocket *dualSocket = ms_new0(DualSocket, 1); if (!mUseDualClient) { TunnelSocket *socket = ((TunnelClient *)mTunnelClient)->createSocket(port); socket->setUserPointer(this); dualSocket->sendSocket = socket; dualSocket->recvSocket = socket; } else { dualSocket->sendSocket = ((DualTunnelClient *)mTunnelClient)->createSocket(TunnelSendOnly, port); dualSocket->sendSocket->setUserPointer(this); dualSocket->recvSocket = ((DualTunnelClient *)mTunnelClient)->createSocket(TunnelRecvOnly, port); dualSocket->recvSocket->setUserPointer(this); } RtpTransport *t = ms_new0(RtpTransport,1); t->t_getsocket=NULL; t->t_recvfrom=customRecvfrom; t->t_sendto=customSendto; t->t_close=sCloseRtpTransport; t->t_destroy=sDestroyRtpTransport; t->data=dualSocket; ms_message("Creating tunnel RTP transport for local virtual port %i", port); return t; } void TunnelManager::startClient() { ms_message("TunnelManager: Starting tunnel client"); if (!mTunnelClient) { if (mUseDualClient) { mTunnelClient = DualTunnelClient::create(TRUE); } else { mTunnelClient = TunnelClient::create(TRUE); } mCore->sal->setTunnel(mTunnelClient); if (!mUseDualClient) { static_cast(mTunnelClient)->setCallback(tunnelCallback,this); } else { static_cast(mTunnelClient)->setCallback(tunnelCallback2,this); } } if (mVerifyServerCertificate) { const char *rootCertificatePath = linphone_core_get_root_ca(mCore); if (rootCertificatePath != NULL) { ms_message("TunnelManager: Load root certificate from %s", rootCertificatePath); mTunnelClient->setRootCertificate(rootCertificatePath); /* give the path to root certificate to the tunnel client in order to be able to verify the server certificate */ } else { ms_warning("TunnelManager is set to verify server certificate but no root certificate is available in linphoneCore"); } } mTunnelClient->cleanServers(); if (mUseDualClient) { list::iterator it; for(it=mDualServerAddrs.begin();it!=mDualServerAddrs.end();++it){ const DualServerAddr &addr=*it; static_cast(mTunnelClient)->addServerPair(addr.mAddr1.c_str(), addr.mPort1, addr.mAddr2.c_str(), addr.mPort2); } } else { list::iterator it; for(it=mServerAddrs.begin();it!=mServerAddrs.end();++it){ const ServerAddr &addr=*it; static_cast(mTunnelClient)->addServer(addr.mAddr.c_str(), addr.mPort); } } mTunnelClient->setHttpProxy(mHttpProxyHost.c_str(), mHttpProxyPort, mHttpUserName.c_str(), mHttpPasswd.c_str()); if (!mTunnelClient->isStarted()) { ms_message("Starting tunnel client"); mTunnelClient->start(); } else { ms_message("Reconnecting tunnel client"); mTunnelClient->reconnect(); /*force a reconnection to take into account new parameters*/ } } void TunnelManager::stopClient(){ if (mTunnelClient) { ms_message("TunnelManager: stoppping tunnel client"); mTunnelClient->stop(); /* We only delete the tunnel client if there is no call running */ if (linphone_core_get_calls_nb(mCore) == 0){ delete mTunnelClient; mTunnelClient = NULL; } } } bool TunnelManager::isConnected() const { return mState == On; } int TunnelManager::customSendto(struct _RtpTransport *t, mblk_t *msg , int flags, const struct sockaddr *to, socklen_t tolen){ int size; DualSocket *ds = (DualSocket *)t->data; msgpullup(msg, (size_t)-1); size = (int)msgdsize(msg); ds->sendSocket->sendto(msg->b_rptr, (size_t)size, to, tolen); return size; } int TunnelManager::customRecvfrom(struct _RtpTransport *t, mblk_t *msg, int flags, struct sockaddr *from, socklen_t *fromlen){ DualSocket *ds = (DualSocket *)t->data; memset(&msg->recv_addr,0,sizeof(msg->recv_addr)); long err=ds->recvSocket->recvfrom(msg->b_wptr, (size_t)(dblk_lim(msg->b_datap) - dblk_base(msg->b_datap)), from, *fromlen); //to make ice happy inet_aton(((TunnelManager*)(ds->recvSocket)->getUserPointer())->mLocalAddr,&msg->recv_addr.addr.ipi_addr); msg->recv_addr.family = AF_INET; msg->recv_addr.port = htons((unsigned short)(ds->recvSocket)->getPort()); if (err>0) return (int)err; return 0; } TunnelManager::TunnelManager(LinphoneCore* lc) : mCore(lc), mMode(LinphoneTunnelModeDisable), mTunnelClient(NULL), mHttpProxyPort(0), mCoreCbs(NULL), mLongRunningTaskId(0), mSimulateUdpLoss(false), mUseDualClient(false) { linphone_core_add_iterate_hook(mCore,(LinphoneCoreIterateHook)sOnIterate,this); mTransportFactories.audio_rtcp_func=sCreateRtpTransport; mTransportFactories.audio_rtcp_func_data=this; mTransportFactories.audio_rtp_func=sCreateRtpTransport; mTransportFactories.audio_rtp_func_data=this; mTransportFactories.video_rtcp_func=sCreateRtpTransport; mTransportFactories.video_rtcp_func_data=this; mTransportFactories.video_rtp_func=sCreateRtpTransport; mTransportFactories.video_rtp_func_data=this; mCoreCbs = linphone_factory_create_core_cbs(linphone_factory_get()); linphone_core_cbs_set_network_reachable(mCoreCbs, networkReachableCb); linphone_core_cbs_set_global_state_changed(mCoreCbs, globalStateChangedCb); linphone_core_cbs_set_user_data(mCoreCbs,this); _linphone_core_add_callbacks(mCore, mCoreCbs,true); linphone_core_get_local_ip_for(AF_INET, NULL, mLocalAddr); mAutodetectionRunning = false; mState = Off; mTargetState = Off; mStarted = false; mTunnelizeSipPackets = true; } void TunnelManager::unlinkLinphoneCore() { if (mCore) { stopClient(); if (mCore->sal) mCore->sal->setTunnel(NULL); linphone_core_remove_callbacks(mCore, mCoreCbs); linphone_core_cbs_unref(mCoreCbs); mCore = nullptr; mCoreCbs = nullptr; } else { ms_message("Core already cleaned up"); } } TunnelManager::~TunnelManager(){ if (mLongRunningTaskId > 0) { sal_end_background_task(mLongRunningTaskId); mLongRunningTaskId = 0; } for(UdpMirrorClientList::iterator udpMirror = mUdpMirrorClients.begin(); udpMirror != mUdpMirrorClients.end(); udpMirror++) { udpMirror->stop(); } unlinkLinphoneCore(); } void TunnelManager::doRegistration(){ LinphoneProxyConfig* lProxy; lProxy = linphone_core_get_default_proxy_config(mCore); if (lProxy) { ms_message("TunnelManager: New registration"); lProxy->commit = TRUE; } } void TunnelManager::doUnregistration() { LinphoneProxyConfig *lProxy; lProxy = linphone_core_get_default_proxy_config(mCore); if(lProxy) { _linphone_proxy_config_unregister(lProxy); } } void TunnelManager::tunnelizeLiblinphone(){ ms_message("LinphoneCore goes into tunneled mode."); mState = On; /*do this first because _linphone_core_apply_transports() will use it to know if tunnel listening point is to be used*/ linphone_core_set_rtp_transport_factories(mCore,&mTransportFactories); if (mTunnelizeSipPackets) { doUnregistration(); _linphone_core_apply_transports(mCore); doRegistration(); } } void TunnelManager::untunnelizeLiblinphone(){ ms_message("LinphoneCore leaves tunneled mode."); mState = Off; linphone_core_set_rtp_transport_factories(mCore, NULL); if (mTunnelizeSipPackets) { doUnregistration(); _linphone_core_apply_transports(mCore); doRegistration(); } } void TunnelManager::applyState() { if (!linphone_core_is_network_reachable(mCore)) return; if (mTargetState == On && mState == Off){ if (!mTunnelClient || !mTunnelClient->isStarted()){ startClient(); } if (mTunnelClient->isReady()) tunnelizeLiblinphone(); }else if (mTargetState == Off && mState == On){ untunnelizeLiblinphone(); stopClient(); } } void TunnelManager::setState ( TunnelManager::State state ) { mTargetState = state; applyState(); } void TunnelManager::processTunnelEvent(const Event &ev){ if (ev.mData.mConnected){ ms_message("TunnelManager: tunnel is connected"); applyState(); } else { ms_error("TunnelManager: tunnel has been disconnected"); } } void TunnelManager::applyMode() { switch(mMode) { case LinphoneTunnelModeEnable: stopAutoDetection(); setState(On); break; case LinphoneTunnelModeDisable: stopAutoDetection(); setState(Off); break; case LinphoneTunnelModeAuto: if (linphone_core_is_network_reachable(mCore)) startAutoDetection(); break; default: ms_error("TunnelManager::setMode(): invalid mode (%d)", (int)mMode); } } void TunnelManager::setMode(LinphoneTunnelMode mode) { if(mMode == mode) return; ms_message("TunnelManager: switching mode from %s to %s", linphone_tunnel_mode_to_string(mMode), linphone_tunnel_mode_to_string(mode)); mMode = mode; applyMode(); } void TunnelManager::stopLongRunningTask() { if (mLongRunningTaskId != 0) { sal_end_background_task(mLongRunningTaskId); mLongRunningTaskId = 0; } } void TunnelManager::tunnelCallback(bool connected, void *user_pointer){ TunnelManager *zis = static_cast(user_pointer); Event ev; ev.mType=TunnelEvent; ev.mData.mConnected=connected; zis->postEvent(ev); } void TunnelManager::tunnelCallback2(TunnelDirection direction, bool connected, void *user_pointer){ TunnelManager *zis = static_cast(user_pointer); Event ev; ev.mType=TunnelEvent; ev.mData.mConnected=connected; zis->postEvent(ev); } void TunnelManager::onIterate(){ mMutex.lock(); while(!mEvq.empty()){ Event ev=mEvq.front(); mEvq.pop(); mMutex.unlock(); if (ev.mType==TunnelEvent) processTunnelEvent(ev); else if (ev.mType==UdpMirrorClientEvent){ processUdpMirrorEvent(ev); } mMutex.lock(); } mMutex.unlock(); } /*invoked from linphone_core_iterate() */ bool_t TunnelManager::sOnIterate(TunnelManager *zis){ zis->onIterate(); return TRUE; } LinphoneTunnelMode TunnelManager::getMode() const { return mMode; } void TunnelManager::processUdpMirrorEvent(const Event &ev){ if (mAutodetectionRunning == false) return; /*auto detection was cancelled, for example by switching to disabled state*/ if (mSimulateUdpLoss || !ev.mData.mHaveUdp) { if (mSimulateUdpLoss) { ms_message("TunnelManager: simulate UDP lost on %s:%d", mCurrentUdpMirrorClient->getServerAddress().mAddr.c_str(), mCurrentUdpMirrorClient->getServerAddress().mPort); } else { ms_message("TunnelManager: UDP mirror test failed on %s:%d", mCurrentUdpMirrorClient->getServerAddress().mAddr.c_str(), mCurrentUdpMirrorClient->getServerAddress().mPort); } mCurrentUdpMirrorClient++; if (mCurrentUdpMirrorClient !=mUdpMirrorClients.end()) { ms_message("TunnelManager: trying another UDP mirror on %s:%d", mCurrentUdpMirrorClient->getServerAddress().mAddr.c_str(), mCurrentUdpMirrorClient->getServerAddress().mPort); UdpMirrorClient &lUdpMirrorClient=*mCurrentUdpMirrorClient; lUdpMirrorClient.start(TunnelManager::sUdpMirrorClientCallback,(void*)this); mAutodetectionRunning = true; return; } else { ms_message("TunnelManager: all UDP mirror tests failed"); setState(On); } } else { ms_message("TunnelManager: UDP mirror test success on %s:%d", mCurrentUdpMirrorClient->getServerAddress().mAddr.c_str(), mCurrentUdpMirrorClient->getServerAddress().mPort); setState(Off); } mAutodetectionRunning = false; stopLongRunningTask(); } void TunnelManager::postEvent(const Event &ev){ mMutex.lock(); mEvq.push(ev); mMutex.unlock(); } void TunnelManager::sUdpMirrorClientCallback(bool isUdpAvailable, void* data) { TunnelManager* thiz = (TunnelManager*)data; Event ev; ev.mType=UdpMirrorClientEvent; ev.mData.mHaveUdp=isUdpAvailable; thiz->postEvent(ev); } void TunnelManager::networkReachableCb(LinphoneCore *lc, bool_t reachable) { TunnelManager *tunnel = bcTunnel(linphone_core_get_tunnel(lc)); if (reachable) { ms_message("TunnelManager: Network is reachable, starting tunnel client"); linphone_core_get_local_ip_for(AF_INET, NULL,tunnel->mLocalAddr); if (tunnel->getMode() == LinphoneTunnelModeAuto){ tunnel->startAutoDetection(); /*autodetection will call applyState() when finished*/ }else{ tunnel->applyState(); } } else if (!reachable) { ms_message("TunnelManager: Network is unreachable, stopping tunnel client"); // if network is no more reachable, cancel autodetection if any tunnel->stopAutoDetection(); //turn off the tunnel connection tunnel->untunnelizeLiblinphone(); tunnel->stopClient(); } } void TunnelManager::globalStateChangedCb(LinphoneCore *lc, LinphoneGlobalState gstate, const char *message) { if (gstate == LinphoneGlobalOff) { ms_message("Core [%p] is Off, unlinking TunnelManager to core",lc); //calling same core as for object destruction TunnelManager *thiz = (TunnelManager *)linphone_core_cbs_get_user_data(linphone_core_get_current_callbacks(lc)); if (thiz) thiz->unlinkLinphoneCore(); } } void TunnelManager::stopAutoDetection(){ if (mAutodetectionRunning){ for(UdpMirrorClientList::iterator udpMirror = mUdpMirrorClients.begin(); udpMirror != mUdpMirrorClients.end(); udpMirror++) { udpMirror->stop(); } mAutodetectionRunning = false; stopLongRunningTask(); } } bool TunnelManager::startAutoDetection() { if (mUdpMirrorClients.empty()) { ms_error("TunnelManager: No UDP mirror server configured aborting auto detection"); return false; } ms_message("TunnelManager: Starting auto-detection"); mCurrentUdpMirrorClient = mUdpMirrorClients.begin(); if (mLongRunningTaskId == 0) mLongRunningTaskId = sal_begin_background_task("Tunnel auto detect", NULL, NULL); UdpMirrorClient &lUdpMirrorClient=*mCurrentUdpMirrorClient; mAutodetectionRunning = true; lUdpMirrorClient.start(TunnelManager::sUdpMirrorClientCallback,(void*)this); return true; } bool TunnelManager::isActivated() const{ return mState == On; } void TunnelManager::setHttpProxyAuthInfo(const char* username,const char* passwd) { mHttpUserName=username?username:""; mHttpPasswd=passwd?passwd:""; if (mTunnelClient) mTunnelClient->setHttpProxyAuthInfo(username,passwd); } void TunnelManager::tunnelizeSipPackets(bool enable){ mTunnelizeSipPackets = enable; } bool TunnelManager::tunnelizeSipPacketsEnabled() const { return mTunnelizeSipPackets; } void TunnelManager::verifyServerCertificate(bool enable){ mVerifyServerCertificate = enable; } bool TunnelManager::verifyServerCertificateEnabled() const { return mVerifyServerCertificate; } void TunnelManager::setHttpProxy(const char *host,int port, const char *username, const char *passwd){ mHttpUserName=username?username:""; mHttpPasswd=passwd?passwd:""; mHttpProxyPort=(port>0) ? port : 0; mHttpProxyHost=host ? host : ""; if (mTunnelClient) mTunnelClient->setHttpProxy(host, port, username, passwd); } LinphoneCore *TunnelManager::getLinphoneCore() const{ return mCore; } void TunnelManager::simulateUdpLoss(bool enabled) { mSimulateUdpLoss = enabled; } liblinphone-4.4.21/coreapi/TunnelManager.hh000066400000000000000000000223161376572216100206260ustar00rootroot00000000000000/* * C Implementation: tunnel * * Description: * * * *Copyright (C) 2011 Belledonne Comunications, Grenoble, France */ #ifndef __TUNNEL_CLIENT_MANAGER_H__ #define __TUNNEL_CLIENT_MANAGER_H__ #include #include #include #include #include "linphone/core.h" #include "linphone/tunnel.h" #ifndef USE_BELLESIP extern "C" { #include } #endif namespace belledonnecomm { /** * @addtogroup tunnel_client * @{ **/ struct DualSocket { TunnelSocket *sendSocket; TunnelSocket *recvSocket; }; /** * The TunnelManager class extends the LinphoneCore functionnality in order to provide an easy to use API to * - provision tunnel servers ip addresses and ports * - start/stop the tunneling service * - be informed of connection and disconnection events to the tunnel server * - perform auto-detection whether tunneling is required, based on a test of sending/receiving a flow of UDP packets. * * It takes in charge automatically the SIP registration procedure when connecting or disconnecting to a tunnel server. * No other action on LinphoneCore is required to enable full operation in tunnel mode. **/ class TunnelManager { public: /** * Add a tunnel server. At least one should be provided to be able to connect. * When several addresses are provided, the tunnel client may try each of them until it gets connected. * * @param ip tunnMethod definition for '-isInitialStateOn' not foundel server ip address * @param port tunnel server tls port, recommended value is 443 */ void addServer(const char *ip, int port); /** *Add tunnel server with auto detection capabilities * * @param ip tunnel server ip address * @param port tunnel server tls port, recommended value is 443 * @param udpMirrorPort remote port on the tunnel server side used to test udp reachability * @param delay udp packet round trip delay in ms considered as acceptable. recommended value is 1000 ms. */ void addServer(const char *ip, int port,unsigned int udpMirrorPort,unsigned int delay); /** * Add a tunnel server couple. At least one should be provided to be able to connect. * This is used when using the dual socket mode where one client will connect to one ip and the other client to the other ip. * * @param ip1 server ip address n°1 * @param port1 tunnel server tls port, recommended value is 443 * @param ip2 server ip address n°2 * @param port2 tunnel server tls port, recommended value is 443 * @param udpMirrorPort remote port on the tunnel server 1 side used to test udp reachability * @param delay udp packet round trip delay in ms considered as acceptable. recommended value is 1000 ms. */ void addServerPair(const char *ip1, int port1, const char *ip2, int port2, unsigned int udpMirrorPort, unsigned int delay); /** * Add a tunnel server couple. At least one should be provided to be able to connect. * This is used when using the dual socket mode where one client will connect to one ip and the other client to the other ip. * * @param ip1 server ip address n°1 * @param port1 tunnel server tls port, recommended value is 443 * @param ip2 server ip address n°2 * @param port2 tunnel server tls port, recommended value is 443 */ void addServerPair(const char *ip1, int port1, const char *ip2, int port2); /** * Removes all tunnel server address previously entered with addServer() **/ void cleanServers(); /** * Enables the dual socket mode. In this mode, we have to configure pairs or ServerAddr * 2 TunneClient will be used, one for each IP and each one will only either send or receive the data stream. * @param enable true to enable the DualMode, false otherwise */ void enableDualMode(bool enable); /** * Returns whether or not the DualMode is enabled * @return true if it is enabled, false otherwise */ bool isDualModeEnabled(); /** * Forces reconnection to the tunnel server. * This method is useful when the device switches from wifi to Edge/3G or vice versa. In most cases the tunnel client socket * won't be notified promptly that its connection is now zombie, so it is recommended to call this method that will cause * the lost connection to be closed and new connection to be issued. **/ void reconnect(); /** * @brief setMode * @param mode */ void setMode(LinphoneTunnelMode mode); /** * @brief Return the tunnel mode * @return #LinphoneTunnelMode */ LinphoneTunnelMode getMode() const; /** * iOS only feature: specify http proxy credentials. * When the iOS device has an http proxy configured in the iOS settings, the tunnel client will connect to the server * through this http proxy. Credentials might be needed depending on the proxy configuration. * @param username The username. * @param passwd The password. **/ void setHttpProxyAuthInfo(const char* username,const char* passwd); void setHttpProxy(const char *host,int port, const char *username, const char *passwd); /** * Indicate to the tunnel manager whether SIP packets must pass * through the tunnel. That featurte is automatically enabled at * the creation of the TunnelManager instance. * @param enable If set to TRUE, SIP packets will pass through the tunnel. * If set to FALSE, SIP packets will pass by the configured proxies. */ void tunnelizeSipPackets(bool enable); /** * @brief Check whether the tunnel manager is set to tunnelize SIP packets * @return True, SIP packets pass through the tunnel */ bool tunnelizeSipPacketsEnabled() const; /** * Indicate to the tunnel manager wether server certificate * must be verified during TLS handshake. Default: disabled * @param enable If set to TRUE, SIP packets will pass through the tunnel. * If set to FALSE, SIP packets will pass by the configured proxies. */ void verifyServerCertificate(bool enable); /** * Check wether the tunnel manager is set to verify server certificate during TLS handshake * @return True, server certificate is verified(using the linphonecore root certificate) */ bool verifyServerCertificateEnabled() const; /** * @brief Constructor * @param lc The LinphoneCore instance of which the TunnelManager will be associated to. */ TunnelManager(LinphoneCore* lc); /** * @brief Destructor */ ~TunnelManager(); /** * @brief Create an RtpTransport * @param port * @return */ RtpTransport *createRtpTransport(int port); /** * @brief Destroy the given RtpTransport * @param t * @param s */ void closeRtpTransport(RtpTransport *t, TunnelSocket *s); /** * @brief Get associated Linphone Core * @return pointer on the associated LinphoneCore */ LinphoneCore *getLinphoneCore() const; /** * @brief Check wehter the tunnel is connected * @return True whether the tunnel is connected */ bool isConnected() const; bool isActivated() const; void simulateUdpLoss(bool enabled); private: enum EventType{ UdpMirrorClientEvent, TunnelEvent, }; struct Event{ EventType mType; union EventData{ bool mConnected; bool mHaveUdp; }mData; }; typedef std::list UdpMirrorClientList; static int customSendto(struct _RtpTransport *t, mblk_t *msg , int flags, const struct sockaddr *to, socklen_t tolen); static int customRecvfrom(struct _RtpTransport *t, mblk_t *msg, int flags, struct sockaddr *from, socklen_t *fromlen); static void tunnelCallback(bool connected, void *zis); static void tunnelCallback2(TunnelDirection direction, bool connected, void *zis); static bool_t sOnIterate(TunnelManager *zis); static void sUdpMirrorClientCallback(bool result, void* data); static void networkReachableCb(LinphoneCore *lc, bool_t reachable); static void globalStateChangedCb(LinphoneCore *lc, LinphoneGlobalState gstate, const char *message); private: enum State{ Off, /*no tunneling */ On /*tunneling activated*/ }; void onIterate(); void doRegistration(); void doUnregistration(); void startClient(); bool startAutoDetection(); void processTunnelEvent(const Event &ev); void processUdpMirrorEvent(const Event &ev); void postEvent(const Event &ev); void stopClient(); void stopAutoDetection(); void stopLongRunningTask(); void applyMode(); void setState(State state); void applyState(); void tunnelizeLiblinphone(); void untunnelizeLiblinphone(); void unlinkLinphoneCore(); private: LinphoneCore* mCore; LinphoneTunnelMode mMode; TunnelClientI* mTunnelClient; std::string mHttpUserName; std::string mHttpPasswd; std::string mHttpProxyHost; int mHttpProxyPort; LinphoneCoreCbs *mCoreCbs; std::list mServerAddrs; std::list mDualServerAddrs; UdpMirrorClientList mUdpMirrorClients; UdpMirrorClientList::iterator mCurrentUdpMirrorClient; LinphoneRtpTransportFactories mTransportFactories; Mutex mMutex; std::queue mEvq; char mLocalAddr[64]; unsigned long mLongRunningTaskId; State mTargetState; State mState; bool mVerifyServerCertificate; bool mStarted; bool mAutodetectionRunning; bool mTunnelizeSipPackets; bool mSimulateUdpLoss; bool mUseDualClient; }; /** * @} **/ } #endif /*__TUNNEL_CLIENT_MANAGER_H__*/ liblinphone-4.4.21/coreapi/account_creator.c000066400000000000000000002201431376572216100210620ustar00rootroot00000000000000/* * Copyright (c) 2010-2019 Belledonne Communications SARL. * * This file is part of Liblinphone. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "linphone/account_creator.h" #include "linphone/core.h" #include "linphone/lpconfig.h" #include "c-wrapper/c-wrapper.h" #include "dial-plan/dial-plan.h" #include "bctoolbox/crypto.h" #include "bctoolbox/regex.h" // TODO: From coreapi. Remove me later. #include "private.h" // ============================================================================= using namespace LinphonePrivate; BELLE_SIP_DECLARE_NO_IMPLEMENTED_INTERFACES(LinphoneAccountCreatorCbs); BELLE_SIP_INSTANCIATE_VPTR(LinphoneAccountCreatorCbs, belle_sip_object_t, NULL, // destroy NULL, // clone NULL, // marshal FALSE ); /************************** Start Misc **************************/ static const char *_get_domain(LinphoneAccountCreator *creator) { if (creator->domain) return creator->domain; else if (creator->proxy_cfg) return linphone_proxy_config_get_domain(creator->proxy_cfg); return NULL; } static const char* ha1_for_passwd(const char* username, const char* realm, const char* passwd, const char* algo) { if(algo==NULL || strcmp(algo, "MD5")==0) { static char ha1[33]; sal_auth_compute_ha1(username, realm, passwd, ha1); return ha1; } else if(strcmp(algo, "SHA-256")==0) { static char ha1[65]; sal_auth_compute_ha1_for_algorithm(username, realm, passwd, ha1, 65, algo); return ha1; } else { return NULL; } } static unsigned int validate_uri(const char* username, const char* domain, const char* display_name) { LinphoneAddress* addr; unsigned int status = 0; LinphoneProxyConfig* proxy = linphone_proxy_config_new(); linphone_proxy_config_set_identity(proxy, "sip:?@domain.com"); if (username) { addr = linphone_proxy_config_normalize_sip_uri(proxy, username); } else { addr = linphone_address_clone(linphone_proxy_config_get_identity_address(proxy)); } if (addr == NULL) { status = 1; goto end; } if (domain && linphone_address_set_domain(addr, domain) != 0) { status = 1; } if (display_name && (!strlen(display_name) || linphone_address_set_display_name(addr, display_name) != 0)) { status = 1; } linphone_address_unref(addr); end: linphone_proxy_config_destroy(proxy); return status; } static char* _get_identity(const LinphoneAccountCreator *creator) { char *identity = NULL; if ((creator->username || creator->phone_number)) { //we must escape username LinphoneProxyConfig* proxy = linphone_core_create_proxy_config(creator->core); LinphoneAddress* addr; addr = linphone_proxy_config_normalize_sip_uri(proxy, creator->username ? creator->username : creator->phone_number); if (addr == NULL || (creator->domain && strcmp(linphone_address_get_domain(addr), creator->domain) != 0)) { if ((creator->username || creator->phone_number) && creator->domain) { char *url = ms_strdup_printf("sip:%s", creator->domain); addr = linphone_address_new(url); ms_free(url); if (addr) { linphone_address_set_username(addr, creator->username ? creator->username : creator->phone_number); } else { goto end; } } else { goto end; } } identity = linphone_address_as_string(addr); linphone_address_unref(addr); end: linphone_proxy_config_destroy(proxy); } return identity; } static inline void resetField (char **field) { if (*field) { bctbx_free(*field); *field = nullptr; } } LinphoneProxyConfig * linphone_account_creator_create_proxy_config(const LinphoneAccountCreator *creator) { LinphoneAuthInfo *info; LinphoneProxyConfig *cfg = linphone_core_create_proxy_config(creator->core); char *identity_str = _get_identity(creator); LinphoneAddress *identity = linphone_address_new(identity_str); ms_free(identity_str); if (creator->display_name) { linphone_address_set_display_name(identity, creator->display_name); } linphone_proxy_config_set_identity_address(cfg, identity); if (creator->phone_country_code) { linphone_proxy_config_set_dial_prefix(cfg, creator->phone_country_code); } else if (creator->phone_number) { int dial_prefix_number = DialPlan::lookupCccFromE164(creator->phone_number); char buff[4]; snprintf(buff, sizeof(buff), "%d", dial_prefix_number); linphone_proxy_config_set_dial_prefix(cfg, buff); } if (linphone_proxy_config_get_server_addr(cfg) == NULL && creator->domain != NULL) { char *url = ms_strdup_printf("sip:%s", creator->domain); LinphoneAddress *proxy_addr = linphone_address_new(url); if (proxy_addr) { linphone_address_set_transport(proxy_addr, creator->transport); linphone_proxy_config_set_server_addr(cfg, linphone_address_as_string_uri_only(proxy_addr)); linphone_address_unref(proxy_addr); } else { linphone_proxy_config_set_server_addr(cfg, creator->domain); } ms_free(url); } linphone_proxy_config_enable_register(cfg, TRUE); info = linphone_auth_info_new_for_algorithm(linphone_address_get_username(identity), // username NULL, //user id creator->password, // passwd creator->password ? NULL : creator->ha1, // ha1 !creator->password && creator->ha1 ? linphone_address_get_domain(identity) : NULL, // realm - assumed to be domain linphone_address_get_domain(identity), // domain creator->password ? NULL : creator->algorithm //if clear text password is given, allow its usage with all algorithms. ); linphone_core_add_auth_info(creator->core, info); linphone_address_unref(identity); if (linphone_core_add_proxy_config(creator->core, cfg) != -1) { if (creator->set_as_default) { linphone_core_set_default_proxy(creator->core, cfg); } return cfg; } linphone_core_remove_auth_info(creator->core, info); linphone_auth_info_unref(info); return NULL; } LinphoneProxyConfig * linphone_account_creator_configure(const LinphoneAccountCreator *creator) { return linphone_account_creator_create_proxy_config(creator); } /************************** End Misc **************************/ /************************** Start Account Creator Cbs **************************/ LinphoneAccountCreatorCbs * linphone_account_creator_cbs_new(void) { return belle_sip_object_new(LinphoneAccountCreatorCbs); } LinphoneAccountCreatorCbs * linphone_account_creator_cbs_ref(LinphoneAccountCreatorCbs *cbs) { belle_sip_object_ref(cbs); return cbs; } void linphone_account_creator_cbs_unref(LinphoneAccountCreatorCbs *cbs) { belle_sip_object_unref(cbs); } void *linphone_account_creator_cbs_get_user_data(const LinphoneAccountCreatorCbs *cbs) { return cbs->user_data; } void linphone_account_creator_cbs_set_user_data(LinphoneAccountCreatorCbs *cbs, void *ud) { cbs->user_data = ud; } LinphoneAccountCreatorCbsStatusCb linphone_account_creator_cbs_get_create_account(const LinphoneAccountCreatorCbs *cbs) { return cbs->create_account_response_cb; } void linphone_account_creator_cbs_set_create_account(LinphoneAccountCreatorCbs *cbs, LinphoneAccountCreatorCbsStatusCb cb) { cbs->create_account_response_cb = cb; } LinphoneAccountCreatorCbsStatusCb linphone_account_creator_cbs_get_delete_account(const LinphoneAccountCreatorCbs *cbs) { return cbs->delete_account_response_cb; } void linphone_account_creator_cbs_set_delete_account(LinphoneAccountCreatorCbs *cbs, LinphoneAccountCreatorCbsStatusCb cb) { cbs->delete_account_response_cb = cb; } LinphoneAccountCreatorCbsStatusCb linphone_account_creator_cbs_get_is_account_exist(const LinphoneAccountCreatorCbs *cbs) { return cbs->is_account_exist_response_cb; } void linphone_account_creator_cbs_set_is_account_exist(LinphoneAccountCreatorCbs *cbs, LinphoneAccountCreatorCbsStatusCb cb) { cbs->is_account_exist_response_cb = cb; } LinphoneAccountCreatorCbsStatusCb linphone_account_creator_cbs_get_confirmation_key(const LinphoneAccountCreatorCbs *cbs) { return cbs->confirmation_key_response_cb; } void linphone_account_creator_cbs_set_confirmation_key(LinphoneAccountCreatorCbs *cbs, LinphoneAccountCreatorCbsStatusCb cb) { cbs->confirmation_key_response_cb = cb; } LinphoneAccountCreatorCbsStatusCb linphone_account_creator_cbs_get_activate_account(const LinphoneAccountCreatorCbs *cbs) { return cbs->activate_account_response_cb; } void linphone_account_creator_cbs_set_activate_account(LinphoneAccountCreatorCbs *cbs, LinphoneAccountCreatorCbsStatusCb cb) { cbs->activate_account_response_cb = cb; } LinphoneAccountCreatorCbsStatusCb linphone_account_creator_cbs_get_is_account_activated(const LinphoneAccountCreatorCbs *cbs) { return cbs->is_account_activated_response_cb; } void linphone_account_creator_cbs_set_is_account_activated(LinphoneAccountCreatorCbs *cbs, LinphoneAccountCreatorCbsStatusCb cb) { cbs->is_account_activated_response_cb = cb; } LinphoneAccountCreatorCbsStatusCb linphone_account_creator_cbs_get_link_account(const LinphoneAccountCreatorCbs *cbs) { return cbs->link_account_response_cb; } void linphone_account_creator_cbs_set_link_account(LinphoneAccountCreatorCbs *cbs, LinphoneAccountCreatorCbsStatusCb cb) { cbs->link_account_response_cb = cb; } LinphoneAccountCreatorCbsStatusCb linphone_account_creator_cbs_get_activate_alias(const LinphoneAccountCreatorCbs *cbs) { return cbs->activate_alias_response_cb; } void linphone_account_creator_cbs_set_activate_alias(LinphoneAccountCreatorCbs *cbs, LinphoneAccountCreatorCbsStatusCb cb) { cbs->activate_alias_response_cb = cb; } LinphoneAccountCreatorCbsStatusCb linphone_account_creator_cbs_get_is_alias_used(const LinphoneAccountCreatorCbs *cbs) { return cbs->is_alias_used_response_cb; } void linphone_account_creator_cbs_set_is_alias_used(LinphoneAccountCreatorCbs *cbs, LinphoneAccountCreatorCbsStatusCb cb) { cbs->is_alias_used_response_cb = cb; } LinphoneAccountCreatorCbsStatusCb linphone_account_creator_cbs_get_is_account_linked(const LinphoneAccountCreatorCbs *cbs) { return cbs->is_account_linked_response_cb; } void linphone_account_creator_cbs_set_is_account_linked(LinphoneAccountCreatorCbs *cbs, LinphoneAccountCreatorCbsStatusCb cb) { cbs->is_account_linked_response_cb = cb; } LinphoneAccountCreatorCbsStatusCb linphone_account_creator_cbs_get_recover_account(const LinphoneAccountCreatorCbs *cbs) { return cbs->recover_account_response_cb; } void linphone_account_creator_cbs_set_recover_account(LinphoneAccountCreatorCbs *cbs, LinphoneAccountCreatorCbsStatusCb cb) { cbs->recover_account_response_cb = cb; } LinphoneAccountCreatorCbsStatusCb linphone_account_creator_cbs_get_update_account(const LinphoneAccountCreatorCbs *cbs) { return cbs->update_account_response_cb; } void linphone_account_creator_cbs_set_update_account(LinphoneAccountCreatorCbs *cbs, LinphoneAccountCreatorCbsStatusCb cb) { cbs->update_account_response_cb = cb; } LinphoneAccountCreatorCbsStatusCb linphone_account_creator_cbs_get_login_linphone_account(const LinphoneAccountCreatorCbs *cbs) { return cbs->login_linphone_account_response_cb; } void linphone_account_creator_cbs_set_login_linphone_account(LinphoneAccountCreatorCbs *cbs, LinphoneAccountCreatorCbsStatusCb cb) { cbs->login_linphone_account_response_cb = cb; } /************************** End Account Creator Cbs **************************/ /************************** Start Account Creator data **************************/ static void _linphone_account_creator_destroy(LinphoneAccountCreator *creator) { /*this will drop all pending requests if any*/ if (creator->xmlrpc_session) linphone_xml_rpc_session_release(creator->xmlrpc_session); if (creator->service != NULL ) { if (linphone_account_creator_service_get_destructor_cb(creator->service) != NULL) linphone_account_creator_service_get_destructor_cb(creator->service)(creator); linphone_account_creator_service_unref(creator->service); } linphone_account_creator_cbs_unref(creator->cbs); bctbx_list_free_with_data(creator->callbacks, (bctbx_list_free_func)linphone_account_creator_cbs_unref); creator->callbacks = nullptr; linphone_account_creator_reset(creator); } BELLE_SIP_DECLARE_NO_IMPLEMENTED_INTERFACES(LinphoneAccountCreator); BELLE_SIP_INSTANCIATE_VPTR(LinphoneAccountCreator, belle_sip_object_t, (belle_sip_object_destroy_t)_linphone_account_creator_destroy, NULL, // clone NULL, // marshal FALSE ); LinphoneAccountCreator * _linphone_account_creator_new(LinphoneCore *core, const char *xmlrpc_url) { LinphoneAccountCreator *creator; creator = belle_sip_object_new(LinphoneAccountCreator); creator->service = linphone_core_get_account_creator_service(core); linphone_account_creator_service_ref(creator->service); creator->cbs = linphone_account_creator_cbs_new(); creator->core = core; creator->transport = LinphoneTransportTcp; creator->xmlrpc_session = (xmlrpc_url) ? linphone_xml_rpc_session_new(core, xmlrpc_url) : NULL; creator->set_as_default = TRUE; creator->proxy_cfg = linphone_core_create_proxy_config(core); if (creator->service != NULL && linphone_account_creator_service_get_constructor_cb(creator->service) != NULL) linphone_account_creator_service_get_constructor_cb(creator->service)(creator); return creator; } static void fill_domain_and_algorithm_if_needed(LinphoneAccountCreator *creator) { if (creator->domain == NULL) { const char* domain = lp_config_get_string(linphone_core_get_config(creator->core), "assistant", "domain", NULL); if (domain) { linphone_account_creator_set_domain(creator, domain); } } if (creator->algorithm == NULL) { const char* algorithm = lp_config_get_string(linphone_core_get_config(creator->core), "assistant", "algorithm", NULL); if (algorithm) { linphone_account_creator_set_algorithm(creator, algorithm); } } } LinphoneAccountCreator * linphone_account_creator_new(LinphoneCore *core, const char *xmlrpc_url) { return _linphone_account_creator_new(core, xmlrpc_url); } void linphone_account_creator_reset(LinphoneAccountCreator *creator) { resetField(&creator->username); resetField(&creator->display_name); resetField(&creator->password); resetField(&creator->ha1); resetField(&creator->phone_number); resetField(&creator->phone_country_code); resetField(&creator->email); resetField(&creator->language); resetField(&creator->activation_code); resetField(&creator->domain); resetField(&creator->route); resetField(&creator->algorithm); if (creator->proxy_cfg) { linphone_proxy_config_unref(creator->proxy_cfg); creator->proxy_cfg = nullptr; } } LinphoneAccountCreator * linphone_core_create_account_creator(LinphoneCore *core, const char *xmlrpc_url) { return _linphone_account_creator_new(core, xmlrpc_url); } LinphoneAccountCreator * linphone_account_creator_ref(LinphoneAccountCreator *creator) { belle_sip_object_ref(creator); return creator; } void linphone_account_creator_unref(LinphoneAccountCreator *creator) { belle_sip_object_unref(creator); } void *linphone_account_creator_get_user_data(const LinphoneAccountCreator *creator) { return creator->user_data; } void linphone_account_creator_set_user_data(LinphoneAccountCreator *creator, void *ud) { creator->user_data = ud; } void linphone_account_creator_set_proxy_config(LinphoneAccountCreator *creator, LinphoneProxyConfig *cfg) { LinphoneProxyConfig *old_cfg = creator->proxy_cfg; creator->proxy_cfg = cfg ? linphone_proxy_config_ref(cfg) : NULL; if (old_cfg) { linphone_proxy_config_unref(old_cfg); } } LinphoneAccountCreatorUsernameStatus linphone_account_creator_set_username(LinphoneAccountCreator *creator, const char *username) { int min_length = lp_config_get_int(linphone_core_get_config(creator->core), "assistant", "username_min_length", -1); int max_length = lp_config_get_int(linphone_core_get_config(creator->core), "assistant", "username_max_length", -1); bool_t use_phone_number = !!lp_config_get_int(linphone_core_get_config(creator->core), "assistant", "use_phone_number", 0); const char* regex = lp_config_get_string(linphone_core_get_config(creator->core), "assistant", "username_regex", 0); if (!username) { resetField(&creator->username); return LinphoneAccountCreatorUsernameStatusOk; } else if (min_length > 0 && strlen(username) < (size_t)min_length) { return LinphoneAccountCreatorUsernameStatusTooShort; } else if (max_length > 0 && strlen(username) > (size_t)max_length) { return LinphoneAccountCreatorUsernameStatusTooLong; } else if (use_phone_number && !linphone_proxy_config_is_phone_number(NULL, username)) { return LinphoneAccountCreatorUsernameStatusInvalid; } else if (regex && !bctbx_is_matching_regex(username, regex)) { return LinphoneAccountCreatorUsernameStatusInvalidCharacters; } else if (validate_uri(username, NULL, NULL) != 0) { return LinphoneAccountCreatorUsernameStatusInvalid; } set_string(&creator->username, username, FALSE); return LinphoneAccountCreatorUsernameStatusOk; } const char * linphone_account_creator_get_username(const LinphoneAccountCreator *creator) { return creator->username; } LinphoneAccountCreatorPhoneNumberStatusMask linphone_account_creator_set_phone_number(LinphoneAccountCreator *creator, const char *phone_number, const char *country_code) { char *normalized_phone_number; LinphoneAccountCreatorPhoneNumberStatusMask return_status = 0; if (!phone_number || !country_code) { if (!phone_number && !country_code) { creator->phone_number = NULL; creator->phone_country_code = NULL; return (LinphoneAccountCreatorPhoneNumberStatusMask)LinphoneAccountCreatorPhoneNumberStatusOk; } else { return (LinphoneAccountCreatorPhoneNumberStatusMask)LinphoneAccountCreatorPhoneNumberStatusInvalid; } } else { if (strlen(country_code) == 0 || strcmp(country_code, "+") == 0) return LinphoneAccountCreatorPhoneNumberStatusInvalidCountryCode; if (!creator->proxy_cfg) creator->proxy_cfg = linphone_core_create_proxy_config(creator->core); LinphoneProxyConfig *numCfg = creator->proxy_cfg; creator->phone_country_code = ms_strdup(country_code[0] == '+' ? &country_code[1] : country_code); linphone_proxy_config_set_dial_prefix(numCfg, creator->phone_country_code); normalized_phone_number = linphone_proxy_config_normalize_phone_number(numCfg, phone_number); if (!normalized_phone_number) { return LinphoneAccountCreatorPhoneNumberStatusInvalid; } // if phone is valid, we lastly want to check that length is OK in case phone_nunber was normilized if (strcmp(normalized_phone_number,phone_number) != 0 || phone_number[0] != '+') { std::shared_ptr plan = DialPlan::findByCcc(creator->phone_country_code); int size = (int)strlen(phone_number); if (plan->isGeneric()) { return_status = LinphoneAccountCreatorPhoneNumberStatusInvalidCountryCode; } // DO NOT NOTIFY ABOUT PHONE NUMBER BEING TOO SHORT, // OUR DIAL PLAN IMPLEMENTATION ISNT PRECISE ENOUGH TO GARANTY // THE PHONE NUMBER IS INVALID /*if (size < plan->getNationalNumberLength() - 1) { return_status = LinphoneAccountCreatorPhoneNumberStatusTooShort; goto end; } else*/ if (size > plan->getNationalNumberLength() + 1) { return_status = LinphoneAccountCreatorPhoneNumberStatusTooLong; goto end; } else if (return_status & LinphoneAccountCreatorPhoneNumberStatusInvalidCountryCode) { goto end; } } } set_string(&creator->phone_number, normalized_phone_number, FALSE); return_status = LinphoneAccountCreatorPhoneNumberStatusOk; end: ms_free(normalized_phone_number); return return_status; } const char * linphone_account_creator_get_phone_number(const LinphoneAccountCreator *creator) { return creator->phone_number; } LinphoneAccountCreatorPasswordStatus linphone_account_creator_set_password(LinphoneAccountCreator *creator, const char *password) { int min_length = lp_config_get_int(linphone_core_get_config(creator->core), "assistant", "password_min_length", -1); int max_length = lp_config_get_int(linphone_core_get_config(creator->core), "assistant", "password_max_length", -1); if (!password) { resetField(&creator->password); return LinphoneAccountCreatorPasswordStatusTooShort; } if (min_length > 0 && strlen(password) < (size_t)min_length) { return LinphoneAccountCreatorPasswordStatusTooShort; } else if (max_length > 0 && strlen(password) > (size_t)max_length) { return LinphoneAccountCreatorPasswordStatusTooLong; } set_string(&creator->password, password, FALSE); return LinphoneAccountCreatorPasswordStatusOk; } const char * linphone_account_creator_get_password(const LinphoneAccountCreator *creator) { return creator->password; } LinphoneAccountCreatorAlgoStatus linphone_account_creator_set_algorithm(LinphoneAccountCreator *creator, const char *algorithm) { set_string(&creator->algorithm, algorithm, FALSE); if(algorithm && strcmp(algorithm,"MD5") && strcmp(algorithm, "SHA-256")) { return LinphoneAccountCreatorAlgoStatusNotSupported; } return LinphoneAccountCreatorAlgoStatusOk; } const char * linphone_account_creator_get_algorithm(const LinphoneAccountCreator *creator) { return creator->algorithm; } LinphoneAccountCreatorPasswordStatus linphone_account_creator_set_ha1(LinphoneAccountCreator *creator, const char *ha1){ set_string(&creator->ha1, ha1, FALSE); return LinphoneAccountCreatorPasswordStatusOk; } const char * linphone_account_creator_get_ha1(const LinphoneAccountCreator *creator) { return creator->ha1; } LinphoneAccountCreatorActivationCodeStatus linphone_account_creator_set_activation_code(LinphoneAccountCreator *creator, const char *activation_code){ set_string(&creator->activation_code, activation_code, FALSE); return LinphoneAccountCreatorActivationCodeStatusOk; } const char * linphone_account_creator_get_activation_code(const LinphoneAccountCreator *creator) { return creator->activation_code; } LinphoneAccountCreatorLanguageStatus linphone_account_creator_set_language(LinphoneAccountCreator *creator, const char *lang) { set_string(&creator->language, lang, FALSE); return LinphoneAccountCreatorLanguageStatusOk; } const char * linphone_account_creator_get_language(const LinphoneAccountCreator *creator) { return creator->language; } LinphoneAccountCreatorUsernameStatus linphone_account_creator_set_display_name(LinphoneAccountCreator *creator, const char *display_name) { if (validate_uri(NULL, display_name, NULL) != 0) { return LinphoneAccountCreatorUsernameStatusInvalid; } set_string(&creator->display_name, display_name, FALSE); return LinphoneAccountCreatorUsernameStatusOk; } const char * linphone_account_creator_get_display_name(const LinphoneAccountCreator *creator) { return creator->display_name; } LinphoneAccountCreatorEmailStatus linphone_account_creator_set_email(LinphoneAccountCreator *creator, const char *email) { if (!email || !bctbx_is_matching_regex(email, "^.+@.+\\..*$")) { return LinphoneAccountCreatorEmailStatusMalformed; } if (!bctbx_is_matching_regex(email, "^.+@.+\\.[A-Za-z]{2}[A-Za-z]*$")) { return LinphoneAccountCreatorEmailStatusInvalidCharacters; } set_string(&creator->email, email, TRUE); return LinphoneAccountCreatorEmailStatusOk; } const char * linphone_account_creator_get_email(const LinphoneAccountCreator *creator) { return creator->email; } LinphoneAccountCreatorDomainStatus linphone_account_creator_set_domain(LinphoneAccountCreator *creator, const char *domain) { if (domain && validate_uri(NULL, domain, NULL) != 0) { return LinphoneAccountCreatorDomainInvalid; } set_string(&creator->domain, domain, TRUE); return LinphoneAccountCreatorDomainOk; } const char * linphone_account_creator_get_domain(const LinphoneAccountCreator *creator) { return creator->domain; } LinphoneAccountCreatorTransportStatus linphone_account_creator_set_transport(LinphoneAccountCreator *creator, LinphoneTransportType transport) { if (!linphone_core_sip_transport_supported(creator->core, transport)) { return LinphoneAccountCreatorTransportUnsupported; } creator->transport = transport; return LinphoneAccountCreatorTransportOk; } LinphoneTransportType linphone_account_creator_get_transport(const LinphoneAccountCreator *creator) { return creator->transport; } LinphoneAccountCreatorStatus linphone_account_creator_set_route(LinphoneAccountCreator *creator, const char *route) { if (!creator->proxy_cfg) creator->proxy_cfg = linphone_core_create_proxy_config(creator->core); if (!route || linphone_proxy_config_set_route(creator->proxy_cfg, route) != 0) return LinphoneAccountCreatorStatusRequestFailed; set_string(&creator->route, route, TRUE); return LinphoneAccountCreatorStatusRequestOk; } const char * linphone_account_creator_get_route(const LinphoneAccountCreator *creator) { return creator->route; } LinphoneAccountCreatorStatus linphone_account_creator_set_as_default(LinphoneAccountCreator *creator, bool_t set_as_default) { creator->set_as_default = set_as_default; return LinphoneAccountCreatorStatusRequestOk; } bool_t linphone_account_creator_get_set_as_default(const LinphoneAccountCreator *creator) { return creator->set_as_default; } LinphoneAccountCreatorCbs * linphone_account_creator_get_callbacks(const LinphoneAccountCreator *creator) { return creator->cbs; } void linphone_account_creator_add_callbacks(LinphoneAccountCreator *creator, LinphoneAccountCreatorCbs *cbs) { creator->callbacks = bctbx_list_append(creator->callbacks, linphone_account_creator_cbs_ref(cbs)); } void linphone_account_creator_remove_callbacks(LinphoneAccountCreator *creator, LinphoneAccountCreatorCbs *cbs) { creator->callbacks = bctbx_list_remove(creator->callbacks, cbs); linphone_account_creator_cbs_unref(cbs); } LinphoneAccountCreatorCbs *linphone_account_creator_get_current_callbacks(const LinphoneAccountCreator *creator) { return creator->currentCbs; } void linphone_account_creator_set_current_callbacks(LinphoneAccountCreator *creator, LinphoneAccountCreatorCbs *cbs) { creator->currentCbs = cbs; } const bctbx_list_t *linphone_account_creator_get_callbacks_list(const LinphoneAccountCreator *creator) { return creator->callbacks; } #define NOTIFY_IF_EXIST(cbName, functionName, ...) \ bctbx_list_t *callbacksCopy = bctbx_list_copy(linphone_account_creator_get_callbacks_list(creator)); \ for (bctbx_list_t *it = callbacksCopy; it; it = bctbx_list_next(it)) { \ linphone_account_creator_set_current_callbacks(creator, reinterpret_cast(bctbx_list_get_data(it))); \ LinphoneAccountCreatorCbs ## cbName ## Cb cb = linphone_account_creator_cbs_get_ ## functionName (linphone_account_creator_get_current_callbacks(creator)); \ if (cb) \ cb(__VA_ARGS__); \ } \ linphone_account_creator_set_current_callbacks(creator, nullptr); \ bctbx_list_free(callbacksCopy); LinphoneAccountCreatorService * linphone_account_creator_get_service(const LinphoneAccountCreator *creator) { return creator->service; } LinphoneAccountCreatorStatus linphone_account_creator_is_account_exist(LinphoneAccountCreator *creator) { return creator->service->is_account_exist_request_cb(creator); } LinphoneAccountCreatorStatus linphone_account_creator_create_account(LinphoneAccountCreator *creator) { return creator->service->create_account_request_cb(creator); } LinphoneAccountCreatorStatus linphone_account_creator_delete_account(LinphoneAccountCreator *creator) { return creator->service->delete_account_request_cb(creator); } LinphoneAccountCreatorStatus linphone_account_creator_get_confirmation_key(LinphoneAccountCreator *creator) { return creator->service->confirmation_key_request_cb(creator); } LinphoneAccountCreatorStatus linphone_account_creator_is_account_activated(LinphoneAccountCreator *creator) { return creator->service->is_account_activated_request_cb(creator); } LinphoneAccountCreatorStatus linphone_account_creator_activate_account(LinphoneAccountCreator *creator) { return creator->service->activate_account_request_cb(creator); } LinphoneAccountCreatorStatus linphone_account_creator_link_account(LinphoneAccountCreator *creator) { return creator->service->link_account_request_cb(creator); } LinphoneAccountCreatorStatus linphone_account_creator_activate_alias(LinphoneAccountCreator *creator) { return creator->service->activate_alias_request_cb(creator); } LinphoneAccountCreatorStatus linphone_account_creator_is_alias_used(LinphoneAccountCreator *creator) { return creator->service->is_alias_used_request_cb(creator); } LinphoneAccountCreatorStatus linphone_account_creator_is_account_linked(LinphoneAccountCreator *creator) { return creator->service->is_account_linked_request_cb(creator); } LinphoneAccountCreatorStatus linphone_account_creator_recover_account(LinphoneAccountCreator *creator) { return creator->service->recover_account_request_cb(creator); } LinphoneAccountCreatorStatus linphone_account_creator_update_account(LinphoneAccountCreator *creator) { return creator->service->update_account_request_cb(creator); } LinphoneAccountCreatorStatus linphone_account_creator_login_linphone_account(LinphoneAccountCreator *creator) { return creator->service->login_linphone_account_request_cb(creator); } /************************** End Account Creator data **************************/ /************************** Start Account Creator Linphone **************************/ LinphoneAccountCreatorStatus linphone_account_creator_constructor_linphone(LinphoneAccountCreator *creator) { LinphoneAddress *addr; const char *identity = lp_config_get_default_string(linphone_core_get_config(creator->core), "proxy", "reg_identity", NULL); const char *proxy = lp_config_get_default_string(linphone_core_get_config(creator->core), "proxy", "reg_proxy", NULL); const char *route = lp_config_get_default_string(linphone_core_get_config(creator->core), "proxy", "reg_route", NULL); const char *realm = lp_config_get_default_string(linphone_core_get_config(creator->core), "proxy", "realm", NULL); if (!creator->proxy_cfg) creator->proxy_cfg = linphone_core_create_proxy_config(creator->core); linphone_proxy_config_set_realm(creator->proxy_cfg, realm ? realm : "sip.linphone.org"); linphone_proxy_config_set_route(creator->proxy_cfg, route ? route : "sip.linphone.org"); linphone_proxy_config_set_server_addr(creator->proxy_cfg, proxy ? proxy : "sip.linphone.org"); addr = linphone_address_new(identity ? identity : "sip:username@sip.linphone.org"); linphone_proxy_config_set_identity_address(creator->proxy_cfg, addr); linphone_address_unref(addr); return LinphoneAccountCreatorStatusRequestOk; } /****************** START OF ACCOUNT USED SECTION *****************************/ static void _is_account_exist_response_cb(LinphoneXmlRpcRequest *request) { LinphoneAccountCreator *creator = (LinphoneAccountCreator *)linphone_xml_rpc_request_get_user_data(request); LinphoneAccountCreatorStatus status = LinphoneAccountCreatorStatusRequestFailed; const char* resp = linphone_xml_rpc_request_get_string_response(request); if (linphone_xml_rpc_request_get_status(request) == LinphoneXmlRpcStatusOk) { status = (strcmp(resp, "ERROR_ACCOUNT_DOESNT_EXIST") == 0) ? LinphoneAccountCreatorStatusAccountNotExist : ( (strcmp(resp, "ERROR_ALIAS_DOESNT_EXIST") == 0) ? LinphoneAccountCreatorStatusAccountExist : LinphoneAccountCreatorStatusAccountExistWithAlias); if (status == LinphoneAccountCreatorStatusAccountExistWithAlias) { set_string(&creator->phone_number, resp, FALSE); } } if (creator->cbs->is_account_exist_response_cb != NULL) { creator->cbs->is_account_exist_response_cb(creator, status, resp); } NOTIFY_IF_EXIST(Status, is_account_exist, creator, status, resp) } LinphoneAccountCreatorStatus linphone_account_creator_is_account_exist_linphone(LinphoneAccountCreator *creator) { LinphoneXmlRpcRequest *request = NULL; if (!creator->username && !creator->phone_number) { if (creator->cbs->is_account_exist_response_cb != NULL) { creator->cbs->is_account_exist_response_cb(creator, LinphoneAccountCreatorStatusMissingArguments, "Missing required parameters"); } NOTIFY_IF_EXIST(Status, is_account_exist, creator, LinphoneAccountCreatorStatusMissingArguments, "Missing required parameters") return LinphoneAccountCreatorStatusMissingArguments; } fill_domain_and_algorithm_if_needed(creator); if (creator->xmlrpc_session) { ms_debug("Account creator: is_account_exist (%s=%s, domain=%s)", (creator->username) ? "username" : "phone number", (creator->username) ? creator->username : creator->phone_number, _get_domain(creator)); request = linphone_xml_rpc_request_new(LinphoneXmlRpcArgString, "get_phone_number_for_account"); linphone_xml_rpc_request_add_string_arg(request, creator->username ? creator->username : creator->phone_number); linphone_xml_rpc_request_add_string_arg(request, _get_domain(creator)); linphone_xml_rpc_request_set_user_data(request, creator); linphone_xml_rpc_request_cbs_set_response(linphone_xml_rpc_request_get_callbacks(request), _is_account_exist_response_cb); linphone_xml_rpc_session_send_request(creator->xmlrpc_session, request); linphone_xml_rpc_request_unref(request); return LinphoneAccountCreatorStatusRequestOk; } return LinphoneAccountCreatorStatusRequestFailed; } /****************** END OF CREATE ACCOUNT USED SECTION ************************/ /****************** START OF CREATE ACCOUNT SECTION ***************************/ static void _create_account_cb_custom(LinphoneXmlRpcRequest *request) { LinphoneAccountCreator *creator = (LinphoneAccountCreator *)linphone_xml_rpc_request_get_user_data(request); LinphoneAccountCreatorStatus status = LinphoneAccountCreatorStatusRequestFailed; const char* resp = linphone_xml_rpc_request_get_string_response(request); if (linphone_xml_rpc_request_get_status(request) == LinphoneXmlRpcStatusOk) { status = (strcmp(resp, "OK") == 0) ? LinphoneAccountCreatorStatusAccountCreated : (strcmp(resp, "ERROR_CANNOT_SEND_SMS") == 0) ? LinphoneAccountCreatorStatusPhoneNumberInvalid : (strcmp(resp, "ERROR_MAX_SMS_EXCEEDED") == 0) ? LinphoneAccountCreatorStatusPhoneNumberOverused : (strcmp(resp, "ERROR_ACCOUNT_ALREADY_IN_USE") == 0) ? LinphoneAccountCreatorStatusAccountExist : (strcmp(resp, "ERROR_ALIAS_ALREADY_IN_USE") == 0) ? LinphoneAccountCreatorStatusAccountExistWithAlias : (strcmp(resp, "ERROR_ALGO_NOT_SUPPORTED") == 0) ? LinphoneAccountCreatorStatusAlgoNotSupported : LinphoneAccountCreatorStatusAccountNotCreated; } if (creator->cbs->create_account_response_cb != NULL) { creator->cbs->create_account_response_cb(creator, status, resp); } NOTIFY_IF_EXIST(Status, create_account, creator, status, resp) } static LinphoneXmlRpcRequest * _create_account_with_phone_custom(LinphoneAccountCreator *creator) { LinphoneXmlRpcRequest *request; if (!creator->phone_number) { return NULL; } ms_debug("Account creator: create_account_with_phone (phone number=%s, username=%s, domain=%s, language=%s, algo=%s)", creator->phone_number, (creator->username) ? creator->username : creator->phone_number, _get_domain(creator), creator->language, creator->algorithm); request = linphone_xml_rpc_request_new(LinphoneXmlRpcArgString, "create_phone_account"); linphone_xml_rpc_request_add_string_arg(request, creator->phone_number); linphone_xml_rpc_request_add_string_arg(request, creator->username ? creator->username : creator->phone_number); linphone_xml_rpc_request_add_string_arg(request, creator->password ? ha1_for_passwd(creator->username ? creator->username : creator->phone_number, _get_domain(creator), creator->password, creator->algorithm) : ""); linphone_xml_rpc_request_add_string_arg(request, linphone_core_get_user_agent(creator->core)); linphone_xml_rpc_request_add_string_arg(request, _get_domain(creator)); linphone_xml_rpc_request_add_string_arg(request, creator->language); linphone_xml_rpc_request_add_string_arg(request, creator->algorithm); return request; } static LinphoneXmlRpcRequest * _create_account_with_email_custom(LinphoneAccountCreator *creator) { LinphoneXmlRpcRequest *request; if (!creator->username || !creator->email || !creator->password) { return NULL; } ms_debug("Account creator: create_account_with_email (username=%s, email=%s, domain=%s, algo=%s)", creator->username, creator->email, _get_domain(creator), creator->algorithm); request = linphone_xml_rpc_request_new(LinphoneXmlRpcArgString, "create_email_account"); linphone_xml_rpc_request_add_string_arg(request, creator->username); linphone_xml_rpc_request_add_string_arg(request, creator->email); linphone_xml_rpc_request_add_string_arg(request, ha1_for_passwd(creator->username ? creator->username : creator->phone_number, _get_domain(creator), creator->password, creator->algorithm)); linphone_xml_rpc_request_add_string_arg(request, linphone_core_get_user_agent(creator->core)); linphone_xml_rpc_request_add_string_arg(request, _get_domain(creator)); linphone_xml_rpc_request_add_string_arg(request, creator->algorithm); return request; } LinphoneAccountCreatorStatus linphone_account_creator_create_account_linphone(LinphoneAccountCreator *creator) { LinphoneXmlRpcRequest *request = NULL; fill_domain_and_algorithm_if_needed(creator); char *identity = _get_identity(creator); if (!identity || (!(request = _create_account_with_phone_custom(creator)) && !(request = _create_account_with_email_custom(creator)))) { if (creator->cbs->create_account_response_cb != NULL) { creator->cbs->create_account_response_cb(creator, LinphoneAccountCreatorStatusMissingArguments, "Missing required parameters"); } NOTIFY_IF_EXIST(Status, create_account, creator, LinphoneAccountCreatorStatusMissingArguments, "Missing required parameters") if (identity) ms_free(identity); return LinphoneAccountCreatorStatusMissingArguments; } if (creator->xmlrpc_session) { linphone_xml_rpc_request_set_user_data(request, creator); linphone_xml_rpc_request_cbs_set_response(linphone_xml_rpc_request_get_callbacks(request), _create_account_cb_custom); linphone_xml_rpc_session_send_request(creator->xmlrpc_session, request); linphone_xml_rpc_request_unref(request); ms_free(identity); return LinphoneAccountCreatorStatusRequestOk; } ms_free(identity); return LinphoneAccountCreatorStatusRequestFailed; } static void _delete_linphone_account_response_cb(LinphoneXmlRpcRequest *request) { LinphoneAccountCreator *creator = (LinphoneAccountCreator *)linphone_xml_rpc_request_get_user_data(request); LinphoneAccountCreatorStatus status = LinphoneAccountCreatorStatusRequestFailed; const char* resp = linphone_xml_rpc_request_get_string_response(request); if (linphone_xml_rpc_request_get_status(request) == LinphoneXmlRpcStatusOk) { status = (strcmp(resp, "OK") == 0) ? LinphoneAccountCreatorStatusAccountCreated : (strcmp(resp, "ERROR_USERNAME_PARAMETER_NOT_FOUND") == 0) ? LinphoneAccountCreatorStatusMissingArguments : (strcmp(resp, "ERROR_ACCOUNT_DOESNT_EXIST") == 0) ? LinphoneAccountCreatorStatusAccountNotExist : (strcmp(resp, "ERROR_ALGO_NOT_SUPPORTED") == 0) ? LinphoneAccountCreatorStatusAlgoNotSupported : (strstr(resp, "ERROR_") == 0) ? LinphoneAccountCreatorStatusRequestFailed : LinphoneAccountCreatorStatusAccountNotCreated; } if (creator->cbs->delete_account_response_cb != NULL) { creator->cbs->delete_account_response_cb(creator, status, resp); } NOTIFY_IF_EXIST(Status, delete_account, creator, status, resp) } LinphoneAccountCreatorStatus linphone_account_creator_delete_account_linphone(LinphoneAccountCreator *creator) { if ((!creator->username && !creator->phone_number) || !creator->password || !creator->proxy_cfg) { if (creator->cbs->delete_account_response_cb != NULL) { creator->cbs->delete_account_response_cb(creator, LinphoneAccountCreatorStatusMissingArguments, "Missing required parameters"); } NOTIFY_IF_EXIST(Status, delete_account, creator, LinphoneAccountCreatorStatusMissingArguments, "Missing required parameters") return LinphoneAccountCreatorStatusMissingArguments; } fill_domain_and_algorithm_if_needed(creator); ms_debug("Account creator: delete_account (username=%s, password=%s, domain=%s, algo=%s)", creator->username, creator->password, linphone_proxy_config_get_domain(creator->proxy_cfg), creator->algorithm); LinphoneXmlRpcRequest *request = linphone_xml_rpc_request_new(LinphoneXmlRpcArgString, "delete_account"); linphone_xml_rpc_request_add_string_arg(request, creator->username ? creator->username : creator->phone_number); linphone_xml_rpc_request_add_string_arg(request, creator->password); linphone_xml_rpc_request_add_string_arg(request, linphone_proxy_config_get_domain(creator->proxy_cfg)); linphone_xml_rpc_request_add_string_arg(request, creator->algorithm); linphone_xml_rpc_request_set_user_data(request, creator); linphone_xml_rpc_request_cbs_set_response(linphone_xml_rpc_request_get_callbacks(request), _delete_linphone_account_response_cb); linphone_xml_rpc_session_send_request(creator->xmlrpc_session, request); linphone_xml_rpc_request_unref(request); return LinphoneAccountCreatorStatusRequestOk; } /****************** END OF CREATE ACCOUNT SECTION *****************************/ /****************** START OF VALIDATE ACCOUNT SECTION *************************/ static void _login_account_confirmation_key_cb_custom(LinphoneXmlRpcRequest *request) { LinphoneAccountCreator *creator = (LinphoneAccountCreator *)linphone_xml_rpc_request_get_user_data(request); LinphoneAccountCreatorStatus status = LinphoneAccountCreatorStatusRequestFailed; if (linphone_xml_rpc_request_get_status(request) == LinphoneXmlRpcStatusFailed) { const char* resp = linphone_xml_rpc_request_get_string_response(request); if (resp != NULL) { if (strcmp(resp, "ERROR_ACCOUNT_DOESNT_EXIST") == 0) { status = LinphoneAccountCreatorStatusAccountNotExist; } else if (strcmp(resp, "ERROR_KEY_DOESNT_MATCH") == 0) { status = LinphoneAccountCreatorStatusWrongActivationCode; } else if (strcmp(resp, "ERROR_ALGO_NOT_SUPPORTED") == 0) { status = LinphoneAccountCreatorStatusAlgoNotSupported; } else { status = LinphoneAccountCreatorStatusUnexpectedError; } } } else if (linphone_xml_rpc_request_get_status(request) == LinphoneXmlRpcStatusOk) { status = LinphoneAccountCreatorStatusRequestOk; const bctbx_map_t *resp = linphone_xml_rpc_request_get_string_struct_response(request); const auto &it = bctbx_map_cchar_find_key(resp, "password"); if (!bctbx_iterator_equals(it, bctbx_map_cchar_end(resp))) { const char *ha1 = (const char *)bctbx_pair_cchar_get_second(bctbx_iterator_cchar_get_pair(it)); set_string(&creator->ha1, ha1, FALSE); } const auto &it2 = bctbx_map_cchar_find_key(resp, "algorithm"); if (!bctbx_iterator_equals(it2, bctbx_map_cchar_end(resp))) { const char *algo = (const char *)bctbx_pair_cchar_get_second(bctbx_iterator_cchar_get_pair(it2)); if (algo) { if (creator->algorithm) { if (strcmp(algo, creator->algorithm) != 0) { ms_warning("Asked for password hashed using %s, got algorithm %s", creator->algorithm, algo); } else { ms_debug("Got password hashed using %s that we requested", algo); } } else { ms_debug("Account creator wasn't configured for a specific alogithm, got %s", algo); } set_string(&creator->algorithm, algo, FALSE); } else { ms_error("Couldn't get algorithm from struct response !"); } } } const char *content = linphone_xml_rpc_request_get_content(request); if (creator->cbs->login_linphone_account_response_cb != NULL) { creator->cbs->login_linphone_account_response_cb(creator, status, content); } NOTIFY_IF_EXIST(Status, login_linphone_account, creator, status, content) } LinphoneAccountCreatorStatus linphone_account_creator_login_linphone_account_linphone(LinphoneAccountCreator *creator) { LinphoneXmlRpcRequest *request = NULL; if ((!creator->username && !creator->phone_number) || !creator->activation_code) { if (creator->cbs->login_linphone_account_response_cb != NULL) { creator->cbs->login_linphone_account_response_cb(creator, LinphoneAccountCreatorStatusMissingArguments, "Missing required parameters"); } NOTIFY_IF_EXIST(Status, login_linphone_account, creator, LinphoneAccountCreatorStatusMissingArguments, "Missing required parameters") return LinphoneAccountCreatorStatusMissingArguments; } fill_domain_and_algorithm_if_needed(creator); if (creator->xmlrpc_session) { ms_debug("Account creator: recover_account_from_confirmation_key (username=%s, activation code=%s, domain=%s, algo=%s)", creator->username ? creator->username : creator->phone_number, creator->activation_code, _get_domain(creator), creator->algorithm); request = linphone_xml_rpc_request_new(LinphoneXmlRpcArgStringStruct, "recover_account_from_confirmation_key"); linphone_xml_rpc_request_add_string_arg(request, creator->username ? creator->username : creator->phone_number); linphone_xml_rpc_request_add_string_arg(request, creator->activation_code); linphone_xml_rpc_request_add_string_arg(request, _get_domain(creator)); linphone_xml_rpc_request_add_string_arg(request, creator->algorithm); linphone_xml_rpc_request_set_user_data(request, creator); linphone_xml_rpc_request_cbs_set_response(linphone_xml_rpc_request_get_callbacks(request), _login_account_confirmation_key_cb_custom); linphone_xml_rpc_session_send_request(creator->xmlrpc_session, request); linphone_xml_rpc_request_unref(request); return LinphoneAccountCreatorStatusRequestOk; } return LinphoneAccountCreatorStatusRequestFailed; } static void _activate_account_cb_custom(LinphoneXmlRpcRequest *request) { LinphoneAccountCreator *creator = (LinphoneAccountCreator *)linphone_xml_rpc_request_get_user_data(request); LinphoneAccountCreatorStatus status = LinphoneAccountCreatorStatusRequestFailed; const char* resp = linphone_xml_rpc_request_get_string_response(request); if (linphone_xml_rpc_request_get_status(request) == LinphoneXmlRpcStatusOk) { if (strcmp(resp, "ERROR_ACCOUNT_ALREADY_ACTIVATED") == 0) { status = LinphoneAccountCreatorStatusAccountAlreadyActivated; } else if (strcmp(resp, "ERROR_KEY_DOESNT_MATCH") == 0) { status = LinphoneAccountCreatorStatusWrongActivationCode; } else if (strcmp(resp, "ERROR_ALGO_NOT_SUPPORTED") == 0) { status = LinphoneAccountCreatorStatusAlgoNotSupported; } else if (strcmp(resp, "ERROR_ACCOUNT_DOESNT_EXIST") == 0) { status = LinphoneAccountCreatorStatusAccountNotExist; } else if (strstr(resp, "ERROR_") == resp) { status = LinphoneAccountCreatorStatusAccountNotActivated; } else { status = LinphoneAccountCreatorStatusAccountActivated; set_string(&creator->ha1, resp, FALSE); } } if (creator->cbs->activate_account_response_cb != NULL) { creator->cbs->activate_account_response_cb(creator, status, resp); } NOTIFY_IF_EXIST(Status, activate_account, creator, status, resp) } LinphoneAccountCreatorStatus linphone_account_creator_activate_account_linphone(LinphoneAccountCreator *creator) { LinphoneXmlRpcRequest *request = NULL; if (!creator->phone_number || !creator->activation_code) { if (creator->cbs->is_account_activated_response_cb != NULL) { creator->cbs->is_account_activated_response_cb(creator, LinphoneAccountCreatorStatusMissingArguments, "Missing required parameters"); } NOTIFY_IF_EXIST(Status, activate_account, creator, LinphoneAccountCreatorStatusMissingArguments, "Missing required parameters") return LinphoneAccountCreatorStatusMissingArguments; } fill_domain_and_algorithm_if_needed(creator); if (creator->xmlrpc_session) { ms_debug("Account creator: activate_account_phone (phone number=%s, username=%s, activation code=%s, domain=%s, algo=%s)", creator->phone_number, creator->username ? creator->username : creator->phone_number, creator->activation_code, _get_domain(creator), creator->algorithm); request = linphone_xml_rpc_request_new(LinphoneXmlRpcArgString, "activate_phone_account"); linphone_xml_rpc_request_add_string_arg(request, creator->phone_number); linphone_xml_rpc_request_add_string_arg(request, creator->username ? creator->username : creator->phone_number); linphone_xml_rpc_request_add_string_arg(request, creator->activation_code); linphone_xml_rpc_request_add_string_arg(request, _get_domain(creator)); linphone_xml_rpc_request_add_string_arg(request, creator->algorithm); linphone_xml_rpc_request_set_user_data(request, creator); linphone_xml_rpc_request_cbs_set_response(linphone_xml_rpc_request_get_callbacks(request), _activate_account_cb_custom); linphone_xml_rpc_session_send_request(creator->xmlrpc_session, request); linphone_xml_rpc_request_unref(request); return LinphoneAccountCreatorStatusRequestOk; } return LinphoneAccountCreatorStatusRequestFailed; } LinphoneAccountCreatorStatus linphone_account_creator_activate_email_account_linphone(LinphoneAccountCreator *creator) { LinphoneXmlRpcRequest *request = NULL; if (!creator->activation_code || !creator->username) { if (creator->cbs->is_account_activated_response_cb != NULL) { creator->cbs->is_account_activated_response_cb(creator, LinphoneAccountCreatorStatusMissingArguments, "Missing required parameters"); } NOTIFY_IF_EXIST(Status, activate_account, creator, LinphoneAccountCreatorStatusMissingArguments, "Missing required parameters") return LinphoneAccountCreatorStatusMissingArguments; } fill_domain_and_algorithm_if_needed(creator); if (creator->xmlrpc_session) { ms_debug("Account creator: activate_account_email (username=%s, activation code=%s, domain=%s, algo=%s)", creator->username, creator->activation_code, _get_domain(creator), creator->algorithm); request = linphone_xml_rpc_request_new(LinphoneXmlRpcArgString, "activate_email_account"); linphone_xml_rpc_request_add_string_arg(request, creator->username); linphone_xml_rpc_request_add_string_arg(request, creator->activation_code); linphone_xml_rpc_request_add_string_arg(request, _get_domain(creator)); linphone_xml_rpc_request_add_string_arg(request, creator->algorithm); linphone_xml_rpc_request_set_user_data(request, creator); linphone_xml_rpc_request_cbs_set_response(linphone_xml_rpc_request_get_callbacks(request), _activate_account_cb_custom); linphone_xml_rpc_session_send_request(creator->xmlrpc_session, request); linphone_xml_rpc_request_unref(request); return LinphoneAccountCreatorStatusRequestOk; } return LinphoneAccountCreatorStatusRequestFailed; } static void get_linphone_confirmation_key_response_cb(LinphoneXmlRpcRequest *request) { LinphoneAccountCreator *creator = (LinphoneAccountCreator *)linphone_xml_rpc_request_get_user_data(request); LinphoneAccountCreatorStatus status = LinphoneAccountCreatorStatusRequestFailed; const char* resp = linphone_xml_rpc_request_get_string_response(request); if (linphone_xml_rpc_request_get_status(request) == LinphoneXmlRpcStatusOk) { if (strcmp(resp, "ERROR_USERNAME_PARAMETER_NOT_FOUND") == 0) { status = LinphoneAccountCreatorStatusMissingArguments; } else if (strcmp(resp, "ERROR_ACCOUNT_DOESNT_EXIST") == 0) { status = LinphoneAccountCreatorStatusAccountNotExist; } else if (strcmp(resp, "ERROR_ALGO_NOT_SUPPORTED") == 0) { status = LinphoneAccountCreatorStatusAlgoNotSupported; } else if (strstr(resp, "ERROR_") == resp) { status = LinphoneAccountCreatorStatusRequestFailed; } else { status = LinphoneAccountCreatorStatusRequestOk; set_string(&creator->activation_code, resp, FALSE); } } if (creator->cbs->confirmation_key_response_cb != NULL) { creator->cbs->confirmation_key_response_cb(creator, status, resp); } NOTIFY_IF_EXIST(Status, confirmation_key, creator, status, resp) } LinphoneAccountCreatorStatus linphone_account_creator_get_confirmation_key_linphone(LinphoneAccountCreator *creator) { if (!creator->username || !creator->password || !creator->proxy_cfg) { if (creator->cbs->confirmation_key_response_cb != NULL) { creator->cbs->confirmation_key_response_cb(creator, LinphoneAccountCreatorStatusMissingArguments, "Missing required parameters"); } NOTIFY_IF_EXIST(Status, confirmation_key, creator, LinphoneAccountCreatorStatusMissingArguments, "Missing required parameters") return LinphoneAccountCreatorStatusMissingArguments; } fill_domain_and_algorithm_if_needed(creator); ms_debug("Account creator: confirmation_key (username=%s, password=%s, domain=%s, algo=%s)", creator->username, creator->password, linphone_proxy_config_get_domain(creator->proxy_cfg), creator->algorithm); LinphoneXmlRpcRequest *request = linphone_xml_rpc_request_new(LinphoneXmlRpcArgString, "get_confirmation_key"); linphone_xml_rpc_request_add_string_arg(request, creator->username); linphone_xml_rpc_request_add_string_arg(request, ha1_for_passwd(creator->username, linphone_proxy_config_get_domain(creator->proxy_cfg), creator->password, creator->algorithm)); linphone_xml_rpc_request_add_string_arg(request, linphone_proxy_config_get_domain(creator->proxy_cfg)); linphone_xml_rpc_request_add_string_arg(request, creator->algorithm); linphone_xml_rpc_request_set_user_data(request, creator); linphone_xml_rpc_request_cbs_set_response(linphone_xml_rpc_request_get_callbacks(request), get_linphone_confirmation_key_response_cb); linphone_xml_rpc_session_send_request(creator->xmlrpc_session, request); linphone_xml_rpc_request_unref(request); return LinphoneAccountCreatorStatusRequestOk; } /****************** END OF CREATE VALIDATE ACCOUNT SECTION ********************/ /****************** START OF ACCOUNT VALIDATED SECTION ************************/ static void _is_account_activated_cb_custom(LinphoneXmlRpcRequest *request) { LinphoneAccountCreator *creator = (LinphoneAccountCreator *)linphone_xml_rpc_request_get_user_data(request); LinphoneAccountCreatorStatus status = LinphoneAccountCreatorStatusRequestFailed; const char* resp = linphone_xml_rpc_request_get_string_response(request); if (linphone_xml_rpc_request_get_status(request) == LinphoneXmlRpcStatusOk) { status = (strcmp(resp, "OK") == 0) ? LinphoneAccountCreatorStatusAccountActivated : LinphoneAccountCreatorStatusAccountNotActivated; } if (creator->cbs->is_account_activated_response_cb != NULL) { creator->cbs->is_account_activated_response_cb(creator, status, resp); } NOTIFY_IF_EXIST(Status, is_account_activated, creator, status, resp) } LinphoneAccountCreatorStatus linphone_account_creator_is_account_activated_linphone(LinphoneAccountCreator *creator) { LinphoneXmlRpcRequest *request = NULL; char *identity = _get_identity(creator); if (!identity) { if (creator->cbs->is_account_activated_response_cb != NULL) { creator->cbs->is_account_activated_response_cb(creator, LinphoneAccountCreatorStatusMissingArguments, "Missing required parameters"); } NOTIFY_IF_EXIST(Status, is_account_activated, creator, LinphoneAccountCreatorStatusMissingArguments, "Missing required parameters") return LinphoneAccountCreatorStatusMissingArguments; } fill_domain_and_algorithm_if_needed(creator); if (creator->xmlrpc_session) { ms_debug("Account creator: is_account_activated (username=%s, domain=%s, algo=%s)", creator->username ? creator->username : creator->phone_number, _get_domain(creator), creator->algorithm); request = linphone_xml_rpc_request_new(LinphoneXmlRpcArgString, "is_account_activated"); linphone_xml_rpc_request_add_string_arg(request, creator->username ? creator->username : creator->phone_number); linphone_xml_rpc_request_add_string_arg(request, _get_domain(creator)); linphone_xml_rpc_request_add_string_arg(request, creator->algorithm); linphone_xml_rpc_request_set_user_data(request, creator); linphone_xml_rpc_request_cbs_set_response(linphone_xml_rpc_request_get_callbacks(request), _is_account_activated_cb_custom); linphone_xml_rpc_session_send_request(creator->xmlrpc_session, request); linphone_xml_rpc_request_unref(request); ms_free(identity); return LinphoneAccountCreatorStatusRequestOk; } ms_free(identity); return LinphoneAccountCreatorStatusRequestFailed; } /****************** END OF CREATE ACCOUNT VALIDATED SECTION********************/ /****************** START OF PHONE NUMBER VALIDATED SECTION *******************/ static void _is_phone_number_used_cb_custom(LinphoneXmlRpcRequest *request) { LinphoneAccountCreator *creator = (LinphoneAccountCreator *)linphone_xml_rpc_request_get_user_data(request); LinphoneAccountCreatorStatus status = LinphoneAccountCreatorStatusRequestFailed; const char* resp = linphone_xml_rpc_request_get_string_response(request); if (linphone_xml_rpc_request_get_status(request) == LinphoneXmlRpcStatusOk) { status = (strcmp(resp, "OK_ACCOUNT") == 0) ? LinphoneAccountCreatorStatusAliasIsAccount : (strcmp(resp, "OK_ALIAS") == 0) ? LinphoneAccountCreatorStatusAliasExist : LinphoneAccountCreatorStatusAliasNotExist; } if (creator->cbs->is_alias_used_response_cb != NULL) { creator->cbs->is_alias_used_response_cb(creator, status, resp); } NOTIFY_IF_EXIST(Status, is_alias_used, creator, status, resp) } LinphoneAccountCreatorStatus linphone_account_creator_is_phone_number_used_linphone(LinphoneAccountCreator *creator) { LinphoneXmlRpcRequest *request = NULL; char *identity = _get_identity(creator); if (!identity) { if (creator->cbs->is_alias_used_response_cb != NULL) { creator->cbs->is_alias_used_response_cb(creator, LinphoneAccountCreatorStatusMissingArguments, "Missing required parameters"); } NOTIFY_IF_EXIST(Status, is_alias_used, creator, LinphoneAccountCreatorStatusMissingArguments, "Missing required parameters") return LinphoneAccountCreatorStatusMissingArguments; } fill_domain_and_algorithm_if_needed(creator); if (creator->xmlrpc_session) { ms_debug("Account creator: is_phone_number_used (phone number=%s, domain=%s)", creator->phone_number, _get_domain(creator)); request = linphone_xml_rpc_request_new(LinphoneXmlRpcArgString, "is_phone_number_used"); linphone_xml_rpc_request_add_string_arg(request, creator->phone_number); linphone_xml_rpc_request_add_string_arg(request, _get_domain(creator)); linphone_xml_rpc_request_set_user_data(request, creator); linphone_xml_rpc_request_cbs_set_response(linphone_xml_rpc_request_get_callbacks(request), _is_phone_number_used_cb_custom); linphone_xml_rpc_session_send_request(creator->xmlrpc_session, request); linphone_xml_rpc_request_unref(request); ms_free(identity); return LinphoneAccountCreatorStatusRequestOk; } ms_free(identity); return LinphoneAccountCreatorStatusRequestFailed; } /****************** END OF PHONE NUMBER VALIDATED SECTION *********************/ /****************** START OF LINK PHONE NUMBER WITH ACCOUNT SECTION ***********/ static void _link_phone_number_with_account_cb_custom(LinphoneXmlRpcRequest *request) { LinphoneAccountCreator *creator = (LinphoneAccountCreator *)linphone_xml_rpc_request_get_user_data(request); LinphoneAccountCreatorStatus status = LinphoneAccountCreatorStatusRequestFailed; const char* resp = linphone_xml_rpc_request_get_string_response(request); if (linphone_xml_rpc_request_get_status(request) == LinphoneXmlRpcStatusOk) { status = (strcmp(resp, "OK") == 0) ? LinphoneAccountCreatorStatusRequestOk : (strcmp(resp, "ERROR_CANNOT_SEND_SMS") == 0) ? LinphoneAccountCreatorStatusPhoneNumberInvalid : (strcmp(resp, "ERROR_MAX_SMS_EXCEEDED") == 0) ? LinphoneAccountCreatorStatusPhoneNumberOverused : LinphoneAccountCreatorStatusAccountNotLinked; } if (creator->cbs->link_account_response_cb != NULL) { creator->cbs->link_account_response_cb(creator, status, resp); } NOTIFY_IF_EXIST(Status, link_account, creator, status, resp) } LinphoneAccountCreatorStatus linphone_account_creator_link_phone_number_with_account_linphone(LinphoneAccountCreator *creator) { LinphoneXmlRpcRequest *request = NULL; if (!creator->phone_number || !creator->username) { if (creator->cbs->link_account_response_cb != NULL) { creator->cbs->link_account_response_cb(creator, LinphoneAccountCreatorStatusMissingArguments, "Missing required parameters"); } NOTIFY_IF_EXIST(Status, link_account, creator, LinphoneAccountCreatorStatusMissingArguments, "Missing required parameters") return LinphoneAccountCreatorStatusMissingArguments; } fill_domain_and_algorithm_if_needed(creator); if (creator->xmlrpc_session) { ms_debug("Account creator: link_phone_number_with_account (phone number=%s, username=%s, domain=%s, language=%s)", creator->phone_number, creator->username, _get_domain(creator), creator->language); request = linphone_xml_rpc_request_new(LinphoneXmlRpcArgString, "link_phone_number_with_account"); linphone_xml_rpc_request_add_string_arg(request, creator->phone_number); linphone_xml_rpc_request_add_string_arg(request, creator->username); linphone_xml_rpc_request_add_string_arg(request, _get_domain(creator)); linphone_xml_rpc_request_add_string_arg(request, creator->language); linphone_xml_rpc_request_set_user_data(request, creator); linphone_xml_rpc_request_cbs_set_response(linphone_xml_rpc_request_get_callbacks(request), _link_phone_number_with_account_cb_custom); linphone_xml_rpc_session_send_request(creator->xmlrpc_session, request); linphone_xml_rpc_request_unref(request); return LinphoneAccountCreatorStatusRequestOk; } return LinphoneAccountCreatorStatusRequestFailed; } static void _get_phone_number_for_account_cb_custom(LinphoneXmlRpcRequest *request) { LinphoneAccountCreator *creator = (LinphoneAccountCreator *)linphone_xml_rpc_request_get_user_data(request); LinphoneAccountCreatorStatus status = LinphoneAccountCreatorStatusRequestFailed; const char* resp = linphone_xml_rpc_request_get_string_response(request); if (linphone_xml_rpc_request_get_status(request) == LinphoneXmlRpcStatusOk) { status = (strcmp(resp, "ERROR_USERNAME_PARAMETER_NOT_FOUND") == 0 || strcmp(resp, "ERROR_ACCOUNT_DOESNT_EXIST") == 0 || strcmp(resp, "ERROR_ALIAS_DOESNT_EXIST") == 0) ? LinphoneAccountCreatorStatusAccountNotLinked : LinphoneAccountCreatorStatusAccountLinked; } if (creator->cbs->is_account_linked_response_cb != NULL) { creator->cbs->is_account_linked_response_cb(creator, status, resp); } NOTIFY_IF_EXIST(Status, is_account_linked, creator, status, resp) } LinphoneAccountCreatorStatus linphone_account_creator_is_account_linked_linphone(LinphoneAccountCreator *creator) { LinphoneXmlRpcRequest *request = NULL; if (!creator->username || !_get_domain(creator)) { if (creator->cbs->is_account_linked_response_cb != NULL) { creator->cbs->is_account_linked_response_cb(creator, LinphoneAccountCreatorStatusMissingArguments, "Missing required parameters"); } NOTIFY_IF_EXIST(Status, is_account_linked, creator, LinphoneAccountCreatorStatusMissingArguments, "Missing required parameters") return LinphoneAccountCreatorStatusMissingArguments; } fill_domain_and_algorithm_if_needed(creator); if (creator->xmlrpc_session) { ms_debug("Account creator: is_account_linked (username=%s, domain=%s)", creator->username, _get_domain(creator)); request = linphone_xml_rpc_request_new(LinphoneXmlRpcArgString, "get_phone_number_for_account"); linphone_xml_rpc_request_add_string_arg(request, creator->username); linphone_xml_rpc_request_add_string_arg(request, _get_domain(creator)); linphone_xml_rpc_request_set_user_data(request, creator); linphone_xml_rpc_request_cbs_set_response(linphone_xml_rpc_request_get_callbacks(request), _get_phone_number_for_account_cb_custom); linphone_xml_rpc_session_send_request(creator->xmlrpc_session, request); linphone_xml_rpc_request_unref(request); return LinphoneAccountCreatorStatusRequestOk; } return LinphoneAccountCreatorStatusRequestFailed; } /****************** END OF LINK PHONE NUMBER WITH ACCOUNT SECTION *************/ /****************** START OF ACTIVE PHONE NUMBER LINK **************************/ static void _activate_phone_number_link_cb_custom(LinphoneXmlRpcRequest *request) { LinphoneAccountCreator *creator = (LinphoneAccountCreator *)linphone_xml_rpc_request_get_user_data(request); LinphoneAccountCreatorStatus status = LinphoneAccountCreatorStatusRequestFailed; const char* resp = linphone_xml_rpc_request_get_string_response(request); if (linphone_xml_rpc_request_get_status(request) == LinphoneXmlRpcStatusOk) { status = (strstr(resp, "ERROR_") == resp) ? LinphoneAccountCreatorStatusAccountNotActivated : LinphoneAccountCreatorStatusAccountActivated; } if (creator->cbs->activate_alias_response_cb != NULL) { creator->cbs->activate_alias_response_cb(creator, status, resp); } NOTIFY_IF_EXIST(Status, activate_alias, creator, status, resp) } LinphoneAccountCreatorStatus linphone_account_creator_activate_phone_number_link_linphone(LinphoneAccountCreator *creator) { LinphoneXmlRpcRequest *request = NULL; if (!creator->phone_number || !creator->username || !creator->activation_code || (!creator->password && !creator->ha1) || !_get_domain(creator)) { if (creator->cbs->activate_alias_response_cb != NULL) { creator->cbs->activate_alias_response_cb(creator, LinphoneAccountCreatorStatusMissingArguments, "Missing required parameters"); } NOTIFY_IF_EXIST(Status, activate_alias, creator, LinphoneAccountCreatorStatusMissingArguments, "Missing required parameters") return LinphoneAccountCreatorStatusMissingArguments; } fill_domain_and_algorithm_if_needed(creator); if (creator->xmlrpc_session) { ms_debug("Account creator: activate_phone_number_link (phone number=%s, username=%s, activation code=%s, domain=%s, algo=%s)", creator->phone_number, creator->username, creator->activation_code, _get_domain(creator), creator->algorithm); request = linphone_xml_rpc_request_new(LinphoneXmlRpcArgString, "activate_phone_number_link"); linphone_xml_rpc_request_add_string_arg(request, creator->phone_number); linphone_xml_rpc_request_add_string_arg(request, creator->username); linphone_xml_rpc_request_add_string_arg(request, creator->activation_code); linphone_xml_rpc_request_add_string_arg(request, creator->ha1 ? creator->ha1 : ha1_for_passwd(creator->username, _get_domain(creator), creator->password, creator->algorithm)); linphone_xml_rpc_request_add_string_arg(request, _get_domain(creator)); linphone_xml_rpc_request_add_string_arg(request, creator->algorithm); linphone_xml_rpc_request_set_user_data(request, creator); linphone_xml_rpc_request_cbs_set_response(linphone_xml_rpc_request_get_callbacks(request), _activate_phone_number_link_cb_custom); linphone_xml_rpc_session_send_request(creator->xmlrpc_session, request); linphone_xml_rpc_request_unref(request); return LinphoneAccountCreatorStatusRequestOk; } return LinphoneAccountCreatorStatusRequestFailed; } /****************** END OF ACTIVE PHONE NUMBER LINK **************************/ /****************** START OF ACTIVE PHONE NUMBER LINK **************************/ static void _recover_phone_account_cb_custom(LinphoneXmlRpcRequest *request) { LinphoneAccountCreator *creator = (LinphoneAccountCreator *)linphone_xml_rpc_request_get_user_data(request); LinphoneAccountCreatorStatus status = LinphoneAccountCreatorStatusRequestFailed; const char* resp = linphone_xml_rpc_request_get_string_response(request); if (linphone_xml_rpc_request_get_status(request) == LinphoneXmlRpcStatusOk) { if (strstr(resp, "ERROR_") == resp) { status = (strcmp(resp, "ERROR_CANNOT_SEND_SMS") == 0) ? LinphoneAccountCreatorStatusPhoneNumberInvalid : (strcmp(resp, "ERROR_MAX_SMS_EXCEEDED") == 0) ? LinphoneAccountCreatorStatusPhoneNumberOverused : (strcmp(resp, "ERROR_ACCOUNT_DOESNT_EXIST") == 0) ? LinphoneAccountCreatorStatusAccountNotExist : LinphoneAccountCreatorStatusRequestFailed; } else { status = LinphoneAccountCreatorStatusRequestOk; set_string(&creator->username, resp, FALSE); } } if (creator->cbs->recover_account_response_cb != NULL) { creator->cbs->recover_account_response_cb(creator, status, resp); } NOTIFY_IF_EXIST(Status, recover_account, creator, status, resp) } LinphoneAccountCreatorStatus linphone_account_creator_recover_phone_account_linphone(LinphoneAccountCreator *creator) { LinphoneXmlRpcRequest *request = NULL; if (!creator->phone_number) { if (creator->cbs->recover_account_response_cb != NULL) { creator->cbs->recover_account_response_cb(creator, LinphoneAccountCreatorStatusMissingArguments, "Missing required parameters"); } NOTIFY_IF_EXIST(Status, recover_account, creator, LinphoneAccountCreatorStatusMissingArguments, "Missing required parameters") return LinphoneAccountCreatorStatusMissingArguments; } fill_domain_and_algorithm_if_needed(creator); if (creator->xmlrpc_session) { ms_debug("Account creator: recover_phone_account (phone number=%s, domain=%s, language=%s)", creator->phone_number, _get_domain(creator), creator->language); request = linphone_xml_rpc_request_new(LinphoneXmlRpcArgString, "recover_phone_account"); linphone_xml_rpc_request_add_string_arg(request, creator->phone_number); linphone_xml_rpc_request_add_string_arg(request, _get_domain(creator)); linphone_xml_rpc_request_add_string_arg(request, creator->language); linphone_xml_rpc_request_set_user_data(request, creator); linphone_xml_rpc_request_cbs_set_response(linphone_xml_rpc_request_get_callbacks(request), _recover_phone_account_cb_custom); linphone_xml_rpc_session_send_request(creator->xmlrpc_session, request); linphone_xml_rpc_request_unref(request); return LinphoneAccountCreatorStatusRequestOk; } return LinphoneAccountCreatorStatusRequestFailed; } /****************** END OF ACTIVE PHONE NUMBER LINK **************************/ /****************** START OF UPDATE ACCOUNT **************************/ static void _password_updated_cb_custom(LinphoneXmlRpcRequest *request) { LinphoneAccountCreator *creator = (LinphoneAccountCreator *)linphone_xml_rpc_request_get_user_data(request); LinphoneAccountCreatorStatus status = LinphoneAccountCreatorStatusRequestFailed; const char* resp = linphone_xml_rpc_request_get_string_response(request); if (linphone_xml_rpc_request_get_status(request) == LinphoneXmlRpcStatusOk) { if (strcmp(resp, "OK") == 0) { status = LinphoneAccountCreatorStatusRequestOk; } else if (strcmp(resp, "ERROR_ALGO_NOT_SUPPORTED") == 0) { status = LinphoneAccountCreatorStatusAlgoNotSupported; } else if (strcmp(resp, "ERROR_PASSWORD_DOESNT_MATCH") == 0) { status = LinphoneAccountCreatorStatusAccountNotExist; } else { status = LinphoneAccountCreatorStatusServerError; } } if (creator->cbs->update_account_response_cb != NULL) { creator->cbs->update_account_response_cb(creator, status, resp); } NOTIFY_IF_EXIST(Status, update_account, creator, status, resp) } LinphoneAccountCreatorStatus linphone_account_creator_update_password_linphone(LinphoneAccountCreator *creator){ LinphoneXmlRpcRequest *request = NULL; char *identity = _get_identity(creator); const char* new_pwd = (const char*)linphone_account_creator_get_user_data(creator); if (!identity || ((!creator->username && !creator->phone_number) || !_get_domain(creator) || (!creator->password && !creator->ha1) || !new_pwd ) ) { if (creator->cbs->update_account_response_cb != NULL) { creator->cbs->update_account_response_cb(creator, LinphoneAccountCreatorStatusMissingArguments, "Missing required parameters"); } NOTIFY_IF_EXIST(Status, update_account, creator, LinphoneAccountCreatorStatusMissingArguments, "Missing required parameters") return LinphoneAccountCreatorStatusMissingArguments; } bctbx_free(identity); fill_domain_and_algorithm_if_needed(creator); if (creator->xmlrpc_session) { const char *username = creator->username ? creator->username : creator->phone_number; char *ha1 = bctbx_strdup(creator->ha1 ? creator->ha1 : ha1_for_passwd(username, _get_domain(creator), creator->password, creator->algorithm)); char *new_ha1 = bctbx_strdup(ha1_for_passwd(username, _get_domain(creator), new_pwd, creator->algorithm)); ms_debug("Account creator: update_password (username=%s, domain=%s, algo=%s)", creator->username, _get_domain(creator), creator->algorithm); request = linphone_xml_rpc_request_new(LinphoneXmlRpcArgString, "update_hash"); linphone_xml_rpc_request_add_string_arg(request, username); linphone_xml_rpc_request_add_string_arg(request, ha1); linphone_xml_rpc_request_add_string_arg(request, new_ha1); linphone_xml_rpc_request_add_string_arg(request, _get_domain(creator)); linphone_xml_rpc_request_add_string_arg(request, creator->algorithm); linphone_xml_rpc_request_set_user_data(request, creator); linphone_xml_rpc_request_cbs_set_response(linphone_xml_rpc_request_get_callbacks(request), _password_updated_cb_custom); linphone_xml_rpc_session_send_request(creator->xmlrpc_session, request); linphone_xml_rpc_request_unref(request); bctbx_free(ha1); bctbx_free(new_ha1); return LinphoneAccountCreatorStatusRequestOk; } return LinphoneAccountCreatorStatusRequestFailed; } /****************** END OF UPDATE ACCOUNT **************************/ /************************** End Account Creator Linphone **************************/ liblinphone-4.4.21/coreapi/account_creator_private.h000066400000000000000000000251061376572216100226230ustar00rootroot00000000000000/* * Copyright (c) 2010-2019 Belledonne Communications SARL. * * This file is part of Liblinphone. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef _ACCOUNT_CREATOR_PRIVATE_H_ #define _ACCOUNT_CREATOR_PRIVATE_H_ #include "linphone/defs.h" #include #include "linphone/account_creator_service.h" #include "linphone/account_creator.h" struct _LinphoneAccountCreatorService { belle_sip_object_t base; void *user_data; LinphoneAccountCreatorRequestFunc account_creator_service_constructor_cb; /**< Constructor */ LinphoneAccountCreatorRequestFunc account_creator_service_destructor_cb; /**< Destructor */ LinphoneAccountCreatorRequestFunc create_account_request_cb; /**< Request to create account */ LinphoneAccountCreatorRequestFunc delete_account_request_cb; /**< Request to delete account */ LinphoneAccountCreatorRequestFunc is_account_exist_request_cb; /**< Request to know if account exist */ LinphoneAccountCreatorRequestFunc confirmation_key_request_cb; /**< Request to get the confirmation key */ LinphoneAccountCreatorRequestFunc activate_account_request_cb; /**< Request to activate account */ LinphoneAccountCreatorRequestFunc is_account_activated_request_cb; /**< Request to know if account is activated */ LinphoneAccountCreatorRequestFunc link_account_request_cb; /**< Request to link account with an alias */ LinphoneAccountCreatorRequestFunc activate_alias_request_cb; /**< Request to activate the link of alias */ LinphoneAccountCreatorRequestFunc is_alias_used_request_cb; /**< Request to know if alias is used */ LinphoneAccountCreatorRequestFunc is_account_linked_request_cb; /**< Request to know if account is linked with an alias */ LinphoneAccountCreatorRequestFunc recover_account_request_cb; /**< Request to recover account */ LinphoneAccountCreatorRequestFunc update_account_request_cb; /**< Request to update account */ LinphoneAccountCreatorRequestFunc login_linphone_account_request_cb; /**< Request to get password & algorithm from confirmation key */ }; BELLE_SIP_DECLARE_VPTR_NO_EXPORT(LinphoneAccountCreatorService); struct _LinphoneAccountCreatorCbs { belle_sip_object_t base; void *user_data; LinphoneAccountCreatorCbsStatusCb create_account_response_cb; /**< Response of create_account request */ LinphoneAccountCreatorCbsStatusCb delete_account_response_cb; /**< Response of delete_account request */ LinphoneAccountCreatorCbsStatusCb is_account_exist_response_cb; /**< Response of is_account_exist request */ LinphoneAccountCreatorCbsStatusCb activate_account_response_cb; /**< Response of activate_account request */ LinphoneAccountCreatorCbsStatusCb is_account_activated_response_cb; /**< Response of is_account_activated request */ LinphoneAccountCreatorCbsStatusCb confirmation_key_response_cb; /**< Response of get_confirmation_key request */ LinphoneAccountCreatorCbsStatusCb link_account_response_cb; /**< Response of link_account request */ LinphoneAccountCreatorCbsStatusCb activate_alias_response_cb; /**< Response of activation alias */ LinphoneAccountCreatorCbsStatusCb is_alias_used_response_cb; /**< Response of is_alias_used request */ LinphoneAccountCreatorCbsStatusCb is_account_linked_response_cb; /**< Response of is_account_linked request */ LinphoneAccountCreatorCbsStatusCb recover_account_response_cb; /**< Response of recover_account request */ LinphoneAccountCreatorCbsStatusCb update_account_response_cb; /**< Response of update_account request */ LinphoneAccountCreatorCbsStatusCb login_linphone_account_response_cb; /** < Response of login_linphone_account request */ }; BELLE_SIP_DECLARE_VPTR_NO_EXPORT(LinphoneAccountCreatorCbs); struct _LinphoneAccountCreator { belle_sip_object_t base; void *user_data; LinphoneCore *core; /* AccountCreator */ LinphoneAccountCreatorService *service; /**< Account creator service */ LinphoneAccountCreatorCbs *cbs; /**< Account creator cbs, deprecated, use a list of Cbs instead */ bctbx_list_t *callbacks; LinphoneAccountCreatorCbs *currentCbs; LinphoneXmlRpcSession *xmlrpc_session; /**< XML-RPC session */ LinphoneProxyConfig *proxy_cfg; /**< Default proxy config */ /* User */ char *username; /**< Username */ char *display_name; /**< Display name */ /* Password */ char *password; /**< Plain text password */ char *ha1; /**< Hash password */ /* Phone Number(Alias) */ char *phone_number; /**< User phone number*/ char *phone_country_code; /**< User phone number country code */ /* Email(Alias) */ char *email; /**< User email */ /* Misc */ char *language; /**< User language */ char *activation_code; /**< Account validation code */ char *domain; /**< Domain */ char *algorithm; /**< Digest authentication algorithm */ LinphoneTransportType transport; /**< Transport used */ bool_t set_as_default; /**< Set proxy config as the default one */ /* Deprecated */ char *route; }; BELLE_SIP_DECLARE_VPTR_NO_EXPORT(LinphoneAccountCreator); #ifdef __cplusplus extern "C" { #endif /** * Account creator custom to set Linphone default values * @param[in] creator LinphoneAccountCreator object * @return LinphoneAccountCreatorStatusRequestOk if the request has been sent, LinphoneAccountCreatorStatusRequestFailed otherwise **/ LINPHONE_PUBLIC LinphoneAccountCreatorStatus linphone_account_creator_constructor_linphone(LinphoneAccountCreator *creator); /** * Send an XML-RPC request to test the existence of a Linphone account. * @param[in] creator LinphoneAccountCreator object * @return LinphoneAccountCreatorStatusRequestOk if the request has been sent, LinphoneAccountCreatorStatusRequestFailed otherwise **/ LINPHONE_PUBLIC LinphoneAccountCreatorStatus linphone_account_creator_is_account_exist_linphone(LinphoneAccountCreator *creator); /** * Send an XML-RPC request to create a Linphone account. * @param[in] creator LinphoneAccountCreator object * @return LinphoneAccountCreatorStatusRequestOk if the request has been sent, LinphoneAccountCreatorStatusRequestFailed otherwise **/ LINPHONE_PUBLIC LinphoneAccountCreatorStatus linphone_account_creator_create_account_linphone(LinphoneAccountCreator *creator); /** * Send an XML-RPC request to activate a Linphone account with phone number. * @param[in] creator LinphoneAccountCreator object * @return LinphoneAccountCreatorStatusRequestOk if the request has been sent, LinphoneAccountCreatorStatusRequestFailed otherwise **/ LINPHONE_PUBLIC LinphoneAccountCreatorStatus linphone_account_creator_activate_account_linphone(LinphoneAccountCreator *creator); /** * Send an XML-RPC request to activate a Linphone account with email. * @param[in] creator LinphoneAccountCreator object * @return LinphoneAccountCreatorStatusRequestOk if the request has been sent, LinphoneAccountCreatorStatusRequestFailed otherwise **/ LINPHONE_PUBLIC LinphoneAccountCreatorStatus linphone_account_creator_activate_email_account_linphone(LinphoneAccountCreator *creator); /** * Send an XML-RPC request to test the validation of a Linphone account. * @param[in] creator LinphoneAccountCreator object * @return LinphoneAccountCreatorStatusRequestOk if the request has been sent, LinphoneAccountCreatorStatusRequestFailed otherwise **/ LINPHONE_PUBLIC LinphoneAccountCreatorStatus linphone_account_creator_is_account_activated_linphone(LinphoneAccountCreator *creator); /** * Send an XML-RPC request to test the existence a phone number with a Linphone account. * @param[in] creator LinphoneAccountCreator object * @return LinphoneAccountCreatorStatusRequestOk if the request has been sent, LinphoneAccountCreatorStatusRequestFailed otherwise **/ LINPHONE_PUBLIC LinphoneAccountCreatorStatus linphone_account_creator_is_phone_number_used_linphone(LinphoneAccountCreator *creator); /** * Send an XML-RPC request to link a phone number with a Linphone account. * @param[in] creator LinphoneAccountCreator object * @return LinphoneAccountCreatorStatusRequestOk if the request has been sent, LinphoneAccountCreatorStatusRequestFailed otherwise **/ LINPHONE_PUBLIC LinphoneAccountCreatorStatus linphone_account_creator_link_phone_number_with_account_linphone(LinphoneAccountCreator *creator); /** * Send an XML-RPC request to activate the link of a phone number with a Linphone account. * @param[in] creator LinphoneAccountCreator object * @return LinphoneAccountCreatorStatusRequestOk if the request has been sent, LinphoneAccountCreatorStatusRequestFailed otherwise **/ LINPHONE_PUBLIC LinphoneAccountCreatorStatus linphone_account_creator_activate_phone_number_link_linphone(LinphoneAccountCreator *creator); /** * Send an XML-RPC request to a Linphone account with the phone number. * @param[in] creator LinphoneAccountCreator object * @return LinphoneAccountCreatorStatusRequestOk if the request has been sent, LinphoneAccountCreatorStatusRequestFailed otherwise **/ LINPHONE_PUBLIC LinphoneAccountCreatorStatus linphone_account_creator_recover_phone_account_linphone(LinphoneAccountCreator *creator); /** * Send an XML-RPC request to ask if an account is linked with a phone number * @param[in] creator LinphoneAccountCreator object * @return if this account is linked with a phone number **/ LINPHONE_PUBLIC LinphoneAccountCreatorStatus linphone_account_creator_is_account_linked_linphone(LinphoneAccountCreator *creator); /** * Send an XML-RPC request to ask if an account is linked with a phone number * @param[in] creator LinphoneAccountCreator object * @param[in] new_pwd const char * : new password for the account creator * @return LinphoneAccountCreatorStatusRequestOk if everything is OK, or a specific error otherwise. **/ LINPHONE_PUBLIC LinphoneAccountCreatorStatus linphone_account_creator_update_password_linphone(LinphoneAccountCreator *creator); /** * Send an XML-RPC request to get the password & algorithm of an account using the confirmation key * @param[in] creator LinphoneAccountCreator object * @return LinphoneAccountCreatorStatusRequestOk if everything is OK, or a specific error otherwise. **/ LINPHONE_PUBLIC LinphoneAccountCreatorStatus linphone_account_creator_login_linphone_account_linphone(LinphoneAccountCreator *creator); #ifdef __cplusplus } #endif #endif // _ACCOUNT_CREATOR_PRIVATE_H_ liblinphone-4.4.21/coreapi/account_creator_service.c000066400000000000000000000161721376572216100226070ustar00rootroot00000000000000/* * Copyright (c) 2010-2019 Belledonne Communications SARL. * * This file is part of Liblinphone. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "linphone/account_creator_service.h" #include "linphone/core.h" #include "c-wrapper/c-wrapper.h" // TODO: From coreapi. Remove me later. #include "private.h" BELLE_SIP_DECLARE_NO_IMPLEMENTED_INTERFACES(LinphoneAccountCreatorService); BELLE_SIP_INSTANCIATE_VPTR(LinphoneAccountCreatorService, belle_sip_object_t, NULL, // destroy NULL, // clone NULL, // marshal FALSE ); /************************** Start Account Creator service **************************/ LinphoneAccountCreatorService * linphone_account_creator_service_new(void) { return belle_sip_object_new(LinphoneAccountCreatorService); } LinphoneAccountCreatorService * linphone_account_creator_service_ref(LinphoneAccountCreatorService *service) { belle_sip_object_ref(service); return service; } void linphone_account_creator_service_unref(LinphoneAccountCreatorService *service) { belle_sip_object_unref(service); } void *linphone_account_creator_service_get_user_data(const LinphoneAccountCreatorService *service) { return service->user_data; } void linphone_account_creator_service_set_user_data(LinphoneAccountCreatorService *service, void *ud) { service->user_data = ud; } LinphoneAccountCreatorRequestFunc linphone_account_creator_service_get_constructor_cb(const LinphoneAccountCreatorService *service) { return service->account_creator_service_constructor_cb; } void linphone_account_creator_service_set_constructor_cb(LinphoneAccountCreatorService *service, LinphoneAccountCreatorRequestFunc cb) { service->account_creator_service_constructor_cb = cb; } LinphoneAccountCreatorRequestFunc linphone_account_creator_service_get_destructor_cb(const LinphoneAccountCreatorService *service) { return service->account_creator_service_destructor_cb; } void linphone_account_creator_service_set_destructor_cb(LinphoneAccountCreatorService *service, LinphoneAccountCreatorRequestFunc cb) { service->account_creator_service_destructor_cb = cb; } LinphoneAccountCreatorRequestFunc linphone_account_creator_service_get_create_account_cb(const LinphoneAccountCreatorService *service) { return service->create_account_request_cb; } void linphone_account_creator_service_set_create_account_cb(LinphoneAccountCreatorService *service, LinphoneAccountCreatorRequestFunc cb) { service->create_account_request_cb = cb; } LinphoneAccountCreatorRequestFunc linphone_account_creator_service_get_is_account_exist_cb(const LinphoneAccountCreatorService *service) { return service->is_account_exist_request_cb; } void linphone_account_creator_service_set_is_account_exist_cb(LinphoneAccountCreatorService *service, LinphoneAccountCreatorRequestFunc cb) { service->is_account_exist_request_cb = cb; } LinphoneAccountCreatorRequestFunc linphone_account_creator_service_get_activate_account_cb(const LinphoneAccountCreatorService *service) { return service->activate_account_request_cb; } void linphone_account_creator_service_set_activate_account_cb(LinphoneAccountCreatorService *service, LinphoneAccountCreatorRequestFunc cb) { service->activate_account_request_cb = cb; } LinphoneAccountCreatorRequestFunc linphone_account_creator_service_get_is_account_activated_cb(const LinphoneAccountCreatorService *service) { return service->is_account_activated_request_cb; } void linphone_account_creator_service_set_is_account_activated_cb(LinphoneAccountCreatorService *service, LinphoneAccountCreatorRequestFunc cb) { service->is_account_activated_request_cb = cb; } LinphoneAccountCreatorRequestFunc linphone_account_creator_service_get_link_account_cb(const LinphoneAccountCreatorService *service) { return service->link_account_request_cb; } void linphone_account_creator_service_set_link_account_cb(LinphoneAccountCreatorService *service, LinphoneAccountCreatorRequestFunc cb) { service->link_account_request_cb = cb; } LinphoneAccountCreatorRequestFunc linphone_account_creator_service_get_activate_alias_cb(const LinphoneAccountCreatorService *service) { return service->activate_alias_request_cb; } void linphone_account_creator_service_set_activate_alias_cb(LinphoneAccountCreatorService *service, LinphoneAccountCreatorRequestFunc cb) { service->activate_alias_request_cb = cb; } LinphoneAccountCreatorRequestFunc linphone_account_creator_service_get_is_alias_used_cb(const LinphoneAccountCreatorService *service) { return service->is_alias_used_request_cb; } void linphone_account_creator_service_set_is_alias_used_cb(LinphoneAccountCreatorService *service, LinphoneAccountCreatorRequestFunc cb) { service->is_alias_used_request_cb = cb; } LinphoneAccountCreatorRequestFunc linphone_account_creator_service_get_is_account_linked_cb(const LinphoneAccountCreatorService *service) { return service->is_account_linked_request_cb; } void linphone_account_creator_service_set_is_account_linked_cb(LinphoneAccountCreatorService *service, LinphoneAccountCreatorRequestFunc cb) { service->is_account_linked_request_cb = cb; } LinphoneAccountCreatorRequestFunc linphone_account_creator_service_get_recover_account_cb(const LinphoneAccountCreatorService *service) { return service->is_account_linked_request_cb; } void linphone_account_creator_service_set_recover_account_cb(LinphoneAccountCreatorService *service, LinphoneAccountCreatorRequestFunc cb) { service->recover_account_request_cb = cb; } LinphoneAccountCreatorRequestFunc linphone_account_creator_service_get_update_account_cb(const LinphoneAccountCreatorService *service) { return service->update_account_request_cb; } void linphone_account_creator_service_set_update_account_cb(LinphoneAccountCreatorService *service, LinphoneAccountCreatorRequestFunc cb) { service->update_account_request_cb = cb; } LinphoneAccountCreatorRequestFunc linphone_account_creator_service_get_login_linphone_account_cb(const LinphoneAccountCreatorService *service) { return service->login_linphone_account_request_cb; } void linphone_account_creator_service_set_login_linphone_account_cb(LinphoneAccountCreatorService *service, LinphoneAccountCreatorRequestFunc cb) { service->login_linphone_account_request_cb = cb; } /************************** End Account Creator service **************************/ void linphone_core_set_account_creator_service(LinphoneCore *lc, LinphoneAccountCreatorService *service) { if (lc->default_ac_service) linphone_account_creator_service_unref(lc->default_ac_service); lc->default_ac_service = service; } LinphoneAccountCreatorService * linphone_core_get_account_creator_service(LinphoneCore *lc) { return lc->default_ac_service; } liblinphone-4.4.21/coreapi/authentication.c000066400000000000000000000277561376572216100207450ustar00rootroot00000000000000/* * Copyright (c) 2010-2019 Belledonne Communications SARL. * * This file is part of Liblinphone. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ /* * 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 Library 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "linphone/core.h" #include "linphone/lpconfig.h" #include "sal/sal.h" #include "linphone/api/c-auth-info.h" #include "c-wrapper/c-wrapper.h" #include "auth-info/auth-info.h" // TODO: From coreapi. Remove me later. #include "private.h" /** * Destroys a LinphoneAuthInfo object. **/ void linphone_auth_info_destroy(LinphoneAuthInfo *obj){ belle_sip_object_unref(obj); } static char * remove_quotes(char * input){ char *tmp; if (*input=='"') input++; tmp=strchr(input,'"'); if (tmp) *tmp='\0'; return input; } static bool_t realm_match(const char *realm1, const char *realm2){ if (realm1==NULL && realm2==NULL) return TRUE; if (realm1!=NULL && realm2!=NULL){ if (strcmp(realm1,realm2)==0) return TRUE; else{ char tmp1[128]; char tmp2[128]; char *p1,*p2; strncpy(tmp1,realm1,sizeof(tmp1)-1); strncpy(tmp2,realm2,sizeof(tmp2)-1); p1=remove_quotes(tmp1); p2=remove_quotes(tmp2); return strcmp(p1,p2)==0; } } return FALSE; } /* Check if the LinphoneAuthInfo candidate is compatible with the requested algorithm. */ static bool_t check_algorithm_compatibility(const LinphoneAuthInfo *ai, const char *algorithm){ const char *ai_algorithm = linphone_auth_info_get_algorithm(ai); if (algorithm == NULL) return TRUE; if (linphone_auth_info_get_password(ai) != NULL){ /* We have the clear text password, so if the user didn't requested a specific algorithm, we can satisfy all algorithms.*/ if (ai_algorithm == NULL) return TRUE; }else{ /* If we don't have the clear text password but the ha1, and if algorithm is empty in LinphoneAuthInfo * for backward compatibility, we assume it is MD5. */ if (ai_algorithm == NULL && strcasecmp(algorithm, "MD5") == 0) return TRUE; } /* In all other cases, algorithm must match. */ if (ai_algorithm && strcasecmp(algorithm, ai_algorithm) == 0) return TRUE; /* algorithm do match */ return FALSE; } static const LinphoneAuthInfo *find_auth_info(LinphoneCore *lc, const char *username, const char *realm, const char *domain, const char *algorithm, bool_t ignore_realm){ bctbx_list_t *elem; const LinphoneAuthInfo *ret=NULL; for (elem=lc->auth_info;elem!=NULL;elem=elem->next) { LinphoneAuthInfo *pinfo = (LinphoneAuthInfo*)elem->data; if (username && linphone_auth_info_get_username(pinfo) && strcmp(username, linphone_auth_info_get_username(pinfo))==0) { if (!check_algorithm_compatibility(pinfo, algorithm)) { continue; } if (realm && domain){ if (linphone_auth_info_get_realm(pinfo) && realm_match(realm, linphone_auth_info_get_realm(pinfo)) && linphone_auth_info_get_domain(pinfo) && strcmp(domain, linphone_auth_info_get_domain(pinfo))==0) { return pinfo; } } else if (realm) { if (linphone_auth_info_get_realm(pinfo) && realm_match(realm, linphone_auth_info_get_realm(pinfo))) { if (ret!=NULL) { ms_warning("Non unique realm found for %s",username); return NULL; } ret=pinfo; } } else if (domain && linphone_auth_info_get_domain(pinfo) && strcmp(domain,linphone_auth_info_get_domain(pinfo))==0 && (linphone_auth_info_get_ha1(pinfo)==NULL || ignore_realm)) { return pinfo; } else if (!domain && (linphone_auth_info_get_ha1(pinfo)==NULL || ignore_realm)) { return pinfo; } } } return ret; } const LinphoneAuthInfo *_linphone_core_find_tls_auth_info(LinphoneCore *lc) { bctbx_list_t *elem; for (elem=lc->auth_info;elem!=NULL;elem=elem->next) { LinphoneAuthInfo *pinfo = (LinphoneAuthInfo*)elem->data; if (linphone_auth_info_get_tls_cert(pinfo) && linphone_auth_info_get_tls_key(pinfo)) { return pinfo; } else if (linphone_auth_info_get_tls_cert_path(pinfo) && linphone_auth_info_get_tls_key_path(pinfo)) { return pinfo; } } return NULL; } const LinphoneAuthInfo *_linphone_core_find_auth_info(LinphoneCore *lc, const char *realm, const char *username, const char *domain, const char *algorithm, bool_t ignore_realm){ const LinphoneAuthInfo *ai=NULL; if (realm){ ai=find_auth_info(lc,username,realm,NULL, algorithm, FALSE); if (ai==NULL && domain){ ai=find_auth_info(lc,username,realm,domain, algorithm, FALSE); } } if (ai == NULL && domain != NULL) { ai=find_auth_info(lc,username,NULL,domain, algorithm, ignore_realm); } if (ai==NULL){ ai=find_auth_info(lc,username,NULL,NULL, algorithm, ignore_realm); } if (ai) ms_message("linphone_core_find_auth_info(): returning auth info username=%s, realm=%s", linphone_auth_info_get_username(ai) ? linphone_auth_info_get_username(ai) : "", linphone_auth_info_get_realm(ai) ? linphone_auth_info_get_realm(ai) : ""); return ai; } const LinphoneAuthInfo *linphone_core_find_auth_info(LinphoneCore *lc, const char *realm, const char *username, const char *domain){ return _linphone_core_find_auth_info(lc, realm, username, domain, NULL, TRUE); } /*the auth info is expected to be in the core's list*/ void linphone_core_write_auth_info(LinphoneCore *lc, LinphoneAuthInfo *ai){ int i; bctbx_list_t *elem = lc->auth_info; if (!lc->sip_conf.save_auth_info) return; for (i=0; elem != NULL; elem = elem->next, i++){ if (ai == elem->data){ linphone_auth_info_write_config(lc->config, ai, i); } } } static void write_auth_infos(LinphoneCore *lc){ bctbx_list_t *elem; int i; if (!linphone_core_ready(lc)) return; if (!lc->sip_conf.save_auth_info) return; for(elem=lc->auth_info,i=0;elem!=NULL;elem=bctbx_list_next(elem),i++){ LinphoneAuthInfo *ai=(LinphoneAuthInfo*)(elem->data); linphone_auth_info_write_config(lc->config,ai,i); } linphone_auth_info_write_config(lc->config,NULL,i); /* mark the end */ } LinphoneAuthInfo *linphone_core_create_auth_info(LinphoneCore *lc, const char *username, const char *userid, const char *passwd, const char *ha1, const char *realm, const char *domain) { return linphone_auth_info_new(username, userid, passwd, ha1, realm, domain); } void linphone_core_add_auth_info(LinphoneCore *lc, const LinphoneAuthInfo *info){ LinphoneAuthInfo *ai=NULL; int restarted_op_count=0; bool_t updating=FALSE; if (!linphone_auth_info_get_tls_key(info) && !linphone_auth_info_get_tls_key_path(info) && !linphone_auth_info_get_ha1(info) && !linphone_auth_info_get_password(info) ){ ms_error("linphone_core_add_auth_info(): info supplied with empty password, ha1 or TLS client/key"); return; } /* find if we are attempting to modify an existing auth info */ ai=(LinphoneAuthInfo*)linphone_core_find_auth_info(lc,linphone_auth_info_get_realm(info),linphone_auth_info_get_username(info),linphone_auth_info_get_domain(info)); if (ai!=NULL && linphone_auth_info_get_domain(ai) && linphone_auth_info_get_domain(info) && strcmp(linphone_auth_info_get_domain(ai), linphone_auth_info_get_domain(info))==0){ lc->auth_info=bctbx_list_remove(lc->auth_info,ai); linphone_auth_info_unref(ai); updating=TRUE; } lc->auth_info=bctbx_list_append(lc->auth_info,linphone_auth_info_clone(info)); /* retry pending authentication operations */ auto pendingAuths = lc->sal->getPendingAuths(); for (const auto &op : pendingAuths) { LinphoneAuthInfo *ai; const SalAuthInfo *req_sai=op->getAuthRequested(); ai=(LinphoneAuthInfo*)_linphone_core_find_auth_info(lc, req_sai->realm, req_sai->username, req_sai->domain, req_sai->algorithm, FALSE); if (ai){ SalAuthInfo sai; bctbx_list_t* proxy; sai.username = (char *) linphone_auth_info_get_username(ai); sai.userid = (char *)linphone_auth_info_get_userid(ai); sai.realm = (char *) linphone_auth_info_get_realm(ai); sai.password = (char *) linphone_auth_info_get_passwd(ai); sai.ha1 = (char *)linphone_auth_info_get_ha1(ai); sai.algorithm = (char *)linphone_auth_info_get_algorithm(ai); if (linphone_auth_info_get_tls_cert(ai) && linphone_auth_info_get_tls_key(ai)) { sal_certificates_chain_parse(&sai, linphone_auth_info_get_tls_cert(ai), SAL_CERTIFICATE_RAW_FORMAT_PEM); sal_signing_key_parse(&sai, linphone_auth_info_get_tls_key(ai), ""); } else if (linphone_auth_info_get_tls_cert_path(ai) && linphone_auth_info_get_tls_key_path(ai)) { sal_certificates_chain_parse_file(&sai, linphone_auth_info_get_tls_cert_path(ai), SAL_CERTIFICATE_RAW_FORMAT_PEM); sal_signing_key_parse_file(&sai, linphone_auth_info_get_tls_key_path(ai), ""); } /*proxy case*/ for (proxy=(bctbx_list_t*)linphone_core_get_proxy_config_list(lc);proxy!=NULL;proxy=proxy->next) { if (proxy->data == op->getUserPointer()) { linphone_proxy_config_set_state((LinphoneProxyConfig*)(proxy->data),LinphoneRegistrationProgress,"Authentication..."); break; } } op->authenticate(&sai); restarted_op_count++; } } if (!pendingAuths.empty()) { ms_message("linphone_core_add_auth_info(): restarted [%i] operation(s) after %s auth info for\n" "\tusername: [%s]\n" "\trealm [%s]\n" "\tdomain [%s]\n", restarted_op_count, updating ? "updating" : "adding", linphone_auth_info_get_username(info) ? linphone_auth_info_get_username(info) : "", linphone_auth_info_get_realm(info) ? linphone_auth_info_get_realm(info) : "", linphone_auth_info_get_domain(info) ? linphone_auth_info_get_domain(info) : ""); } write_auth_infos(lc); } void linphone_core_abort_authentication(LinphoneCore *lc, LinphoneAuthInfo *info){ } void linphone_core_remove_auth_info(LinphoneCore *lc, const LinphoneAuthInfo *info){ LinphoneAuthInfo *r; r=(LinphoneAuthInfo*)linphone_core_find_auth_info(lc, linphone_auth_info_get_realm(info), linphone_auth_info_get_username(info), linphone_auth_info_get_domain(info)); if (r){ lc->auth_info=bctbx_list_remove(lc->auth_info,r); linphone_auth_info_unref(r); write_auth_infos(lc); } } const bctbx_list_t *linphone_core_get_auth_info_list(const LinphoneCore *lc){ return lc->auth_info; } void linphone_core_clear_all_auth_info(LinphoneCore *lc){ bctbx_list_t *elem; int i; for(i=0,elem=lc->auth_info;elem!=NULL;elem=bctbx_list_next(elem),i++){ LinphoneAuthInfo *info=(LinphoneAuthInfo*)elem->data; linphone_auth_info_unref(info); linphone_auth_info_write_config(lc->config,NULL,i); } bctbx_list_free(lc->auth_info); lc->auth_info=NULL; } void linphone_auth_info_fill_belle_sip_event(const LinphoneAuthInfo *auth_info, belle_sip_auth_event *event) { if (auth_info) { const char *auth_username = linphone_auth_info_get_username(auth_info); const char *auth_password = linphone_auth_info_get_password(auth_info); const char *auth_ha1 = linphone_auth_info_get_ha1(auth_info); const char *auth_algo = linphone_auth_info_get_algorithm(auth_info); belle_sip_auth_event_set_username(event, auth_username); belle_sip_auth_event_set_passwd(event, auth_password); belle_sip_auth_event_set_ha1(event, auth_ha1); belle_sip_auth_event_set_algorithm(event, auth_algo); } } liblinphone-4.4.21/coreapi/bellesip_sal/000077500000000000000000000000001376572216100201775ustar00rootroot00000000000000liblinphone-4.4.21/coreapi/bellesip_sal/sal_address_impl.c000066400000000000000000000236671376572216100236660ustar00rootroot00000000000000/* * Copyright (c) 2010-2019 Belledonne Communications SARL. * * This file is part of Liblinphone. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "sal_impl.h" /**/ /* Address manipulation API*/ SalAddress * sal_address_new(const char *uri){ belle_sip_header_address_t* result; if (uri) { result=belle_sip_header_address_fast_parse (uri); /*may return NULL*/ } else { result = belle_sip_header_address_new(); belle_sip_header_address_set_uri(result,belle_sip_uri_new()); } if (result) belle_sip_object_ref(result); return (SalAddress *)result; } SalAddress * sal_address_clone(const SalAddress *addr){ return (SalAddress *) belle_sip_object_ref(belle_sip_header_address_clone(BELLE_SIP_HEADER_ADDRESS(addr))); } const char *sal_address_get_scheme(const SalAddress *addr){ belle_sip_header_address_t* header_addr = BELLE_SIP_HEADER_ADDRESS(addr); belle_sip_uri_t* uri = belle_sip_header_address_get_uri(header_addr); belle_generic_uri_t* generic_uri = belle_sip_header_address_get_absolute_uri(header_addr); if (uri) { if (belle_sip_uri_is_secure(uri)) return "sips"; else return "sip"; } else if (generic_uri) return belle_generic_uri_get_scheme(generic_uri); else return NULL; } void sal_address_set_secure(SalAddress *addr, bool_t enabled){ belle_sip_header_address_t* header_addr = BELLE_SIP_HEADER_ADDRESS(addr); belle_sip_uri_t* uri = belle_sip_header_address_get_uri(header_addr); if (uri) belle_sip_uri_set_secure(uri,enabled); } bool_t sal_address_is_secure(const SalAddress *addr){ belle_sip_header_address_t* header_addr = BELLE_SIP_HEADER_ADDRESS(addr); belle_sip_uri_t* uri = belle_sip_header_address_get_uri(header_addr); if (uri) return !!belle_sip_uri_is_secure(uri); return FALSE; } const char *sal_address_get_display_name(const SalAddress* addr){ belle_sip_header_address_t* header_addr = BELLE_SIP_HEADER_ADDRESS(addr); return belle_sip_header_address_get_displayname(header_addr); } const char *sal_address_get_display_name_unquoted(const SalAddress *addr){ return sal_address_get_display_name(addr); } #define SAL_ADDRESS_GET(addr,param) \ {belle_sip_header_address_t* header_addr = BELLE_SIP_HEADER_ADDRESS(addr);\ belle_sip_uri_t* uri = belle_sip_header_address_get_uri(header_addr);\ if (uri) {\ return belle_sip_uri_get_##param(uri);\ } else\ return NULL;} #define SAL_ADDRESS_SET(addr,param,value) {\ belle_sip_header_address_t* header_addr = BELLE_SIP_HEADER_ADDRESS(addr);\ belle_sip_uri_t* uri = belle_sip_header_address_get_uri(header_addr);\ belle_sip_uri_set_##param(uri,value);} const char *sal_address_get_username(const SalAddress *addr){ SAL_ADDRESS_GET(addr,user) } const char *sal_address_get_domain(const SalAddress *addr){ SAL_ADDRESS_GET(addr,host) } int sal_address_get_port(const SalAddress *addr){ belle_sip_header_address_t* header_addr = BELLE_SIP_HEADER_ADDRESS(addr); belle_sip_uri_t* uri = belle_sip_header_address_get_uri(header_addr); if (uri) { return belle_sip_uri_get_port(uri); } else return -1; } SalTransport sal_address_get_transport(const SalAddress* addr){ const char *transport=sal_address_get_transport_name(addr); if (transport) return sal_transport_parse(transport); else return SalTransportUDP; }; const char* sal_address_get_transport_name(const SalAddress* addr){ belle_sip_header_address_t* header_addr = BELLE_SIP_HEADER_ADDRESS(addr); belle_sip_uri_t* uri = belle_sip_header_address_get_uri(header_addr); if (uri) { return belle_sip_uri_get_transport_param(uri); } return NULL; } const char *sal_address_get_method_param(const SalAddress *addr) { belle_sip_header_address_t* header_addr = BELLE_SIP_HEADER_ADDRESS(addr); belle_sip_uri_t* uri = belle_sip_header_address_get_uri(header_addr); if (uri) { return belle_sip_uri_get_method_param(uri); } return NULL; } void sal_address_set_display_name(SalAddress *addr, const char *display_name){ belle_sip_header_address_t* header_addr = BELLE_SIP_HEADER_ADDRESS(addr); belle_sip_header_address_set_displayname(header_addr,display_name); } void sal_address_set_username(SalAddress *addr, const char *username){ SAL_ADDRESS_SET(addr,user,username); } void sal_address_set_password(SalAddress *addr, const char *passwd){ SAL_ADDRESS_SET(addr,user_password,passwd); } const char* sal_address_get_password(const SalAddress *addr){ SAL_ADDRESS_GET(addr,user_password); } void sal_address_set_domain(SalAddress *addr, const char *host){ SAL_ADDRESS_SET(addr,host,host); } void sal_address_set_port(SalAddress *addr, int port){ SAL_ADDRESS_SET(addr,port,port); } void sal_address_clean(SalAddress *addr){ belle_sip_header_address_t* header_addr = BELLE_SIP_HEADER_ADDRESS(addr); belle_sip_uri_t* uri=belle_sip_header_address_get_uri(header_addr); if (uri) { belle_sip_parameters_clean(BELLE_SIP_PARAMETERS(uri)); belle_sip_uri_headers_clean(uri); } belle_sip_parameters_clean(BELLE_SIP_PARAMETERS(header_addr)); return ; } char *sal_address_as_string(const SalAddress *addr){ char tmp[1024]={0}; size_t off=0; belle_sip_object_marshal((belle_sip_object_t*)addr,tmp,sizeof(tmp),&off); return ms_strdup(tmp); } bool_t sal_address_is_sip(const SalAddress *addr){ belle_sip_header_address_t* header_addr = BELLE_SIP_HEADER_ADDRESS(addr); return belle_sip_header_address_get_uri(header_addr) != NULL; } char *sal_address_as_string_uri_only(const SalAddress *addr){ belle_sip_header_address_t* header_addr = BELLE_SIP_HEADER_ADDRESS(addr); belle_sip_uri_t* sip_uri = belle_sip_header_address_get_uri(header_addr); belle_generic_uri_t* absolute_uri = belle_sip_header_address_get_absolute_uri(header_addr); char tmp[1024]={0}; size_t off=0; belle_sip_object_t* uri; if (sip_uri) { uri=(belle_sip_object_t*)sip_uri; } else if (absolute_uri) { uri=(belle_sip_object_t*)absolute_uri; } else { ms_error("Cannot generate string for addr [%p] with null uri",addr); return NULL; } belle_sip_object_marshal(uri,tmp,sizeof(tmp),&off); return ms_strdup(tmp); } void sal_address_set_param(SalAddress *addr,const char* name,const char* value){ belle_sip_parameters_t* parameters = BELLE_SIP_PARAMETERS(addr); belle_sip_parameters_set_parameter(parameters,name,value); return ; } bool_t sal_address_has_param(const SalAddress *addr, const char *name){ belle_sip_parameters_t* parameters = BELLE_SIP_PARAMETERS(addr); return !!belle_sip_parameters_has_parameter(parameters, name); } const char * sal_address_get_param(const SalAddress *addr, const char *name) { belle_sip_parameters_t* parameters = BELLE_SIP_PARAMETERS(addr); return belle_sip_parameters_get_parameter(parameters, name); } void sal_address_set_params(SalAddress *addr, const char *params){ belle_sip_parameters_t* parameters = BELLE_SIP_PARAMETERS(addr); belle_sip_parameters_set(parameters,params); } void sal_address_set_uri_param(SalAddress *addr, const char *name, const char *value) { belle_sip_parameters_t* parameters = BELLE_SIP_PARAMETERS(belle_sip_header_address_get_uri(BELLE_SIP_HEADER_ADDRESS(addr))); belle_sip_parameters_set_parameter(parameters, name, value); } void sal_address_set_uri_params(SalAddress *addr, const char *params){ belle_sip_parameters_t* parameters = BELLE_SIP_PARAMETERS(belle_sip_header_address_get_uri(BELLE_SIP_HEADER_ADDRESS(addr))); belle_sip_parameters_set(parameters,params); } bool_t sal_address_has_uri_param(const SalAddress *addr, const char *name){ belle_sip_parameters_t* parameters = BELLE_SIP_PARAMETERS(belle_sip_header_address_get_uri(BELLE_SIP_HEADER_ADDRESS(addr))); return !!belle_sip_parameters_has_parameter(parameters, name); } const char * sal_address_get_uri_param(const SalAddress *addr, const char *name) { belle_sip_parameters_t* parameters = BELLE_SIP_PARAMETERS(belle_sip_header_address_get_uri(BELLE_SIP_HEADER_ADDRESS(addr))); return belle_sip_parameters_get_parameter(parameters, name); } void sal_address_remove_uri_param(const SalAddress *addr, const char *name) { belle_sip_parameters_t* parameters = BELLE_SIP_PARAMETERS(belle_sip_header_address_get_uri(BELLE_SIP_HEADER_ADDRESS(addr))); belle_sip_parameters_remove_parameter(parameters, name); } void sal_address_set_header(SalAddress *addr, const char *header_name, const char *header_value){ belle_sip_uri_set_header(belle_sip_header_address_get_uri(BELLE_SIP_HEADER_ADDRESS(addr)),header_name, header_value); } const char* sal_address_get_header(const SalAddress *addr, const char *name){ return belle_sip_uri_get_header(belle_sip_header_address_get_uri(BELLE_SIP_HEADER_ADDRESS(addr)),name); } void sal_address_set_transport(SalAddress* addr,SalTransport transport){ if (!sal_address_is_secure(addr)){ SAL_ADDRESS_SET(addr,transport_param,sal_transport_to_string(transport)); } } void sal_address_set_transport_name(SalAddress* addr,const char *transport){ SAL_ADDRESS_SET(addr,transport_param,transport); } void sal_address_set_method_param(SalAddress *addr, const char *method) { SAL_ADDRESS_SET(addr, method_param, method); } SalAddress *sal_address_ref(SalAddress *addr){ return (SalAddress*)belle_sip_object_ref(BELLE_SIP_HEADER_ADDRESS(addr)); } void sal_address_unref(SalAddress *addr){ belle_sip_object_unref(BELLE_SIP_HEADER_ADDRESS(addr)); } bool_t sal_address_is_ipv6(const SalAddress *addr){ belle_sip_header_address_t* header_addr = BELLE_SIP_HEADER_ADDRESS(addr); belle_sip_uri_t* uri = belle_sip_header_address_get_uri(header_addr); if (uri){ const char *host=belle_sip_uri_get_host(uri); if (host && strchr(host,':')!=NULL) return TRUE; } return FALSE; } liblinphone-4.4.21/coreapi/bellesip_sal/sal_impl.c000066400000000000000000000417261376572216100221550ustar00rootroot00000000000000/* * Copyright (c) 2010-2019 Belledonne Communications SARL. * * This file is part of Liblinphone. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "sal_impl.h" #ifdef HAVE_CONFIG_H #include "config.h" #endif SalAuthInfo* sal_auth_info_create(belle_sip_auth_event_t* event) { SalAuthInfo* auth_info = sal_auth_info_new(); auth_info->realm = ms_strdup(belle_sip_auth_event_get_realm(event)); auth_info->username = ms_strdup(belle_sip_auth_event_get_username(event)); auth_info->domain = ms_strdup(belle_sip_auth_event_get_domain(event)); auth_info->mode = (SalAuthMode)belle_sip_auth_event_get_mode(event); auth_info->algorithm = ms_strdup(belle_sip_auth_event_get_algorithm(event)); return auth_info; } SalAuthMode sal_auth_info_get_mode(const SalAuthInfo* auth_info) { return auth_info->mode; } belle_sip_signing_key_t *sal_auth_info_get_signing_key(const SalAuthInfo* auth_info) { return auth_info->key; } belle_sip_certificates_chain_t *sal_auth_info_get_certificates_chain(const SalAuthInfo* auth_info) { return auth_info->certificates; } void sal_auth_info_set_mode(SalAuthInfo* auth_info, SalAuthMode mode) { auth_info->mode = mode; } void sal_certificates_chain_delete(belle_sip_certificates_chain_t *chain) { belle_sip_object_unref((belle_sip_object_t *)chain); } void sal_signing_key_delete(belle_sip_signing_key_t *key) { belle_sip_object_unref((belle_sip_object_t *)key); } int sal_auth_compute_ha1(const char* userid,const char* realm,const char* password, char ha1[33]) { return belle_sip_auth_helper_compute_ha1(userid, realm, password, ha1); } int sal_auth_compute_ha1_for_algorithm( const char *userid, const char *realm, const char *password, char *ha1, size_t size, const char *algo ) { return belle_sip_auth_helper_compute_ha1_for_algorithm(userid, realm, password, ha1, size, algo); } SalCustomHeader *sal_custom_header_ref(SalCustomHeader *ch){ if (ch == NULL) return NULL; belle_sip_object_ref(ch); return ch; } void sal_custom_header_unref(SalCustomHeader *ch){ if (ch == NULL) return; belle_sip_object_unref(ch); } SalCustomHeader *sal_custom_header_append(SalCustomHeader *ch, const char *name, const char *value){ belle_sip_message_t *msg=(belle_sip_message_t*)ch; belle_sip_header_t *h; if (msg==NULL){ msg=(belle_sip_message_t*)belle_sip_request_new(); belle_sip_object_ref(msg); } h=belle_sip_header_create(name,value); if (h==NULL){ belle_sip_error("Fail to parse custom header."); return (SalCustomHeader*)msg; } belle_sip_message_add_header(msg,h); return (SalCustomHeader*)msg; } const char *sal_custom_header_find(const SalCustomHeader *ch, const char *name){ if (ch){ belle_sip_header_t *h=belle_sip_message_get_header((belle_sip_message_t*)ch,name); if (h){ return belle_sip_header_get_unparsed_value(h); } } return NULL; } SalCustomHeader *sal_custom_header_remove(SalCustomHeader *ch, const char *name) { belle_sip_message_t *msg=(belle_sip_message_t*)ch; if (msg==NULL) return NULL; belle_sip_message_remove_header(msg, name); return (SalCustomHeader*)msg; } void sal_custom_header_free(SalCustomHeader *ch){ if (ch==NULL) return; belle_sip_object_unref((belle_sip_message_t*)ch); } SalCustomHeader *sal_custom_header_clone(const SalCustomHeader *ch){ if (ch==NULL) return NULL; return (SalCustomHeader*)belle_sip_object_ref((belle_sip_message_t*)ch); } SalCustomSdpAttribute * sal_custom_sdp_attribute_append(SalCustomSdpAttribute *csa, const char *name, const char *value) { belle_sdp_session_description_t *desc = (belle_sdp_session_description_t *)csa; belle_sdp_attribute_t *attr; if (desc == NULL) { desc = (belle_sdp_session_description_t *)belle_sdp_session_description_new(); belle_sip_object_ref(desc); } attr = BELLE_SDP_ATTRIBUTE(belle_sdp_raw_attribute_create(name, value)); if (attr == NULL) { belle_sip_error("Fail to create custom SDP attribute."); return (SalCustomSdpAttribute*)desc; } belle_sdp_session_description_add_attribute(desc, attr); return (SalCustomSdpAttribute *)desc; } const char * sal_custom_sdp_attribute_find(const SalCustomSdpAttribute *csa, const char *name) { if (csa) { return belle_sdp_session_description_get_attribute_value((belle_sdp_session_description_t *)csa, name); } return NULL; } void sal_custom_sdp_attribute_free(SalCustomSdpAttribute *csa) { if (csa == NULL) return; belle_sip_object_unref((belle_sdp_session_description_t *)csa); } SalCustomSdpAttribute * sal_custom_sdp_attribute_clone(const SalCustomSdpAttribute *csa) { if (csa == NULL) return NULL; return (SalCustomSdpAttribute *)belle_sip_object_ref((belle_sdp_session_description_t *)csa); } /** Parse a file containing either a certificate chain order in PEM format or a single DER cert * @param auth_info structure where to store the result of parsing * @param path path to certificate chain file * @param format either PEM or DER */ void sal_certificates_chain_parse_file(SalAuthInfo* auth_info, const char* path, SalCertificateRawFormat format) { auth_info->certificates = (belle_sip_certificates_chain_t*) belle_sip_certificates_chain_parse_file(path, (belle_sip_certificate_raw_format_t)format); if (auth_info->certificates) belle_sip_object_ref((belle_sip_object_t *) auth_info->certificates); } /** * Parse a file containing either a private or public rsa key * @param auth_info structure where to store the result of parsing * @param passwd password (optionnal) */ void sal_signing_key_parse_file(SalAuthInfo* auth_info, const char* path, const char *passwd) { auth_info->key = (belle_sip_signing_key_t *) belle_sip_signing_key_parse_file(path, passwd); if (auth_info->key) belle_sip_object_ref((belle_sip_object_t *) auth_info->key); } /** Parse a buffer containing either a certificate chain order in PEM format or a single DER cert * @param auth_info structure where to store the result of parsing * @param buffer the buffer to parse * @param format either PEM or DER */ void sal_certificates_chain_parse(SalAuthInfo* auth_info, const char* buffer, SalCertificateRawFormat format) { size_t len = buffer != NULL ? strlen(buffer) : 0; auth_info->certificates = (belle_sip_certificates_chain_t*) belle_sip_certificates_chain_parse(buffer, len, (belle_sip_certificate_raw_format_t)format); if (auth_info->certificates) belle_sip_object_ref((belle_sip_object_t *) auth_info->certificates); } /** * Parse a buffer containing either a private or public rsa key * @param auth_info structure where to store the result of parsing * @param passwd password (optionnal) */ void sal_signing_key_parse(SalAuthInfo* auth_info, const char* buffer, const char *passwd) { size_t len = buffer != NULL ? strlen(buffer) : 0; auth_info->key = (belle_sip_signing_key_t *) belle_sip_signing_key_parse(buffer, len, passwd); if (auth_info->key) belle_sip_object_ref((belle_sip_object_t *) auth_info->key); } /** * Parse a directory to get a certificate with the given subject common name * */ void sal_certificates_chain_parse_directory(char **certificate_pem, char **key_pem, char **fingerprint, const char* path, const char *subject, SalCertificateRawFormat format, bool_t generate_certificate, bool_t generate_dtls_fingerprint) { belle_sip_certificates_chain_t *certificate = NULL; belle_sip_signing_key_t *key = NULL; *certificate_pem = NULL; *key_pem = NULL; if (belle_sip_get_certificate_and_pkey_in_dir(path, subject, &certificate, &key, (belle_sip_certificate_raw_format_t)format) == 0) { *certificate_pem = belle_sip_certificates_chain_get_pem(certificate); *key_pem = belle_sip_signing_key_get_pem(key); ms_message("Retrieve certificate with CN=%s successful\n", subject); } else { if (generate_certificate == TRUE) { if ( belle_sip_generate_self_signed_certificate(path, subject, &certificate, &key) == 0) { *certificate_pem = belle_sip_certificates_chain_get_pem(certificate); *key_pem = belle_sip_signing_key_get_pem(key); ms_message("Generate self-signed certificate with CN=%s successful\n", subject); }else{ ms_error("Self-signed certificate generation failed."); return; } } } /* generate the fingerprint as described in RFC4572 if needed */ if ((generate_dtls_fingerprint == TRUE) && (fingerprint != NULL)) { if (*fingerprint != NULL) { ms_free(*fingerprint); } *fingerprint = belle_sip_certificates_chain_get_fingerprint(certificate); } /* free key and certificate */ if ( certificate != NULL ) { belle_sip_object_unref(certificate); } if ( key != NULL ) { belle_sip_object_unref(key); } } unsigned char * sal_get_random_bytes(unsigned char *ret, size_t size){ return belle_sip_random_bytes(ret,size); } char *sal_get_random_token(int size){ return belle_sip_random_token(reinterpret_cast(ms_malloc((size_t)size)),(size_t)size); } unsigned int sal_get_random(void){ unsigned int ret=0; belle_sip_random_bytes((unsigned char*)&ret,4); return ret; } unsigned long sal_begin_background_task(const char *name, void (*max_time_reached)(void *), void *data){ return belle_sip_begin_background_task(name, max_time_reached, data); } void sal_end_background_task(unsigned long id){ belle_sip_end_background_task(id); } static belle_sip_header_t * sal_body_handler_find_header(const SalBodyHandler *body_handler, const char *header_name) { belle_sip_body_handler_t *bsbh = BELLE_SIP_BODY_HANDLER(body_handler); const belle_sip_list_t *l = belle_sip_body_handler_get_headers(bsbh); for (; l != NULL; l = l->next) { belle_sip_header_t *header = BELLE_SIP_HEADER(l->data); if (strcmp(belle_sip_header_get_name(header), header_name) == 0) { return header; } } return NULL; } SalBodyHandler * sal_body_handler_new(void) { belle_sip_memory_body_handler_t *body_handler = belle_sip_memory_body_handler_new(NULL, NULL); return (SalBodyHandler *)BELLE_SIP_BODY_HANDLER(body_handler); } SalBodyHandler * sal_body_handler_ref(SalBodyHandler *body_handler) { return (SalBodyHandler *)belle_sip_object_ref(BELLE_SIP_OBJECT(body_handler)); } void sal_body_handler_unref(SalBodyHandler *body_handler) { belle_sip_object_unref(BELLE_SIP_OBJECT(body_handler)); } const char * sal_body_handler_get_type(const SalBodyHandler *body_handler) { belle_sip_header_content_type_t *content_type = BELLE_SIP_HEADER_CONTENT_TYPE(sal_body_handler_find_header(body_handler, "Content-Type")); if (content_type != NULL) { return belle_sip_header_content_type_get_type(content_type); } return NULL; } void sal_body_handler_set_type(SalBodyHandler *body_handler, const char *type) { belle_sip_header_content_type_t *content_type = BELLE_SIP_HEADER_CONTENT_TYPE(sal_body_handler_find_header(body_handler, "Content-Type")); if (content_type == NULL) { content_type = belle_sip_header_content_type_new(); belle_sip_body_handler_add_header(BELLE_SIP_BODY_HANDLER(body_handler), BELLE_SIP_HEADER(content_type)); } belle_sip_header_content_type_set_type(content_type, type); } const char * sal_body_handler_get_subtype(const SalBodyHandler *body_handler) { belle_sip_header_content_type_t *content_type = BELLE_SIP_HEADER_CONTENT_TYPE(sal_body_handler_find_header(body_handler, "Content-Type")); if (content_type != NULL) { return belle_sip_header_content_type_get_subtype(content_type); } return NULL; } void sal_body_handler_set_subtype(SalBodyHandler *body_handler, const char *subtype) { belle_sip_header_content_type_t *content_type = BELLE_SIP_HEADER_CONTENT_TYPE(sal_body_handler_find_header(body_handler, "Content-Type")); if (content_type == NULL) { content_type = belle_sip_header_content_type_new(); belle_sip_body_handler_add_header(BELLE_SIP_BODY_HANDLER(body_handler), BELLE_SIP_HEADER(content_type)); } belle_sip_header_content_type_set_subtype(content_type, subtype); } const belle_sip_list_t * sal_body_handler_get_content_type_parameters_names(const SalBodyHandler *body_handler) { belle_sip_header_content_type_t *content_type = BELLE_SIP_HEADER_CONTENT_TYPE(sal_body_handler_find_header(body_handler, "Content-Type")); if (content_type != NULL) { return belle_sip_parameters_get_parameter_names(BELLE_SIP_PARAMETERS(content_type)); } return NULL; } const char * sal_body_handler_get_content_type_parameter(const SalBodyHandler *body_handler, const char *name) { belle_sip_header_content_type_t *content_type = BELLE_SIP_HEADER_CONTENT_TYPE(sal_body_handler_find_header(body_handler, "Content-Type")); if (content_type != NULL) { return belle_sip_parameters_get_parameter(BELLE_SIP_PARAMETERS(content_type), name); } return NULL; } void sal_body_handler_set_content_type_parameter(SalBodyHandler *body_handler, const char *paramName, const char *paramValue) { belle_sip_header_content_type_t *content_type = BELLE_SIP_HEADER_CONTENT_TYPE(sal_body_handler_find_header(body_handler, "Content-Type")); if (content_type != NULL) { belle_sip_parameters_set_parameter(BELLE_SIP_PARAMETERS(content_type), paramName, paramValue); } } const char * sal_body_handler_get_encoding(const SalBodyHandler *body_handler) { belle_sip_header_t *content_encoding = sal_body_handler_find_header(body_handler, "Content-Encoding"); if (content_encoding != NULL) { return belle_sip_header_get_unparsed_value(content_encoding); } return NULL; } void sal_body_handler_set_encoding(SalBodyHandler *body_handler, const char *encoding) { belle_sip_header_t *content_encoding = sal_body_handler_find_header(body_handler, "Content-Encoding"); if (content_encoding != NULL) { belle_sip_body_handler_remove_header_from_ptr(BELLE_SIP_BODY_HANDLER(body_handler), content_encoding); } belle_sip_body_handler_add_header(BELLE_SIP_BODY_HANDLER(body_handler), belle_sip_header_create("Content-Encoding", encoding)); } void * sal_body_handler_get_data(const SalBodyHandler *body_handler) { return belle_sip_memory_body_handler_get_buffer(BELLE_SIP_MEMORY_BODY_HANDLER(body_handler)); } void sal_body_handler_set_data(SalBodyHandler *body_handler, void *data) { belle_sip_memory_body_handler_set_buffer(BELLE_SIP_MEMORY_BODY_HANDLER(body_handler), data); } size_t sal_body_handler_get_size(const SalBodyHandler *body_handler) { return belle_sip_body_handler_get_size(BELLE_SIP_BODY_HANDLER(body_handler)); } void sal_body_handler_set_size(SalBodyHandler *body_handler, size_t size) { belle_sip_header_content_length_t *content_length = BELLE_SIP_HEADER_CONTENT_LENGTH(sal_body_handler_find_header(body_handler, "Content-Length")); if (content_length == NULL) { content_length = belle_sip_header_content_length_new(); belle_sip_body_handler_add_header(BELLE_SIP_BODY_HANDLER(body_handler), BELLE_SIP_HEADER(content_length)); } belle_sip_header_content_length_set_content_length(content_length, size); belle_sip_body_handler_set_size(BELLE_SIP_BODY_HANDLER(body_handler), size); } bool_t sal_body_handler_is_multipart(const SalBodyHandler *body_handler) { if (BELLE_SIP_IS_INSTANCE_OF(body_handler, belle_sip_multipart_body_handler_t)) return TRUE; return FALSE; } SalBodyHandler * sal_body_handler_get_part(const SalBodyHandler *body_handler, int idx) { const belle_sip_list_t *l = belle_sip_multipart_body_handler_get_parts(BELLE_SIP_MULTIPART_BODY_HANDLER(body_handler)); return (SalBodyHandler *)belle_sip_list_nth_data(l, idx); } const belle_sip_list_t * sal_body_handler_get_parts(const SalBodyHandler *body_handler) { if (!sal_body_handler_is_multipart(body_handler)) return NULL; return belle_sip_multipart_body_handler_get_parts(BELLE_SIP_MULTIPART_BODY_HANDLER(body_handler)); } SalBodyHandler * sal_body_handler_find_part_by_header(const SalBodyHandler *body_handler, const char *header_name, const char *header_value) { const belle_sip_list_t *l = belle_sip_multipart_body_handler_get_parts(BELLE_SIP_MULTIPART_BODY_HANDLER(body_handler)); for (; l != NULL; l = l->next) { belle_sip_body_handler_t *bsbh = BELLE_SIP_BODY_HANDLER(l->data); const belle_sip_list_t *headers = belle_sip_body_handler_get_headers(bsbh); for (; headers != NULL; headers = headers->next) { belle_sip_header_t *header = BELLE_SIP_HEADER(headers->data); if ((strcmp(belle_sip_header_get_name(header), header_name) == 0) && (strcmp(belle_sip_header_get_unparsed_value(header), header_value) == 0)) { return (SalBodyHandler *)bsbh; } } } return NULL; } const char * sal_body_handler_get_header(const SalBodyHandler *body_handler, const char *header_name) { belle_sip_header_t *header = sal_body_handler_find_header(body_handler, header_name); if (header != NULL) { return belle_sip_header_get_unparsed_value(header); } return NULL; } const belle_sip_list_t* sal_body_handler_get_headers(const SalBodyHandler *body_handler) { return belle_sip_body_handler_get_headers(BELLE_SIP_BODY_HANDLER(body_handler)); } liblinphone-4.4.21/coreapi/bellesip_sal/sal_impl.h000066400000000000000000000026171376572216100221560ustar00rootroot00000000000000/* * Copyright (c) 2010-2019 Belledonne Communications SARL. * * This file is part of Liblinphone. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef SAL_IMPL_H_ #define SAL_IMPL_H_ #include "c-wrapper/internal/c-sal.h" #include "belle-sip/belle-sip.h" #include "belle-sip/belle-sdp.h" belle_sdp_session_description_t * media_description_to_sdp(const SalMediaDescription *desc); int sdp_to_media_description(belle_sdp_session_description_t *sdp, SalMediaDescription *desc); bool_t _sal_compute_sal_errors(belle_sip_response_t* response, SalReason* sal_reason, char* reason, size_t reason_size); SalReason _sal_reason_from_sip_code(int code); /*create SalAuthInfo by copying username and realm from suth event*/ SalAuthInfo* sal_auth_info_create(belle_sip_auth_event_t* event) ; #endif /* SAL_IMPL_H_ */ liblinphone-4.4.21/coreapi/bellesip_sal/sal_op_impl.c000066400000000000000000000064531376572216100226510ustar00rootroot00000000000000/* * Copyright (c) 2010-2019 Belledonne Communications SARL. * * This file is part of Liblinphone. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "sal_impl.h" #include "sal/sal.h" SalReason _sal_reason_from_sip_code(int code) { if (code>=100 && code<300) return SalReasonNone; switch(code) { case 0: return SalReasonIOError; case 301: return SalReasonMovedPermanently; case 302: return SalReasonRedirect; case 401: case 407: return SalReasonUnauthorized; case 403: return SalReasonForbidden; case 404: return SalReasonNotFound; case 408: return SalReasonRequestTimeout; case 410: return SalReasonGone; case 415: return SalReasonUnsupportedContent; case 422: return SalReasonSessionIntervalTooSmall; case 480: return SalReasonTemporarilyUnavailable; case 481: return SalReasonNoMatch; case 484: return SalReasonAddressIncomplete; case 486: return SalReasonBusy; case 487: return SalReasonNone; case 488: return SalReasonNotAcceptable; case 491: return SalReasonRequestPending; case 500: return SalReasonInternalError; case 501: return SalReasonNotImplemented; case 502: return SalReasonBadGateway; case 503: return SalReasonServiceUnavailable; case 504: return SalReasonServerTimeout; case 600: return SalReasonDoNotDisturb; case 603: return SalReasonDeclined; default: return SalReasonUnknown; } return SalReasonUnknown; } const SalErrorInfo *sal_error_info_none(void){ static const SalErrorInfo none = { SalReasonNone, (char *)"Ok", 200, NULL, NULL }; return &none; } void sal_error_info_reset(SalErrorInfo *ei){ if (ei->status_string){ ms_free(ei->status_string); ei->status_string=NULL; } if (ei->warnings){ ms_free(ei->warnings); ei->warnings=NULL; } if (ei->full_string){ ms_free(ei->full_string); ei->full_string=NULL; } if (ei->protocol){ ms_free(ei->protocol); ei->protocol = NULL; } ei->protocol_code=0; ei->reason=SalReasonNone; ei->sub_sei = NULL; } void sal_error_info_set(SalErrorInfo *ei, SalReason reason, const char *protocol, int code, const char *status_string, const char *warning){ sal_error_info_reset(ei); if (reason==SalReasonUnknown && strcmp(protocol, "SIP") == 0 && code != 0) ei->reason=_sal_reason_from_sip_code(code); else{ ei->reason=reason; if (code == 0) { code = LinphonePrivate::toSipCode(reason); } } ei->protocol_code=code; ei->status_string=status_string ? ms_strdup(status_string) : NULL; ei->warnings=warning ? ms_strdup(warning) : NULL; ei->protocol = protocol ? ms_strdup(protocol) : NULL; if (ei->status_string){ if (ei->warnings) ei->full_string=ms_strdup_printf("%s %s",ei->status_string,ei->warnings); else ei->full_string=ms_strdup(ei->status_string); } } liblinphone-4.4.21/coreapi/bellesip_sal/sal_sdp.c000066400000000000000000001311231376572216100217710ustar00rootroot00000000000000 /* * Copyright (c) 2010-2019 Belledonne Communications SARL. * * This file is part of Liblinphone. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "sal_impl.h" #define keywordcmp(key,b) strncmp(key,b,sizeof(key)) inline OrtpRtcpXrStatSummaryFlag operator|=(OrtpRtcpXrStatSummaryFlag a, OrtpRtcpXrStatSummaryFlag b) { int ia = static_cast(a); int ib = static_cast(b); return static_cast(ia |= ib); } static void add_ice_candidates(belle_sdp_media_description_t *md, const SalStreamDescription *desc){ char buffer[1024]; const SalIceCandidate *candidate; int nb; int i; for (i = 0; i < SAL_MEDIA_DESCRIPTION_MAX_ICE_CANDIDATES; i++) { candidate = &desc->ice_candidates[i]; if ((candidate->addr[0] == '\0') || (candidate->port == 0)) break; nb = snprintf(buffer, sizeof(buffer), "%s %u UDP %u %s %d typ %s", candidate->foundation, candidate->componentID, candidate->priority, candidate->addr, candidate->port, candidate->type); if (nb < 0) { ms_error("Cannot add ICE candidate attribute!"); return; } if (candidate->raddr[0] != '\0') { nb = snprintf(buffer + nb, sizeof(buffer) - (size_t)nb, " raddr %s rport %d", candidate->raddr, candidate->rport); if (nb < 0) { ms_error("Cannot add ICE candidate attribute!"); return; } } belle_sdp_media_description_add_attribute(md,belle_sdp_attribute_create("candidate",buffer)); } } static void add_ice_remote_candidates(belle_sdp_media_description_t *md, const SalStreamDescription *desc){ char buffer[1024]; char *ptr = buffer; const SalIceRemoteCandidate *candidate; int offset = 0; int i; buffer[0] = '\0'; for (i = 0; i < SAL_MEDIA_DESCRIPTION_MAX_ICE_REMOTE_CANDIDATES; i++) { candidate = &desc->ice_remote_candidates[i]; if ((candidate->addr[0] != '\0') && (candidate->port != 0)) { offset = snprintf(ptr, (size_t)(buffer + sizeof(buffer) - ptr), "%s%d %s %d", (i > 0) ? " " : "", i + 1, candidate->addr, candidate->port); if (offset < 0) { ms_error("Cannot add ICE remote-candidates attribute!"); return; } ptr += offset; } } if (buffer[0] != '\0') belle_sdp_media_description_add_attribute(md,belle_sdp_attribute_create("remote-candidates",buffer)); } static bool_t is_rtcp_fb_trr_int_the_same_for_all_payloads(const SalStreamDescription *stream, uint16_t *trr_int) { bctbx_list_t *pt_it; bool_t first = TRUE; for (pt_it = stream->payloads; pt_it != NULL; pt_it = pt_it->next) { PayloadType *pt = (PayloadType *)pt_it->data; if (payload_type_get_flags(pt) & PAYLOAD_TYPE_RTCP_FEEDBACK_ENABLED) { if (first == TRUE) { *trr_int = payload_type_get_avpf_params(pt).trr_interval; first = FALSE; } else if (payload_type_get_avpf_params(pt).trr_interval != *trr_int) { return FALSE; } } } return TRUE; } static void add_rtcp_fb_trr_int_attribute(belle_sdp_media_description_t *media_desc, int8_t id, uint16_t trr_int) { belle_sdp_rtcp_fb_attribute_t *attribute = belle_sdp_rtcp_fb_attribute_new(); belle_sdp_rtcp_fb_attribute_set_id(attribute, id); belle_sdp_rtcp_fb_attribute_set_type(attribute, BELLE_SDP_RTCP_FB_TRR_INT); belle_sdp_rtcp_fb_attribute_set_trr_int(attribute, trr_int); belle_sdp_media_description_add_attribute(media_desc, BELLE_SDP_ATTRIBUTE(attribute)); } static void add_rtcp_fb_ack_attribute(belle_sdp_media_description_t *media_desc, int8_t id, belle_sdp_rtcp_fb_val_param_t param) { belle_sdp_rtcp_fb_attribute_t *attribute = belle_sdp_rtcp_fb_attribute_new(); belle_sdp_rtcp_fb_attribute_set_id(attribute, id); belle_sdp_rtcp_fb_attribute_set_type(attribute, BELLE_SDP_RTCP_FB_ACK); belle_sdp_rtcp_fb_attribute_set_param(attribute, param); belle_sdp_media_description_add_attribute(media_desc, BELLE_SDP_ATTRIBUTE(attribute)); } static void add_rtcp_fb_nack_attribute(belle_sdp_media_description_t *media_desc, int8_t id, belle_sdp_rtcp_fb_val_param_t param) { belle_sdp_rtcp_fb_attribute_t *attribute = belle_sdp_rtcp_fb_attribute_new(); belle_sdp_rtcp_fb_attribute_set_id(attribute, id); belle_sdp_rtcp_fb_attribute_set_type(attribute, BELLE_SDP_RTCP_FB_NACK); belle_sdp_rtcp_fb_attribute_set_param(attribute, param); belle_sdp_media_description_add_attribute(media_desc, BELLE_SDP_ATTRIBUTE(attribute)); } static void add_rtcp_fb_ccm_attribute(belle_sdp_media_description_t *media_desc, int8_t id, belle_sdp_rtcp_fb_val_param_t param) { belle_sdp_rtcp_fb_attribute_t *attribute = belle_sdp_rtcp_fb_attribute_new(); belle_sdp_rtcp_fb_attribute_set_id(attribute, id); belle_sdp_rtcp_fb_attribute_set_type(attribute, BELLE_SDP_RTCP_FB_CCM); belle_sdp_rtcp_fb_attribute_set_param(attribute, param); belle_sdp_media_description_add_attribute(media_desc, BELLE_SDP_ATTRIBUTE(attribute)); } static void add_rtcp_fb_attributes(belle_sdp_media_description_t *media_desc, const SalMediaDescription *md, const SalStreamDescription *stream) { bctbx_list_t *pt_it; PayloadType *pt; PayloadTypeAvpfParams avpf_params; bool_t general_trr_int; uint16_t trr_int = 0; general_trr_int = is_rtcp_fb_trr_int_the_same_for_all_payloads(stream, &trr_int); if (general_trr_int == TRUE && trr_int != 0) { add_rtcp_fb_trr_int_attribute(media_desc, -1, trr_int); } if (stream->rtcp_fb.generic_nack_enabled == TRUE) { add_rtcp_fb_nack_attribute(media_desc, -1, BELLE_SDP_RTCP_FB_NONE); } if (stream->rtcp_fb.tmmbr_enabled == TRUE) { add_rtcp_fb_ccm_attribute(media_desc, -1, BELLE_SDP_RTCP_FB_TMMBR); } for (pt_it = stream->payloads; pt_it != NULL; pt_it = pt_it->next) { pt = (PayloadType *)pt_it->data; /* AVPF/SAVPF profile is used so enable AVPF for all payload types. */ payload_type_set_flag(pt, PAYLOAD_TYPE_RTCP_FEEDBACK_ENABLED); avpf_params = payload_type_get_avpf_params(pt); /* Add trr-int if not set generally. */ if (general_trr_int != TRUE && trr_int != 0) { add_rtcp_fb_trr_int_attribute(media_desc, (int8_t)payload_type_get_number(pt), avpf_params.trr_interval); } /* Add rtcp-fb attributes according to the AVPF features of the payload types. */ if (avpf_params.features & PAYLOAD_TYPE_AVPF_PLI) { add_rtcp_fb_nack_attribute(media_desc, (int8_t)payload_type_get_number(pt), BELLE_SDP_RTCP_FB_PLI); } if (avpf_params.features & PAYLOAD_TYPE_AVPF_SLI) { add_rtcp_fb_nack_attribute(media_desc, (int8_t)payload_type_get_number(pt), BELLE_SDP_RTCP_FB_SLI); } if (avpf_params.features & PAYLOAD_TYPE_AVPF_RPSI) { if (avpf_params.rpsi_compatibility == TRUE) { add_rtcp_fb_nack_attribute(media_desc, (int8_t)payload_type_get_number(pt), BELLE_SDP_RTCP_FB_RPSI); } else { add_rtcp_fb_ack_attribute(media_desc, (int8_t)payload_type_get_number(pt), BELLE_SDP_RTCP_FB_RPSI); } } if (avpf_params.features & PAYLOAD_TYPE_AVPF_FIR) { add_rtcp_fb_ccm_attribute(media_desc, (int8_t)payload_type_get_number(pt), BELLE_SDP_RTCP_FB_FIR); } } } static belle_sdp_attribute_t * create_rtcp_xr_attribute(const OrtpRtcpXrConfiguration *config) { belle_sdp_rtcp_xr_attribute_t *attribute = belle_sdp_rtcp_xr_attribute_new(); if (config->rcvr_rtt_mode != OrtpRtcpXrRcvrRttNone) { if (config->rcvr_rtt_mode == OrtpRtcpXrRcvrRttAll) belle_sdp_rtcp_xr_attribute_set_rcvr_rtt_mode(attribute, "all"); else if (config->rcvr_rtt_mode == OrtpRtcpXrRcvrRttSender) belle_sdp_rtcp_xr_attribute_set_rcvr_rtt_mode(attribute, "sender"); belle_sdp_rtcp_xr_attribute_set_rcvr_rtt_max_size(attribute, config->rcvr_rtt_max_size); } belle_sdp_rtcp_xr_attribute_set_stat_summary(attribute, (config->stat_summary_enabled == TRUE)); if (config->stat_summary_enabled == TRUE) { if (config->stat_summary_flags & OrtpRtcpXrStatSummaryLoss) belle_sdp_rtcp_xr_attribute_add_stat_summary_flag(attribute, "loss"); if (config->stat_summary_flags & OrtpRtcpXrStatSummaryDup) belle_sdp_rtcp_xr_attribute_add_stat_summary_flag(attribute, "dup"); if (config->stat_summary_flags & OrtpRtcpXrStatSummaryJitt) belle_sdp_rtcp_xr_attribute_add_stat_summary_flag(attribute, "jitt"); if (config->stat_summary_flags & OrtpRtcpXrStatSummaryTTL) belle_sdp_rtcp_xr_attribute_add_stat_summary_flag(attribute, "TTL"); if (config->stat_summary_flags & OrtpRtcpXrStatSummaryHL) belle_sdp_rtcp_xr_attribute_add_stat_summary_flag(attribute, "HL"); } belle_sdp_rtcp_xr_attribute_set_voip_metrics(attribute, (config->voip_metrics_enabled == TRUE)); return BELLE_SDP_ATTRIBUTE(attribute); } static void add_mid_attributes(belle_sdp_media_description_t *media_desc, const SalStreamDescription *stream){ if (stream->mid[0] != '\0'){ belle_sdp_media_description_add_attribute(media_desc, belle_sdp_attribute_create("mid", stream->mid)); } if (stream->mid_rtp_ext_header_id){ char *value = bctbx_strdup_printf("%i urn:ietf:params:rtp-hdrext:sdes:mid", stream->mid_rtp_ext_header_id); belle_sdp_media_description_add_attribute(media_desc, belle_sdp_attribute_create("extmap", value)); bctbx_free(value); } if (stream->bundle_only){ belle_sdp_media_description_add_attribute(media_desc, belle_sdp_attribute_create("bundle-only", NULL)); } } static void stream_description_to_sdp ( belle_sdp_session_description_t *session_desc, const SalMediaDescription *md, const SalStreamDescription *stream ) { belle_sdp_mime_parameter_t* mime_param; belle_sdp_media_description_t* media_desc; int j; bctbx_list_t* pt_it; PayloadType* pt; char buffer[1024]; const char* dir=NULL; const char *rtp_addr; const char *rtcp_addr; int rtp_port; int rtcp_port; bool_t different_rtp_and_rtcp_addr; bool_t stream_enabled = sal_stream_description_enabled(stream); rtp_addr=stream->rtp_addr; rtcp_addr=stream->rtcp_addr; rtp_port=stream->rtp_port; rtcp_port=stream->rtcp_port; media_desc = belle_sdp_media_description_create ( sal_stream_description_get_type_as_string(stream) ,stream->rtp_port ,1 ,sal_media_proto_to_string ( stream->proto ) ,NULL ); if (stream->payloads) { for ( pt_it=stream->payloads; pt_it!=NULL; pt_it=pt_it->next ) { pt= ( PayloadType* ) pt_it->data; mime_param= belle_sdp_mime_parameter_create ( pt->mime_type , payload_type_get_number ( pt ) , pt->clock_rate , pt->channels>0 ? pt->channels : -1 ); belle_sdp_mime_parameter_set_parameters ( mime_param,pt->recv_fmtp ); if ( stream->ptime>0 ) { belle_sdp_mime_parameter_set_ptime ( mime_param,stream->ptime ); } belle_sdp_media_description_append_values_from_mime_parameter ( media_desc,mime_param ); belle_sip_object_unref ( mime_param ); } } else { /* to comply with SDP we cannot have an empty payload type number list */ /* as it happens only when mline is declined with a zero port, it does not matter to put whatever codec*/ belle_sip_list_t* format = belle_sip_list_append(NULL,0); belle_sdp_media_set_media_formats(belle_sdp_media_description_get_media(media_desc),format); } /*only add a c= line within the stream description if address are differents*/ if (rtp_addr[0]!='\0' && strcmp(rtp_addr,md->addr)!=0){ bool_t inet6; belle_sdp_connection_t *connection; if (strchr(rtp_addr,':')!=NULL){ inet6=TRUE; }else inet6=FALSE; connection = belle_sdp_connection_create("IN", inet6 ? "IP6" : "IP4", rtp_addr); if (ms_is_multicast(rtp_addr)) { /*remove session cline in case of multicast*/ belle_sdp_session_description_set_connection(session_desc,NULL); if (inet6 == FALSE) belle_sdp_connection_set_ttl(connection,stream->ttl); } belle_sdp_media_description_set_connection(media_desc,connection); } if ( stream->bandwidth>0 ) belle_sdp_media_description_set_bandwidth ( media_desc,"AS",stream->bandwidth ); if (sal_stream_description_has_srtp(stream)) { /* add crypto lines */ for ( j=0; jcrypto[j].algo,&desc)==0){ if (desc.params) snprintf ( buffer, sizeof ( buffer )-1, "%d %s inline:%s %s", stream->crypto[j].tag, desc.name, stream->crypto[j].master_key,desc.params); else snprintf ( buffer, sizeof ( buffer )-1, "%d %s inline:%s", stream->crypto[j].tag, desc.name, stream->crypto[j].master_key ); belle_sdp_media_description_add_attribute( media_desc,belle_sdp_attribute_create ("crypto", buffer)); }else break; } } /* insert DTLS session attribute if needed */ if ((stream->proto == SalProtoUdpTlsRtpSavpf) || (stream->proto == SalProtoUdpTlsRtpSavp)) { char* ssrc_attribute = ms_strdup_printf("%u cname:%s",stream->rtp_ssrc,stream->rtcp_cname); if ((stream->dtls_role != SalDtlsRoleInvalid) && (strlen(stream->dtls_fingerprint)>0)) { switch(stream->dtls_role) { case SalDtlsRoleIsClient: belle_sdp_media_description_add_attribute(media_desc, belle_sdp_attribute_create("setup","active")); break; case SalDtlsRoleIsServer: belle_sdp_media_description_add_attribute(media_desc, belle_sdp_attribute_create("setup","passive")); break; case SalDtlsRoleUnset: default: belle_sdp_media_description_add_attribute(media_desc, belle_sdp_attribute_create("setup","actpass")); break; } belle_sdp_media_description_add_attribute(media_desc, belle_sdp_attribute_create("fingerprint",stream->dtls_fingerprint)); } belle_sdp_media_description_add_attribute(media_desc, belle_sdp_attribute_create("ssrc",ssrc_attribute)); ms_free(ssrc_attribute); } /* insert zrtp-hash attribute if needed */ if (stream->haveZrtpHash == 1) { belle_sdp_media_description_add_attribute(media_desc, belle_sdp_attribute_create("zrtp-hash", (const char *)(stream->zrtphash))); } switch ( stream->dir ) { case SalStreamSendRecv: /*dir="sendrecv";*/ dir=NULL; break; case SalStreamRecvOnly: dir="recvonly"; break; case SalStreamSendOnly: dir="sendonly"; break; case SalStreamInactive: dir="inactive"; break; } if ( dir ) belle_sdp_media_description_add_attribute ( media_desc,belle_sdp_attribute_create ( dir,NULL ) ); if (stream->rtcp_mux){ belle_sdp_media_description_add_attribute(media_desc, belle_sdp_attribute_create ("rtcp-mux",NULL ) ); } add_mid_attributes(media_desc, stream); if (rtp_port != 0) { different_rtp_and_rtcp_addr = (rtcp_addr[0] != '\0') && (strcmp(rtp_addr, rtcp_addr) != 0); if ((rtcp_port != (rtp_port + 1)) || (different_rtp_and_rtcp_addr == TRUE)) { if (different_rtp_and_rtcp_addr == TRUE) { snprintf(buffer, sizeof(buffer), "%u IN IP4 %s", rtcp_port, rtcp_addr); } else { snprintf(buffer, sizeof(buffer), "%u",rtcp_port); } belle_sdp_media_description_add_attribute(media_desc,belle_sdp_attribute_create ("rtcp",buffer)); } } if (stream->set_nortpproxy == TRUE) { belle_sdp_media_description_add_attribute(media_desc,belle_sdp_attribute_create ("nortpproxy","yes")); } if (stream->ice_mismatch == TRUE) { belle_sdp_media_description_add_attribute(media_desc,belle_sdp_attribute_create ("ice-mismatch",NULL)); } else { if (rtp_port != 0) { if (stream->ice_pwd[0] != '\0') belle_sdp_media_description_add_attribute(media_desc,belle_sdp_attribute_create ("ice-pwd",stream->ice_pwd)); if (stream->ice_ufrag[0] != '\0') belle_sdp_media_description_add_attribute(media_desc,belle_sdp_attribute_create ("ice-ufrag",stream->ice_ufrag)); add_ice_candidates(media_desc,stream); add_ice_remote_candidates(media_desc,stream); } } if (stream_enabled && (sal_stream_description_has_avpf(stream) || sal_stream_description_has_implicit_avpf(stream))) { add_rtcp_fb_attributes(media_desc, md, stream); } if (stream_enabled && (stream->rtcp_xr.enabled == TRUE)) { char sastr[1024] = {0}; char mastr[1024] = {0}; size_t saoff = 0; size_t maoff = 0; const belle_sdp_attribute_t *session_attribute = belle_sdp_session_description_get_attribute(session_desc, "rtcp-xr"); belle_sdp_attribute_t *media_attribute; if (session_attribute != NULL) { belle_sip_object_marshal((belle_sip_object_t*)session_attribute, sastr, sizeof(sastr), &saoff); } media_attribute = create_rtcp_xr_attribute(&stream->rtcp_xr); if (media_attribute != NULL) { belle_sip_object_marshal((belle_sip_object_t*)media_attribute, mastr, sizeof(mastr), &maoff); } if (strcmp(sastr, mastr) != 0) { belle_sdp_media_description_add_attribute(media_desc, media_attribute); } else { belle_sip_object_unref((belle_sip_object_t*)media_attribute); } } if (stream->custom_sdp_attributes) { belle_sdp_session_description_t *custom_desc = (belle_sdp_session_description_t *)stream->custom_sdp_attributes; belle_sip_list_t *l = belle_sdp_session_description_get_attributes(custom_desc); belle_sip_list_t *elem; for (elem = l; elem != NULL; elem = elem->next) { belle_sdp_media_description_add_attribute(media_desc, (belle_sdp_attribute_t *)elem->data); } } /* * rfc5576 * 4.1. The "ssrc" Media Attribute * is the synchronization source (SSRC) ID of the * source being described, interpreted as a 32-bit unsigned integer in * network byte order and represented in decimal.*/ belle_sdp_session_description_add_media_description(session_desc, media_desc); } static void bundles_to_sdp(const bctbx_list_t *bundles, belle_sdp_session_description_t *session_desc){ const bctbx_list_t *elem; for (elem = bundles; elem != NULL; elem = elem->next){ SalStreamBundle *bundle = (SalStreamBundle*) elem->data; const bctbx_list_t * id_iterator; char *attr_value = ms_strdup("BUNDLE"); for (id_iterator = bundle->mids; id_iterator != NULL; id_iterator = id_iterator->next){ const char *mid = (const char*) id_iterator->data; char *tmp = ms_strdup_printf("%s %s", attr_value, mid); ms_free(attr_value); attr_value = tmp; } belle_sdp_session_description_add_attribute(session_desc, belle_sdp_attribute_create("group", attr_value)); bctbx_free(attr_value); } } belle_sdp_session_description_t * media_description_to_sdp(const SalMediaDescription *desc) { belle_sdp_session_description_t* session_desc=belle_sdp_session_description_new(); bool_t inet6; belle_sdp_origin_t* origin; int i; char *escaped_username = belle_sip_uri_to_escaped_username(desc->username); if ( strchr ( desc->addr,':' ) !=NULL ) { inet6=1; } else inet6=0; belle_sdp_session_description_set_version ( session_desc,belle_sdp_version_create ( 0 ) ); origin = belle_sdp_origin_create ( escaped_username ,desc->session_id ,desc->session_ver ,"IN" , inet6 ? "IP6" :"IP4" ,desc->addr ); bctbx_free(escaped_username); belle_sdp_session_description_set_origin ( session_desc,origin ); belle_sdp_session_description_set_session_name ( session_desc, belle_sdp_session_name_create ( desc->name[0]!='\0' ? desc->name : "Talk" ) ); if ( !sal_media_description_has_dir ( desc,SalStreamInactive ) || desc->ice_ufrag[0] != '\0' ) { /*in case of sendonly, setting of the IP on cnx we give a chance to receive stun packets*/ belle_sdp_session_description_set_connection ( session_desc ,belle_sdp_connection_create ( "IN",inet6 ? "IP6" :"IP4",desc->addr ) ); } else { belle_sdp_session_description_set_connection ( session_desc ,belle_sdp_connection_create ( "IN" ,inet6 ? "IP6" :"IP4" ,inet6 ? "::0" :"0.0.0.0" ) ); } belle_sdp_session_description_set_time_description ( session_desc,belle_sdp_time_description_create ( 0,0 ) ); if ( desc->bandwidth>0 ) { belle_sdp_session_description_set_bandwidth ( session_desc,"AS",desc->bandwidth ); } if (desc->set_nortpproxy == TRUE) belle_sdp_session_description_add_attribute(session_desc, belle_sdp_attribute_create("nortpproxy","yes")); if (desc->ice_pwd[0] != '\0') belle_sdp_session_description_add_attribute(session_desc, belle_sdp_attribute_create("ice-pwd",desc->ice_pwd)); if (desc->ice_ufrag[0] != '\0') belle_sdp_session_description_add_attribute(session_desc, belle_sdp_attribute_create("ice-ufrag",desc->ice_ufrag)); if (desc->rtcp_xr.enabled == TRUE) { belle_sdp_session_description_add_attribute(session_desc, create_rtcp_xr_attribute(&desc->rtcp_xr)); } if (desc->bundles) bundles_to_sdp(desc->bundles, session_desc); if (desc->custom_sdp_attributes) { belle_sdp_session_description_t *custom_desc = (belle_sdp_session_description_t *)desc->custom_sdp_attributes; belle_sip_list_t *l = belle_sdp_session_description_get_attributes(custom_desc); belle_sip_list_t *elem; for (elem = l; elem != NULL; elem = elem->next) { belle_sdp_session_description_add_attribute(session_desc, (belle_sdp_attribute_t *)elem->data); } } for ( i=0; inb_streams; i++ ) { stream_description_to_sdp(session_desc, desc, &desc->streams[i]); } return session_desc; } static void sdp_parse_payload_types(belle_sdp_media_description_t *media_desc, SalStreamDescription *stream) { PayloadType *pt; PayloadTypeAvpfParams avpf_params; belle_sip_list_t* mime_param_it=NULL; belle_sdp_mime_parameter_t* mime_param; belle_sip_list_t* mime_params=belle_sdp_media_description_build_mime_parameters ( media_desc ); memset(&avpf_params, 0, sizeof(avpf_params)); for ( mime_param_it=mime_params ; mime_param_it!=NULL ; mime_param_it=mime_param_it->next ) { mime_param=BELLE_SDP_MIME_PARAMETER ( mime_param_it->data ); pt=payload_type_new(); payload_type_set_number ( pt,belle_sdp_mime_parameter_get_media_format ( mime_param ) ); pt->clock_rate=belle_sdp_mime_parameter_get_rate ( mime_param ); pt->mime_type=ms_strdup ( belle_sdp_mime_parameter_get_type ( mime_param ) ); pt->channels=belle_sdp_mime_parameter_get_channel_count ( mime_param ); payload_type_set_send_fmtp ( pt,belle_sdp_mime_parameter_get_parameters ( mime_param ) ); payload_type_set_avpf_params(pt, avpf_params); stream->payloads=bctbx_list_append ( stream->payloads,pt ); stream->ptime=belle_sdp_mime_parameter_get_ptime ( mime_param ); stream->maxptime=belle_sdp_mime_parameter_get_max_ptime ( mime_param ); ms_message ( "Found payload %s/%i fmtp=%s",pt->mime_type,pt->clock_rate, pt->send_fmtp ? pt->send_fmtp : "" ); } if ( mime_params ) belle_sip_list_free_with_data ( mime_params,belle_sip_object_unref ); } static void sdp_parse_media_crypto_parameters(belle_sdp_media_description_t *media_desc, SalStreamDescription *stream) { belle_sip_list_t *attribute_it; belle_sdp_attribute_t *attribute; char tmp[257]={0}, tmp2[129]={0}, parameters[257]={0}; int valid_count = 0; int nb; memset ( &stream->crypto, 0, sizeof ( stream->crypto ) ); for ( attribute_it=belle_sdp_media_description_get_attributes ( media_desc ) ; valid_count < SAL_CRYPTO_ALGO_MAX && attribute_it!=NULL; attribute_it=attribute_it->next ) { attribute=BELLE_SDP_ATTRIBUTE ( attribute_it->data ); if ( keywordcmp ( "crypto",belle_sdp_attribute_get_name ( attribute ) ) ==0 && belle_sdp_attribute_get_value ( attribute ) !=NULL ) { nb = sscanf ( belle_sdp_attribute_get_value ( attribute ), "%d %256s inline:%128s %256s", &stream->crypto[valid_count].tag, tmp, tmp2, parameters ); if ( nb >= 3 ) { MSCryptoSuite cs; MSCryptoSuiteNameParams np; np.name=tmp; np.params=parameters[0]!='\0' ? parameters : NULL; cs=ms_crypto_suite_build_from_name_params(&np); if (cs==MS_CRYPTO_SUITE_INVALID){ ms_warning ( "Failed to parse crypto-algo: '%s'", tmp ); stream->crypto[valid_count].algo = MS_CRYPTO_SUITE_INVALID; }else{ char *sep; strncpy ( stream->crypto[valid_count].master_key, tmp2, sizeof(stream->crypto[valid_count].master_key)); stream->crypto[valid_count].master_key[sizeof(stream->crypto[valid_count].master_key) - 1] = '\0'; sep=strchr(stream->crypto[valid_count].master_key,'|'); if (sep) *sep='\0'; stream->crypto[valid_count].algo = cs; ms_message ( "Found valid crypto line (tag:%d algo:'%s' key:'%s'", stream->crypto[valid_count].tag, tmp, stream->crypto[valid_count].master_key ); valid_count++; } }else{ ms_warning ( "sdp has a strange a= line (%s) nb=%i",belle_sdp_attribute_get_value ( attribute ),nb ); } } } ms_message("Found: %d valid crypto lines", valid_count ); } static void sdp_parse_media_ice_parameters(belle_sdp_media_description_t *media_desc, SalStreamDescription *stream) { belle_sip_list_t *attribute_it; belle_sdp_attribute_t *attribute; const char *att_name; const char *value; int nb_ice_candidates = 0; for (attribute_it = belle_sdp_media_description_get_attributes(media_desc); attribute_it != NULL; attribute_it=attribute_it->next) { attribute=BELLE_SDP_ATTRIBUTE(attribute_it->data); att_name = belle_sdp_attribute_get_name(attribute); value = belle_sdp_attribute_get_value(attribute); if ((nb_ice_candidates < (int)(sizeof(stream->ice_candidates)/sizeof(SalIceCandidate))) && (keywordcmp("candidate", att_name) == 0) && (value != NULL)) { SalIceCandidate *candidate = &stream->ice_candidates[nb_ice_candidates]; char proto[4]; int nb = sscanf(value, "%s %u %3s %u %s %d typ %s raddr %s rport %d", candidate->foundation, &candidate->componentID, proto, &candidate->priority, candidate->addr, &candidate->port, candidate->type, candidate->raddr, &candidate->rport); if (strcasecmp("udp",proto)==0 && ((nb == 7) || (nb == 9))) nb_ice_candidates++; else { ms_error("ice: Failed parsing a=candidate SDP attribute"); memset(candidate, 0, sizeof(*candidate)); } } else if ((keywordcmp("remote-candidates", att_name) == 0) && (value != NULL)) { SalIceRemoteCandidate candidate; unsigned int componentID; int offset; const char *ptr = value; const char *endptr = value + strlen(ptr); while (3 == sscanf(ptr, "%u %s %u%n", &componentID, candidate.addr, &candidate.port, &offset)) { if ((componentID > 0) && (componentID <= SAL_MEDIA_DESCRIPTION_MAX_ICE_REMOTE_CANDIDATES)) { SalIceRemoteCandidate *remote_candidate = &stream->ice_remote_candidates[componentID - 1]; strncpy(remote_candidate->addr, candidate.addr, sizeof(remote_candidate->addr)); remote_candidate->port = candidate.port; } ptr += offset; if (ptr < endptr) { if (*ptr == ' ') ptr += 1; } else break; } } else if ((keywordcmp("ice-ufrag", att_name) == 0) && (value != NULL)) { strncpy(stream->ice_ufrag, value, sizeof(stream->ice_ufrag)-1); } else if ((keywordcmp("ice-pwd", att_name) == 0) && (value != NULL)) { strncpy(stream->ice_pwd, value, sizeof(stream->ice_pwd) -1); } else if (keywordcmp("ice-mismatch", att_name) == 0) { stream->ice_mismatch = TRUE; } } } static void enable_avpf_for_stream(SalStreamDescription *stream) { bctbx_list_t *pt_it; for (pt_it = stream->payloads; pt_it != NULL; pt_it = pt_it->next) { PayloadType *pt = (PayloadType *)pt_it->data; payload_type_set_flag(pt, PAYLOAD_TYPE_RTCP_FEEDBACK_ENABLED); } } static void apply_rtcp_fb_attribute_to_payload(belle_sdp_rtcp_fb_attribute_t *fb_attribute, SalStreamDescription *stream, PayloadType *pt) { PayloadTypeAvpfParams avpf_params = payload_type_get_avpf_params(pt); switch (belle_sdp_rtcp_fb_attribute_get_type(fb_attribute)) { case BELLE_SDP_RTCP_FB_ACK: if (belle_sdp_rtcp_fb_attribute_get_param(fb_attribute) == BELLE_SDP_RTCP_FB_RPSI) { avpf_params.features |= PAYLOAD_TYPE_AVPF_RPSI; } break; case BELLE_SDP_RTCP_FB_NACK: switch (belle_sdp_rtcp_fb_attribute_get_param(fb_attribute)) { case BELLE_SDP_RTCP_FB_PLI: avpf_params.features |= PAYLOAD_TYPE_AVPF_PLI; break; case BELLE_SDP_RTCP_FB_SLI: avpf_params.features |= PAYLOAD_TYPE_AVPF_SLI; break; case BELLE_SDP_RTCP_FB_RPSI: /* Linphone uses positive feeback for RPSI. However first versions handling * AVPF wrongly declared RPSI as negative feedback, so this is kept for compatibility * with these versions but will probably be removed at some point in time. */ avpf_params.features |= PAYLOAD_TYPE_AVPF_RPSI; avpf_params.rpsi_compatibility = TRUE; break; case BELLE_SDP_RTCP_FB_NONE: stream->rtcp_fb.generic_nack_enabled = TRUE; break; default: break; } break; case BELLE_SDP_RTCP_FB_TRR_INT: avpf_params.trr_interval = belle_sdp_rtcp_fb_attribute_get_trr_int(fb_attribute); break; case BELLE_SDP_RTCP_FB_CCM: switch (belle_sdp_rtcp_fb_attribute_get_param(fb_attribute)) { case BELLE_SDP_RTCP_FB_FIR: avpf_params.features |= PAYLOAD_TYPE_AVPF_FIR; break; case BELLE_SDP_RTCP_FB_TMMBR: stream->rtcp_fb.tmmbr_enabled = TRUE; break; default: break; } break; default: break; } payload_type_set_avpf_params(pt, avpf_params); } static bool_t sdp_parse_rtcp_fb_parameters(belle_sdp_media_description_t *media_desc, SalStreamDescription *stream) { belle_sip_list_t *it; belle_sdp_attribute_t *attribute; belle_sdp_rtcp_fb_attribute_t *fb_attribute; bctbx_list_t *pt_it; PayloadType *pt; int8_t pt_num; bool_t retval = FALSE; /* Handle rtcp-fb attributes that concern all payload types. */ for (it = belle_sdp_media_description_get_attributes(media_desc); it != NULL; it = it->next) { attribute = BELLE_SDP_ATTRIBUTE(it->data); if (keywordcmp("rtcp-fb", belle_sdp_attribute_get_name(attribute)) == 0) { fb_attribute = BELLE_SDP_RTCP_FB_ATTRIBUTE(attribute); if (belle_sdp_rtcp_fb_attribute_get_id(fb_attribute) == -1) { for (pt_it = stream->payloads; pt_it != NULL; pt_it = pt_it->next) { pt = (PayloadType *)pt_it->data; apply_rtcp_fb_attribute_to_payload(fb_attribute, stream, pt); retval = TRUE; } } } } /* Handle rtcp-fb attributes that are specefic to a payload type. */ for (it = belle_sdp_media_description_get_attributes(media_desc); it != NULL; it = it->next) { attribute = BELLE_SDP_ATTRIBUTE(it->data); if (keywordcmp("rtcp-fb", belle_sdp_attribute_get_name(attribute)) == 0) { fb_attribute = BELLE_SDP_RTCP_FB_ATTRIBUTE(attribute); pt_num = belle_sdp_rtcp_fb_attribute_get_id(fb_attribute); for (pt_it = stream->payloads; pt_it != NULL; pt_it = pt_it->next) { pt = (PayloadType *)pt_it->data; retval = TRUE; if (payload_type_get_number(pt) == (int)pt_num) { apply_rtcp_fb_attribute_to_payload(fb_attribute, stream, pt); } } } } return retval; } static void sal_init_rtcp_xr_description(OrtpRtcpXrConfiguration *config) { config->enabled = FALSE; config->rcvr_rtt_mode = OrtpRtcpXrRcvrRttNone; config->rcvr_rtt_max_size = -1; config->stat_summary_flags = OrtpRtcpXrStatSummaryNone; config->voip_metrics_enabled = FALSE; } static void sdp_parse_rtcp_xr_parameters(const belle_sdp_attribute_t *attribute, OrtpRtcpXrConfiguration *config) { if (attribute != NULL) { const belle_sdp_rtcp_xr_attribute_t *xr_attr; const char *rcvr_rtt_mode; sal_init_rtcp_xr_description(config); xr_attr = BELLE_SDP_RTCP_XR_ATTRIBUTE(attribute); rcvr_rtt_mode = belle_sdp_rtcp_xr_attribute_get_rcvr_rtt_mode(xr_attr); if (rcvr_rtt_mode != NULL) { if (strcasecmp(rcvr_rtt_mode, "all") == 0) { config->rcvr_rtt_mode = OrtpRtcpXrRcvrRttAll; } else if (strcasecmp(rcvr_rtt_mode, "sender") == 0) { config->rcvr_rtt_mode = OrtpRtcpXrRcvrRttSender; } config->rcvr_rtt_max_size = belle_sdp_rtcp_xr_attribute_get_rcvr_rtt_max_size(xr_attr); } config->stat_summary_enabled = (belle_sdp_rtcp_xr_attribute_has_stat_summary(xr_attr) != 0); if (config->stat_summary_enabled) { const belle_sip_list_t *stat_summary_flag_it; for (stat_summary_flag_it = belle_sdp_rtcp_xr_attribute_get_stat_summary_flags(xr_attr); stat_summary_flag_it != NULL; stat_summary_flag_it = stat_summary_flag_it->next ) { const char *flag = (const char *)stat_summary_flag_it->data; if (flag != NULL) { if (strcasecmp(flag, "loss") == 0) config->stat_summary_flags |= OrtpRtcpXrStatSummaryLoss; else if (strcasecmp(flag, "dup") == 0) config->stat_summary_flags |= OrtpRtcpXrStatSummaryDup; else if (strcasecmp(flag, "jitt") == 0) config->stat_summary_flags |= OrtpRtcpXrStatSummaryJitt; else if (strcasecmp(flag, "TTL") == 0) config->stat_summary_flags |= OrtpRtcpXrStatSummaryTTL; else if (strcasecmp(flag, "HL") == 0) config->stat_summary_flags |= OrtpRtcpXrStatSummaryHL; } } } config->voip_metrics_enabled = (belle_sdp_rtcp_xr_attribute_has_voip_metrics(xr_attr) != 0); config->enabled = TRUE; } } static void sdp_parse_session_rtcp_xr_parameters(belle_sdp_session_description_t *session_desc, OrtpRtcpXrConfiguration *config) { const belle_sdp_attribute_t *attribute = belle_sdp_session_description_get_attribute(session_desc, "rtcp-xr"); sdp_parse_rtcp_xr_parameters(attribute, config); } static void sdp_parse_media_rtcp_xr_parameters(belle_sdp_media_description_t *media_desc, OrtpRtcpXrConfiguration *config) { const belle_sdp_attribute_t *attribute = belle_sdp_media_description_get_attribute(media_desc, "rtcp-xr"); sdp_parse_rtcp_xr_parameters(attribute, config); } static SalStreamDescription * sdp_to_stream_description(SalMediaDescription *md, belle_sdp_media_description_t *media_desc) { SalStreamDescription *stream; belle_sdp_connection_t* cnx; belle_sdp_media_t* media; belle_sdp_attribute_t* attribute; belle_sip_list_t *custom_attribute_it; const char* value; const char *mtype,*proto; bool_t has_avpf_attributes; stream=&md->streams[md->nb_streams]; media=belle_sdp_media_description_get_media ( media_desc ); proto = belle_sdp_media_get_protocol ( media ); stream->proto=SalProtoOther; if ( proto ) { if (strcasecmp(proto, "RTP/AVP") == 0) { stream->proto = SalProtoRtpAvp; } else if (strcasecmp(proto, "RTP/SAVP") == 0) { stream->proto = SalProtoRtpSavp; } else if (strcasecmp(proto, "RTP/AVPF") == 0) { stream->proto = SalProtoRtpAvpf; } else if (strcasecmp(proto, "RTP/SAVPF") == 0) { stream->proto = SalProtoRtpSavpf; } else if (strcasecmp(proto, "UDP/TLS/RTP/SAVP") == 0) { stream->proto = SalProtoUdpTlsRtpSavp; } else if (strcasecmp(proto, "UDP/TLS/RTP/SAVPF") == 0) { stream->proto = SalProtoUdpTlsRtpSavpf; } else { strncpy(stream->proto_other,proto,sizeof(stream->proto_other)-1); } } if ( ( cnx=belle_sdp_media_description_get_connection ( media_desc ) ) && belle_sdp_connection_get_address ( cnx ) ) { strncpy ( stream->rtp_addr,belle_sdp_connection_get_address ( cnx ), sizeof ( stream->rtp_addr ) -1 ); stream->ttl=belle_sdp_connection_get_ttl(cnx); } stream->rtp_port=belle_sdp_media_get_media_port ( media ); mtype = belle_sdp_media_get_media_type ( media ); if ( strcasecmp ( "audio", mtype ) == 0 ) { stream->type=SalAudio; } else if ( strcasecmp ( "video", mtype ) == 0 ) { stream->type=SalVideo; } else if ( strcasecmp ( "text", mtype ) == 0 ) { stream->type=SalText; } else { stream->type=SalOther; strncpy ( stream->typeother,mtype,sizeof ( stream->typeother )-1 ); } if ( belle_sdp_media_description_get_bandwidth ( media_desc,"AS" ) >0 ) { stream->bandwidth=belle_sdp_media_description_get_bandwidth ( media_desc,"AS" ); } if ( belle_sdp_media_description_get_attribute ( media_desc,"sendrecv" ) ) { stream->dir=SalStreamSendRecv; } else if ( belle_sdp_media_description_get_attribute ( media_desc,"sendonly" ) ) { stream->dir=SalStreamSendOnly; } else if ( belle_sdp_media_description_get_attribute ( media_desc,"recvonly" ) ) { stream->dir=SalStreamRecvOnly; } else if ( belle_sdp_media_description_get_attribute ( media_desc,"inactive" ) ) { stream->dir=SalStreamInactive; } else { stream->dir=md->dir; /*takes default value if not present*/ } stream->rtcp_mux = belle_sdp_media_description_get_attribute(media_desc, "rtcp-mux") != NULL; stream->bundle_only = belle_sdp_media_description_get_attribute(media_desc, "bundle-only") != NULL; attribute = belle_sdp_media_description_get_attribute(media_desc, "mid"); if (attribute){ value = belle_sdp_attribute_get_value(attribute); if (value) strncpy(stream->mid, value, sizeof(stream->mid) - 1); } /* Get media payload types */ sdp_parse_payload_types(media_desc, stream); /* Get media specific RTCP attribute */ stream->rtcp_port = stream->rtp_port + 1; strncpy(stream->rtcp_addr, stream->rtp_addr, sizeof(stream->rtcp_addr) - 1); attribute=belle_sdp_media_description_get_attribute(media_desc,"rtcp"); if (attribute && (value=belle_sdp_attribute_get_value(attribute))!=NULL){ char *tmp = (char *)ms_malloc0(strlen(value)); int nb = sscanf(value, "%d IN IP4 %s", &stream->rtcp_port, tmp); if (nb == 1) { /* SDP rtcp attribute only contains the port */ } else if (nb == 2) { strncpy(stream->rtcp_addr, tmp, sizeof(stream->rtcp_addr)); stream->rtcp_addr[sizeof(stream->rtcp_addr) - 1] = '\0'; } else { ms_warning("sdp has a strange a=rtcp line (%s) nb=%i", value, nb); } ms_free(tmp); } /* Read DTLS specific attributes : check is some are found in the stream description otherwise copy the session description one(which are at least set to Invalid) */ if (((stream->proto == SalProtoUdpTlsRtpSavpf) || (stream->proto == SalProtoUdpTlsRtpSavp))) { attribute=belle_sdp_media_description_get_attribute(media_desc,"setup"); if (attribute && (value=belle_sdp_attribute_get_value(attribute))!=NULL){ if (strncmp(value, "actpass", 7) == 0) { stream->dtls_role = SalDtlsRoleUnset; } else if (strncmp(value, "active", 6) == 0) { stream->dtls_role = SalDtlsRoleIsClient; } else if (strncmp(value, "passive", 7) == 0) { stream->dtls_role = SalDtlsRoleIsServer; } } if (stream->dtls_role != SalDtlsRoleInvalid && (attribute=belle_sdp_media_description_get_attribute(media_desc,"fingerprint"))) { strncpy(stream->dtls_fingerprint, belle_sdp_attribute_get_value(attribute),sizeof(stream->dtls_fingerprint)); } } /* Read crypto lines if any */ if (sal_stream_description_has_srtp(stream)) { sdp_parse_media_crypto_parameters(media_desc, stream); } /* Read zrtp-hash attribute */ if ((attribute=belle_sdp_media_description_get_attribute(media_desc,"zrtp-hash"))!=NULL) { if ((value=belle_sdp_attribute_get_value(attribute))!=NULL) { strncpy((char *)(stream->zrtphash), belle_sdp_attribute_get_value(attribute),sizeof(stream->zrtphash)); stream->haveZrtpHash = 1; } } /* Do we have Lime Ik attribute */ if ((attribute=belle_sdp_media_description_get_attribute(media_desc,"Ik"))!=NULL) { if (belle_sdp_attribute_get_value(attribute)!=NULL) { stream->haveLimeIk = 1; } } /* get ready to parse also lime-Ik */ if ((attribute=belle_sdp_media_description_get_attribute(media_desc,"lime-Ik"))!=NULL) { if (belle_sdp_attribute_get_value(attribute)!=NULL) { stream->haveLimeIk = 1; } } /* Get ICE candidate attributes if any */ sdp_parse_media_ice_parameters(media_desc, stream); has_avpf_attributes = sdp_parse_rtcp_fb_parameters(media_desc, stream); /* Get RTCP-FB attributes if any */ if (sal_stream_description_has_avpf(stream)) { enable_avpf_for_stream(stream); }else if (has_avpf_attributes ){ enable_avpf_for_stream(stream); stream->implicit_rtcp_fb = TRUE; } /* Get RTCP-XR attributes if any */ stream->rtcp_xr = md->rtcp_xr; // Use session parameters if no stream parameters are defined sdp_parse_media_rtcp_xr_parameters(media_desc, &stream->rtcp_xr); /* Get the custom attributes, and parse some 'extmap'*/ for (custom_attribute_it = belle_sdp_media_description_get_attributes(media_desc); custom_attribute_it != NULL; custom_attribute_it = custom_attribute_it->next) { belle_sdp_attribute_t *attr = (belle_sdp_attribute_t *)custom_attribute_it->data; const char *attr_name = belle_sdp_attribute_get_name(attr); const char *attr_value = belle_sdp_attribute_get_value(attr); stream->custom_sdp_attributes = sal_custom_sdp_attribute_append(stream->custom_sdp_attributes, attr_name, attr_value); if (strcasecmp(attr_name, "extmap") == 0){ char *extmap_urn = (char*)bctbx_malloc0(strlen(attr_value) + 1); int rtp_ext_header_id = 0; if (sscanf(attr_value, "%i %s", &rtp_ext_header_id, extmap_urn) > 0 && strcasecmp(extmap_urn, "urn:ietf:params:rtp-hdrext:sdes:mid") == 0){ stream->mid_rtp_ext_header_id = rtp_ext_header_id; } bctbx_free(extmap_urn); } } md->nb_streams++; return stream; } static void add_bundles(SalMediaDescription *desc, const char *ids){ char *tmp = (char*)ms_malloc0(strlen(ids) + 1); int err; SalStreamBundle *bundle = sal_media_description_add_new_bundle(desc); do{ int consumed = 0; err = sscanf(ids, "%s%n", tmp, &consumed); if (err > 0){ bundle->mids = bctbx_list_append(bundle->mids, bctbx_strdup(tmp)); ids += consumed; }else break; }while( *ids != '\0'); ms_free(tmp); } int sdp_to_media_description( belle_sdp_session_description_t *session_desc, SalMediaDescription *desc ) { belle_sdp_connection_t* cnx; belle_sip_list_t* media_desc_it; belle_sdp_media_description_t* media_desc; belle_sdp_session_name_t *sname; belle_sip_list_t *custom_attribute_it; const char* value; SalDtlsRole session_role=SalDtlsRoleInvalid; int i; desc->nb_streams = 0; desc->dir = SalStreamSendRecv; if ( ( cnx=belle_sdp_session_description_get_connection ( session_desc ) ) && belle_sdp_connection_get_address ( cnx ) ) { strncpy ( desc->addr,belle_sdp_connection_get_address ( cnx ),sizeof ( desc->addr ) -1 ); } if ( (sname=belle_sdp_session_description_get_session_name(session_desc)) && belle_sdp_session_name_get_value(sname) ){ strncpy(desc->name,belle_sdp_session_name_get_value(sname),sizeof(desc->name) - 1); } if ( belle_sdp_session_description_get_bandwidth ( session_desc,"AS" ) >0 ) { desc->bandwidth=belle_sdp_session_description_get_bandwidth ( session_desc,"AS" ); } belle_sdp_origin_t *origin = belle_sdp_session_description_get_origin(session_desc); desc->session_id = belle_sdp_origin_get_session_id(origin); desc->session_ver = belle_sdp_origin_get_session_version(origin); /*in some very rare case, session attribute may set stream dir*/ if ( belle_sdp_session_description_get_attribute ( session_desc,"sendrecv" ) ) { desc->dir=SalStreamSendRecv; } else if ( belle_sdp_session_description_get_attribute ( session_desc,"sendonly" ) ) { desc->dir=SalStreamSendOnly; } else if ( belle_sdp_session_description_get_attribute ( session_desc,"recvonly" ) ) { desc->dir=SalStreamRecvOnly; } else if ( belle_sdp_session_description_get_attribute ( session_desc,"inactive" ) ) { desc->dir=SalStreamInactive; } /*DTLS attributes can be defined at session level.*/ value=belle_sdp_session_description_get_attribute_value(session_desc,"setup"); if (value){ if (strncmp(value, "actpass", 7) == 0) { session_role = SalDtlsRoleUnset; } else if (strncmp(value, "active", 6) == 0) { session_role = SalDtlsRoleIsClient; } else if (strncmp(value, "passive", 7) == 0) { session_role = SalDtlsRoleIsServer; } } value=belle_sdp_session_description_get_attribute_value(session_desc,"fingerprint"); /*copy dtls attributes to every streams, might be overwritten stream by stream*/ for (i=0;istreams[i].dtls_fingerprint, value, sizeof(desc->streams[i].dtls_fingerprint)); desc->streams[i].dtls_role=session_role; /*set or reset value*/ } /* Get ICE remote ufrag and remote pwd, and ice_lite flag */ value=belle_sdp_session_description_get_attribute_value(session_desc,"ice-ufrag"); if (value) strncpy(desc->ice_ufrag, value, sizeof(desc->ice_ufrag) - 1); value=belle_sdp_session_description_get_attribute_value(session_desc,"ice-pwd"); if (value) strncpy(desc->ice_pwd, value, sizeof(desc->ice_pwd)-1); value=belle_sdp_session_description_get_attribute_value(session_desc,"ice-lite"); if (value) desc->ice_lite = TRUE; /* Get session RTCP-XR attributes if any */ sdp_parse_session_rtcp_xr_parameters(session_desc, &desc->rtcp_xr); /* Get the custom attributes, parse some of them that are relevant */ for (custom_attribute_it = belle_sdp_session_description_get_attributes(session_desc); custom_attribute_it != NULL; custom_attribute_it = custom_attribute_it->next) { belle_sdp_attribute_t *attr = (belle_sdp_attribute_t *)custom_attribute_it->data; desc->custom_sdp_attributes = sal_custom_sdp_attribute_append(desc->custom_sdp_attributes, belle_sdp_attribute_get_name(attr), belle_sdp_attribute_get_value(attr)); if (strcasecmp(belle_sdp_attribute_get_name(attr), "group") == 0){ value = belle_sdp_attribute_get_value(attr); if (value && strncasecmp(value, "BUNDLE", strlen("BUNDLE")) == 0){ add_bundles(desc, value + strlen("BUNDLE")); } } } for ( media_desc_it=belle_sdp_session_description_get_media_descriptions ( session_desc ) ; media_desc_it!=NULL ; media_desc_it=media_desc_it->next ) { if (desc->nb_streams==SAL_MEDIA_DESCRIPTION_MAX_STREAMS){ ms_warning("Cannot convert mline at position [%i] from SDP to SalMediaDescription",desc->nb_streams); break; } media_desc=BELLE_SDP_MEDIA_DESCRIPTION ( media_desc_it->data ); sdp_to_stream_description(desc, media_desc); } return 0; } liblinphone-4.4.21/coreapi/buffer.c000066400000000000000000000062751376572216100171700ustar00rootroot00000000000000/* * Copyright (c) 2010-2019 Belledonne Communications SARL. * * This file is part of Liblinphone. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "linphone/core.h" #include "c-wrapper/c-wrapper.h" // TODO: From coreapi. Remove me later. #include "private.h" static void linphone_buffer_destroy(LinphoneBuffer *buffer) { if (buffer->content) belle_sip_free(buffer->content); } BELLE_SIP_DECLARE_NO_IMPLEMENTED_INTERFACES(LinphoneBuffer); BELLE_SIP_INSTANCIATE_VPTR(LinphoneBuffer, belle_sip_object_t, (belle_sip_object_destroy_t)linphone_buffer_destroy, NULL, // clone NULL, // marshal TRUE ); LinphoneBuffer * linphone_buffer_new(void) { LinphoneBuffer *buffer = belle_sip_object_new(LinphoneBuffer); belle_sip_object_ref(buffer); return buffer; } LinphoneBuffer * linphone_buffer_new_from_data(const uint8_t *data, size_t size) { LinphoneBuffer *buffer = linphone_buffer_new(); linphone_buffer_set_content(buffer, data, size); return buffer; } LinphoneBuffer * linphone_buffer_new_from_string(const char *data) { LinphoneBuffer *buffer = linphone_buffer_new(); linphone_buffer_set_string_content(buffer, data); return buffer; } LinphoneBuffer * linphone_buffer_ref(LinphoneBuffer *buffer) { belle_sip_object_ref(buffer); return buffer; } void linphone_buffer_unref(LinphoneBuffer *buffer) { belle_sip_object_unref(buffer); } void *linphone_buffer_get_user_data(const LinphoneBuffer *buffer) { return buffer->user_data; } void linphone_buffer_set_user_data(LinphoneBuffer *buffer, void *ud) { buffer->user_data = ud; } const uint8_t * linphone_buffer_get_content(const LinphoneBuffer *buffer) { return buffer->content; } void linphone_buffer_set_content(LinphoneBuffer *buffer, const uint8_t *content, size_t size) { buffer->size = size; if (buffer->content) belle_sip_free(buffer->content); buffer->content = reinterpret_cast(belle_sip_malloc(size + 1)); memcpy(buffer->content, content, size); ((char *)buffer->content)[size] = '\0'; } const char * linphone_buffer_get_string_content(const LinphoneBuffer *buffer) { return (const char *)buffer->content; } void linphone_buffer_set_string_content(LinphoneBuffer *buffer, const char *content) { buffer->size = strlen(content); if (buffer->content) belle_sip_free(buffer->content); buffer->content = (uint8_t *)belle_sip_strdup(content); } size_t linphone_buffer_get_size(const LinphoneBuffer *buffer) { return buffer->size; } void linphone_buffer_set_size(LinphoneBuffer *buffer, size_t size) { buffer->size = size; } bool_t linphone_buffer_is_empty(const LinphoneBuffer *buffer) { return (buffer->size == 0) ? TRUE : FALSE; } liblinphone-4.4.21/coreapi/call_log.c000066400000000000000000000563741376572216100175000ustar00rootroot00000000000000/* * Copyright (c) 2010-2019 Belledonne Communications SARL. * * This file is part of Liblinphone. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef __APPLE__ /*XOPEN_SOURCE icompilation issue with xcode9 https://github.com/eclipse/omr/pull/1721*/ #define _XOPEN_SOURCE 700 /*required for strptime of GNU libc*/ #endif #include #if !defined(_WIN32) && !defined(__ANDROID__) && !defined(__QNXNTO__) #include #include #include #endif // if !defined(_WIN32) && !defined(__ANDROID__) && !defined(__QNXNTO__) #define MAX_PATH_SIZE 1024 #include "c-wrapper/c-wrapper.h" // TODO: From coreapi. Remove me later. #include "private.h" typedef struct _CallLogStorageResult { LinphoneCore *core; bctbx_list_t *result; } CallLogStorageResult; /******************************************************************************* * Internal functions * ******************************************************************************/ /*prevent a gcc bug with %c*/ static size_t my_strftime(char *s, size_t max, const char *fmt, const struct tm *tm){ return strftime(s, max, fmt, tm); } static time_t string_to_time(const char *date){ #ifndef _WIN32 struct tm tmtime={0}; strptime(date,"%c",&tmtime); return mktime(&tmtime); #else return 0; #endif } static void set_call_log_date(LinphoneCallLog *cl, time_t start_time){ struct tm loctime; #ifdef _WIN32 #if !defined(_WIN32_WCE) loctime=*localtime(&start_time); /*FIXME*/ #endif /*_WIN32_WCE*/ #else localtime_r(&start_time,&loctime); #endif my_strftime(cl->start_date,sizeof(cl->start_date),"%c",&loctime); } /******************************************************************************* * Private functions * ******************************************************************************/ void call_logs_write_to_config_file(LinphoneCore *lc){ bctbx_list_t *elem; char logsection[32]; int i; char *tmp; LpConfig *cfg=lc->config; if (linphone_core_get_global_state (lc)==LinphoneGlobalStartup) return; if (lc->max_call_logs == LINPHONE_MAX_CALL_HISTORY_UNLIMITED) return; for(i=0,elem=lc->call_logs;elem!=NULL;elem=elem->next,++i){ LinphoneCallLog *cl=(LinphoneCallLog*)elem->data; snprintf(logsection,sizeof(logsection),"call_log_%i",i); lp_config_clean_section(cfg,logsection); lp_config_set_int(cfg,logsection,"dir",cl->dir); lp_config_set_int(cfg,logsection,"status",cl->status); tmp=linphone_address_as_string(cl->from); lp_config_set_string(cfg,logsection,"from",tmp); ms_free(tmp); tmp=linphone_address_as_string(cl->to); lp_config_set_string(cfg,logsection,"to",tmp); ms_free(tmp); if (cl->start_date_time) lp_config_set_int64(cfg,logsection,"start_date_time",(int64_t)cl->start_date_time); else lp_config_set_string(cfg,logsection,"start_date",cl->start_date); lp_config_set_int(cfg,logsection,"duration",cl->duration); if (cl->refkey) lp_config_set_string(cfg,logsection,"refkey",cl->refkey); lp_config_set_float(cfg,logsection,"quality",cl->quality); lp_config_set_int(cfg,logsection,"video_enabled", cl->video_enabled); lp_config_set_string(cfg,logsection,"call_id",cl->call_id); } for(;imax_call_logs;++i){ snprintf(logsection,sizeof(logsection),"call_log_%i",i); lp_config_clean_section(cfg,logsection); } } bctbx_list_t * linphone_core_read_call_logs_from_config_file(LinphoneCore *lc){ char logsection[32]; int i; const char *tmp; uint64_t sec; LpConfig *cfg=lc->config; bctbx_list_t *call_logs = NULL; for(i=0;;++i){ snprintf(logsection,sizeof(logsection),"call_log_%i",i); if (lp_config_has_section(cfg,logsection)){ LinphoneCallLog *cl; LinphoneAddress *from=NULL,*to=NULL; tmp=lp_config_get_string(cfg,logsection,"from",NULL); if (tmp) from=linphone_address_new(tmp); tmp=lp_config_get_string(cfg,logsection,"to",NULL); if (tmp) to=linphone_address_new(tmp); if (!from || !to) continue; cl=linphone_call_log_new(static_cast(lp_config_get_int(cfg,logsection,"dir",0)),from,to); cl->status=static_cast(lp_config_get_int(cfg,logsection,"status",0)); sec=(uint64_t)lp_config_get_int64(cfg,logsection,"start_date_time",0); if (sec) { /*new call log format with date expressed in seconds */ cl->start_date_time=(time_t)sec; set_call_log_date(cl,cl->start_date_time); }else{ tmp=lp_config_get_string(cfg,logsection,"start_date",NULL); if (tmp) { strncpy(cl->start_date,tmp,sizeof(cl->start_date)); cl->start_date[sizeof(cl->start_date) - 1] = '\0'; cl->start_date_time=string_to_time(cl->start_date); } } cl->duration=lp_config_get_int(cfg,logsection,"duration",0); tmp=lp_config_get_string(cfg,logsection,"refkey",NULL); if (tmp) cl->refkey=ms_strdup(tmp); cl->quality=lp_config_get_float(cfg,logsection,"quality",-1); cl->video_enabled=!!lp_config_get_int(cfg,logsection,"video_enabled",0); tmp=lp_config_get_string(cfg,logsection,"call_id",NULL); if (tmp) cl->call_id=ms_strdup(tmp); call_logs=bctbx_list_append(call_logs,cl); }else break; } return call_logs; } /******************************************************************************* * Public functions * ******************************************************************************/ LinphoneCallLog *linphone_core_create_call_log(LinphoneCore *lc, LinphoneAddress *from, LinphoneAddress *to, LinphoneCallDir dir, int duration, time_t start_time, time_t connected_time, LinphoneCallStatus status, bool_t video_enabled, float quality) { LinphoneCallLog *log = linphone_call_log_new(dir, linphone_address_ref(from), linphone_address_ref(to)); log->duration = duration; log->start_date_time = start_time; set_call_log_date(log,log->start_date_time); log->connected_date_time = connected_time; log->status = status; log->video_enabled = video_enabled; log->quality = quality; linphone_core_store_call_log(lc, log); return log; } const char *linphone_call_log_get_call_id(const LinphoneCallLog *cl){ return cl->call_id; } void linphone_call_log_set_call_id(LinphoneCallLog *cl, const char *call_id) { if (cl->call_id) bctbx_free(cl->call_id); cl->call_id = call_id ? bctbx_strdup(call_id) : NULL; } LinphoneCallDir linphone_call_log_get_dir(const LinphoneCallLog *cl){ return cl->dir; } int linphone_call_log_get_duration(const LinphoneCallLog *cl){ return cl->duration; } const LinphoneAddress *linphone_call_log_get_from_address(const LinphoneCallLog *cl){ return cl->from; } const rtp_stats_t *linphone_call_log_get_local_stats(const LinphoneCallLog *cl){ return &cl->local_stats; } float linphone_call_log_get_quality(const LinphoneCallLog *cl){ return cl->quality; } const char *linphone_call_log_get_ref_key(const LinphoneCallLog *cl){ return cl->refkey; } const LinphoneAddress *linphone_call_log_get_local_address(const LinphoneCallLog *cl) { return (cl->dir == LinphoneCallIncoming) ? cl->to : cl->from; } const LinphoneAddress *linphone_call_log_get_remote_address(const LinphoneCallLog *cl){ return (cl->dir == LinphoneCallIncoming) ? cl->from : cl->to; } const rtp_stats_t *linphone_call_log_get_remote_stats(const LinphoneCallLog *cl){ return &cl->remote_stats; } time_t linphone_call_log_get_start_date(const LinphoneCallLog *cl){ return cl->start_date_time; } LinphoneCallStatus linphone_call_log_get_status(const LinphoneCallLog *cl){ return cl->status; } const LinphoneAddress *linphone_call_log_get_to_address(const LinphoneCallLog *cl){ return cl->to; } void linphone_call_log_set_ref_key(LinphoneCallLog *cl, const char *refkey){ if (cl->refkey!=NULL){ ms_free(cl->refkey); cl->refkey=NULL; } if (refkey) cl->refkey=ms_strdup(refkey); } char * linphone_call_log_to_str(const LinphoneCallLog *cl){ const char *status; char *tmp; char *from=linphone_address_as_string (cl->from); char *to=linphone_address_as_string (cl->to); switch(cl->status){ case LinphoneCallAborted: status="aborted"; break; case LinphoneCallSuccess: status="completed"; break; case LinphoneCallMissed: status="missed"; break; case LinphoneCallAcceptedElsewhere: status="answered elsewhere"; break; case LinphoneCallDeclinedElsewhere: status="declined elsewhere"; break; default: status="unknown"; } tmp=ms_strdup_printf("%s at %s\nFrom: %s\nTo: %s\nStatus: %s\nDuration: %i mn %i sec\n", (cl->dir==LinphoneCallIncoming) ? "Incoming call" : "Outgoing call", cl->start_date, from, to, status, cl->duration/60, cl->duration%60); ms_free(from); ms_free(to); return tmp; } bool_t linphone_call_log_video_enabled(const LinphoneCallLog *cl) { return cl->video_enabled; } bool_t linphone_call_log_was_conference(const LinphoneCallLog *cl) { return cl->was_conference; } const LinphoneErrorInfo *linphone_call_log_get_error_info(const LinphoneCallLog *cl){ return cl->error_info; } /******************************************************************************* * Reference and user data handling functions * ******************************************************************************/ void *linphone_call_log_get_user_data(const LinphoneCallLog *cl) { return cl->user_data; } void linphone_call_log_set_user_data(LinphoneCallLog *cl, void *ud) { cl->user_data = ud; } LinphoneCallLog * linphone_call_log_ref(LinphoneCallLog *cl) { belle_sip_object_ref(cl); return cl; } void linphone_call_log_unref(LinphoneCallLog *cl) { belle_sip_object_unref(cl); } /******************************************************************************* * Constructor and destructor functions * ******************************************************************************/ static void _linphone_call_log_destroy(LinphoneCallLog *cl) { if (cl->from!=NULL) linphone_address_unref(cl->from); if (cl->to!=NULL) linphone_address_unref(cl->to); if (cl->refkey!=NULL) ms_free(cl->refkey); if (cl->call_id) ms_free(cl->call_id); if (cl->reporting.reports[LINPHONE_CALL_STATS_AUDIO]!=NULL) linphone_reporting_destroy(cl->reporting.reports[LINPHONE_CALL_STATS_AUDIO]); if (cl->reporting.reports[LINPHONE_CALL_STATS_VIDEO]!=NULL) linphone_reporting_destroy(cl->reporting.reports[LINPHONE_CALL_STATS_VIDEO]); if (cl->reporting.reports[LINPHONE_CALL_STATS_TEXT]!=NULL) linphone_reporting_destroy(cl->reporting.reports[LINPHONE_CALL_STATS_TEXT]); if (cl->error_info) linphone_error_info_unref(cl->error_info); } LinphoneCallLog * linphone_call_log_new(LinphoneCallDir dir, LinphoneAddress *from, LinphoneAddress *to) { LinphoneCallLog *cl=belle_sip_object_new(LinphoneCallLog); cl->dir=dir; cl->start_date_time=time(NULL); set_call_log_date(cl,cl->start_date_time); cl->from=from; cl->to=to; cl->status=LinphoneCallAborted; /*default status*/ cl->quality=-1; cl->storage_id=0; cl->reporting.reports[LINPHONE_CALL_STATS_AUDIO]=linphone_reporting_new(); cl->reporting.reports[LINPHONE_CALL_STATS_VIDEO]=linphone_reporting_new(); cl->reporting.reports[LINPHONE_CALL_STATS_TEXT]=linphone_reporting_new(); cl->connected_date_time=0; return cl; } /* DEPRECATED */ void linphone_call_log_destroy(LinphoneCallLog *cl) { belle_sip_object_unref(cl); } BELLE_SIP_DECLARE_NO_IMPLEMENTED_INTERFACES(LinphoneCallLog); BELLE_SIP_INSTANCIATE_VPTR(LinphoneCallLog, belle_sip_object_t, (belle_sip_object_destroy_t)_linphone_call_log_destroy, NULL, // clone NULL, // marshal FALSE ); /******************************************************************************* * SQL storage related functions * ******************************************************************************/ static void linphone_create_call_log_table(sqlite3* db) { char* errmsg=NULL; int ret; ret=sqlite3_exec(db,"CREATE TABLE IF NOT EXISTS call_history (" "id INTEGER PRIMARY KEY AUTOINCREMENT," "caller TEXT NOT NULL," // Can't name a field "from"... "callee TEXT NOT NULL," "direction INTEGER," "duration INTEGER," "start_time TEXT NOT NULL," "connected_time TEXT NOT NULL," "status INTEGER," "videoEnabled INTEGER," "quality REAL" ");", 0,0,&errmsg); if(ret != SQLITE_OK) { ms_error("Error in creation: %s.\n", errmsg); sqlite3_free(errmsg); } } static void linphone_update_call_log_table(sqlite3* db) { char* errmsg=NULL; int ret; // for image url storage ret=sqlite3_exec(db,"ALTER TABLE call_history ADD COLUMN call_id TEXT;",NULL,NULL,&errmsg); if(ret != SQLITE_OK) { ms_message("Table already up to date: %s.", errmsg); sqlite3_free(errmsg); } else { ret=sqlite3_exec(db,"ALTER TABLE call_history ADD COLUMN refkey TEXT;",NULL,NULL,&errmsg); if(ret != SQLITE_OK) { ms_message("Table already up to date: %s.", errmsg); sqlite3_free(errmsg); } else { ms_debug("Table call_history updated successfully for call_id and refkey."); } } } void linphone_core_call_log_storage_init(LinphoneCore *lc) { int ret; const char *errmsg; sqlite3 *db; linphone_core_call_log_storage_close(lc); ret=_linphone_sqlite3_open(lc->logs_db_file, &db); if(ret != SQLITE_OK) { errmsg = sqlite3_errmsg(db); ms_error("Error in the opening: %s.\n", errmsg); sqlite3_close(db); return; } linphone_create_call_log_table(db); linphone_update_call_log_table(db); lc->logs_db = db; // Load the existing call logs linphone_core_get_call_history(lc); } void linphone_core_call_log_storage_close(LinphoneCore *lc) { if (lc->logs_db){ sqlite3_close(lc->logs_db); lc->logs_db = NULL; } } static LinphoneCallLog * find_call_log_by_storage_id(bctbx_list_t *call_logs, unsigned int storage_id) { bctbx_list_t *item; for (item = call_logs; item != NULL; item = bctbx_list_next(item)) { LinphoneCallLog *call_log = reinterpret_cast(bctbx_list_get_data(item)); if (call_log->storage_id == storage_id) return call_log; } return NULL; } /* DB layout: * | 0 | storage_id * | 1 | from * | 2 | to * | 3 | direction flag * | 4 | duration * | 5 | start date time (time_t) * | 6 | connected date time (time_t) * | 7 | status * | 8 | video enabled (1 or 0) * | 9 | quality * | 10 | call_id * | 11 | refkey */ static int create_call_log(void *data, int argc, char **argv, char **colName) { CallLogStorageResult *clsres = (CallLogStorageResult *)data; LinphoneAddress *from; LinphoneAddress *to; LinphoneCallDir dir; LinphoneCallLog *log; unsigned int storage_id = (unsigned int)atoi(argv[0]); log = find_call_log_by_storage_id(clsres->core->call_logs, storage_id); if (log != NULL) { clsres->result = bctbx_list_append(clsres->result, linphone_call_log_ref(log)); return 0; } from = linphone_address_new(argv[1]); to = linphone_address_new(argv[2]); if (from == NULL || to == NULL) goto error; dir = (LinphoneCallDir) atoi(argv[3]); log = linphone_call_log_new(dir, from, to); log->storage_id = storage_id; log->duration = atoi(argv[4]); log->start_date_time = (time_t)atol(argv[5]); set_call_log_date(log,log->start_date_time); log->connected_date_time = (time_t)atol(argv[6]); log->status = (LinphoneCallStatus) atoi(argv[7]); log->video_enabled = atoi(argv[8]) == 1; log->quality = (float)atof(argv[9]); if (argc > 10) { if (argv[10] != NULL) { log->call_id = ms_strdup(argv[10]); } if (argv[10] != NULL) { log->refkey = ms_strdup(argv[11]); } } clsres->result = bctbx_list_append(clsres->result, log); return 0; error: if (from){ linphone_address_unref(from); } if (to){ linphone_address_unref(to); } ms_error("Bad call log at storage_id %u", storage_id); return 0; } static void linphone_sql_request_call_log(sqlite3 *db, const char *stmt, CallLogStorageResult *clsres) { char* errmsg = NULL; int ret; ret = sqlite3_exec(db, stmt, create_call_log, clsres, &errmsg); if (ret != SQLITE_OK) { ms_error("linphone_sql_request: statement %s -> error sqlite3_exec(): %s.", stmt, errmsg); sqlite3_free(errmsg); } } static int linphone_sql_request_generic(sqlite3* db, const char *stmt) { char* errmsg = NULL; int ret; ret = sqlite3_exec(db, stmt, NULL, NULL, &errmsg); if (ret != SQLITE_OK) { ms_error("linphone_sql_request: statement %s -> error sqlite3_exec(): %s.", stmt, errmsg); sqlite3_free(errmsg); } return ret; } void linphone_core_store_call_log(LinphoneCore *lc, LinphoneCallLog *log) { if (lc && lc->logs_db){ char *from, *to; char *buf; from = linphone_address_as_string(log->from); to = linphone_address_as_string(log->to); buf = sqlite3_mprintf("INSERT INTO call_history VALUES(NULL,%Q,%Q,%i,%i,%lld,%lld,%i,%i,%f,%Q,%Q);", from, to, log->dir, log->duration, (int64_t)log->start_date_time, (int64_t)log->connected_date_time, log->status, log->video_enabled ? 1 : 0, log->quality, log->call_id, log->refkey ); linphone_sql_request_generic(lc->logs_db, buf); sqlite3_free(buf); ms_free(from); ms_free(to); log->storage_id = (unsigned int)sqlite3_last_insert_rowid(lc->logs_db); } if (lc) { lc->call_logs = bctbx_list_prepend(lc->call_logs, linphone_call_log_ref(log)); } } const bctbx_list_t *linphone_core_get_call_history(LinphoneCore *lc) { char *buf; uint64_t begin,end; CallLogStorageResult clsres; if (!lc || lc->logs_db == NULL) return NULL; if (lc->call_logs != NULL) return lc->call_logs; if (lc->max_call_logs != LINPHONE_MAX_CALL_HISTORY_UNLIMITED){ buf = sqlite3_mprintf("SELECT * FROM call_history ORDER BY id DESC LIMIT %i", lc->max_call_logs); }else{ buf = sqlite3_mprintf("SELECT * FROM call_history ORDER BY id DESC"); } clsres.core = lc; clsres.result = NULL; begin = ortp_get_cur_time_ms(); linphone_sql_request_call_log(lc->logs_db, buf, &clsres); end = ortp_get_cur_time_ms(); ms_message("%s(): completed in %i ms",__FUNCTION__, (int)(end-begin)); sqlite3_free(buf); lc->call_logs = clsres.result; return lc->call_logs; } void linphone_core_delete_call_history(LinphoneCore *lc) { char *buf; if (!lc || lc->logs_db == NULL) return ; buf = sqlite3_mprintf("DELETE FROM call_history"); linphone_sql_request_generic(lc->logs_db, buf); sqlite3_free(buf); } void linphone_core_delete_call_log(LinphoneCore *lc, LinphoneCallLog *log) { char *buf; if (!lc || lc->logs_db == NULL) return ; buf = sqlite3_mprintf("DELETE FROM call_history WHERE id = %u", log->storage_id); linphone_sql_request_generic(lc->logs_db, buf); sqlite3_free(buf); } int linphone_core_get_call_history_size(LinphoneCore *lc) { int numrows = 0; char *buf; sqlite3_stmt *selectStatement; int returnValue; if (!lc) return 0; if (!lc->logs_db) return (int)bctbx_list_size(lc->call_logs); buf = sqlite3_mprintf("SELECT count(*) FROM call_history"); returnValue = sqlite3_prepare_v2(lc->logs_db, buf, -1, &selectStatement, NULL); if (returnValue == SQLITE_OK){ if(sqlite3_step(selectStatement) == SQLITE_ROW){ numrows = sqlite3_column_int(selectStatement, 0); } } sqlite3_finalize(selectStatement); sqlite3_free(buf); return numrows; } bctbx_list_t * linphone_core_get_call_history_for_address(LinphoneCore *lc, const LinphoneAddress *addr) { char *buf; char *sipAddress; uint64_t begin,end; CallLogStorageResult clsres; if (!lc || lc->logs_db == NULL || addr == NULL) return NULL; /*since we want to append query parameters depending on arguments given, we use malloc instead of sqlite3_mprintf*/ sipAddress = linphone_address_as_string_uri_only(addr); buf = sqlite3_mprintf("SELECT * FROM call_history WHERE caller LIKE '%%%q%%' OR callee LIKE '%%%q%%' ORDER BY id DESC", sipAddress, sipAddress); // The '%%%q%%' takes care of the eventual presence of a display name clsres.core = lc; clsres.result = NULL; begin = ortp_get_cur_time_ms(); linphone_sql_request_call_log(lc->logs_db, buf, &clsres); end = ortp_get_cur_time_ms(); ms_message("%s(): completed in %i ms",__FUNCTION__, (int)(end-begin)); sqlite3_free(buf); ms_free(sipAddress); return clsres.result; } bctbx_list_t *linphone_core_get_call_history_2( LinphoneCore *lc, const LinphoneAddress *peer_addr, const LinphoneAddress *local_addr ) { char *buf; char *peer_addr_str; char *local_addr_str; uint64_t begin, end; CallLogStorageResult clsres; if (!lc || !lc->logs_db || !peer_addr || !local_addr) return NULL; peer_addr_str = bctbx_strdup(L_GET_CPP_PTR_FROM_C_OBJECT(peer_addr)->asStringUriOnly().c_str()); local_addr_str = bctbx_strdup(L_GET_CPP_PTR_FROM_C_OBJECT(local_addr)->asStringUriOnly().c_str()); buf = sqlite3_mprintf( "SELECT * FROM call_history WHERE " "(caller LIKE '%%%q%%' AND callee LIKE '%%%q%%' AND direction = 0) OR " "(caller LIKE '%%%q%%' AND callee LIKE '%%%q%%' AND direction = 1) " "ORDER BY id DESC", local_addr_str, peer_addr_str, peer_addr_str, local_addr_str ); clsres.core = lc; clsres.result = NULL; begin = ortp_get_cur_time_ms(); linphone_sql_request_call_log(lc->logs_db, buf, &clsres); end = ortp_get_cur_time_ms(); bctbx_message("%s(): completed in %i ms", __FUNCTION__, (int)(end - begin)); sqlite3_free(buf); bctbx_free(peer_addr_str); bctbx_free(local_addr_str); return clsres.result; } LinphoneCallLog * linphone_core_get_last_outgoing_call_log(LinphoneCore *lc) { char *buf; uint64_t begin,end; CallLogStorageResult clsres; LinphoneCallLog *result = NULL; if (!lc || lc->logs_db == NULL) return NULL; /*since we want to append query parameters depending on arguments given, we use malloc instead of sqlite3_mprintf*/ buf = sqlite3_mprintf("SELECT * FROM call_history WHERE direction = 0 ORDER BY id DESC LIMIT 1"); clsres.core = lc; clsres.result = NULL; begin = ortp_get_cur_time_ms(); linphone_sql_request_call_log(lc->logs_db, buf, &clsres); end = ortp_get_cur_time_ms(); ms_message("%s(): completed in %i ms",__FUNCTION__, (int)(end-begin)); sqlite3_free(buf); if (clsres.result != NULL) { result = (LinphoneCallLog *)bctbx_list_get_data(clsres.result); } return result; } LinphoneCallLog * linphone_core_find_call_log_from_call_id(LinphoneCore *lc, const char *call_id) { char *buf; uint64_t begin,end; CallLogStorageResult clsres; LinphoneCallLog* result = NULL; if (!lc || lc->logs_db == NULL) return NULL; /*since we want to append query parameters depending on arguments given, we use malloc instead of sqlite3_mprintf*/ buf = sqlite3_mprintf("SELECT * FROM call_history WHERE call_id = '%q' ORDER BY id DESC LIMIT 1", call_id); clsres.core = lc; clsres.result = NULL; begin = ortp_get_cur_time_ms(); linphone_sql_request_call_log(lc->logs_db, buf, &clsres); end = ortp_get_cur_time_ms(); ms_message("%s(): completed in %i ms",__FUNCTION__, (int)(end-begin)); sqlite3_free(buf); if (clsres.result != NULL) { result = (LinphoneCallLog *)bctbx_list_get_data(clsres.result); } return result; } liblinphone-4.4.21/coreapi/callbacks.c000066400000000000000000001073161376572216100176340ustar00rootroot00000000000000/* * Copyright (c) 2010-2019 Belledonne Communications SARL. * * This file is part of Liblinphone. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "c-wrapper/internal/c-sal.h" #include "sal/call-op.h" #include "sal/message-op.h" #include "sal/refer-op.h" #include "linphone/api/c-content.h" #include "linphone/core.h" #include "linphone/utils/utils.h" #include "private.h" #include "mediastreamer2/mediastream.h" #include "linphone/lpconfig.h" #include // stat #ifndef _WIN32 #include #include #include #endif #include "c-wrapper/c-wrapper.h" #include "call/call-p.h" #include "chat/chat-message/chat-message-p.h" #include "chat/chat-room/chat-room.h" #ifdef HAVE_ADVANCED_IM #include "chat/chat-room/client-group-chat-room-p.h" #include "chat/chat-room/server-group-chat-room-p.h" #endif #include "conference/participant.h" #include "conference/session/call-session-p.h" #include "conference/session/call-session.h" #include "conference/session/media-session-p.h" #include "conference/session/media-session.h" #include "core/core-p.h" using namespace std; using namespace LinphonePrivate; static void register_failure(SalOp *op); bool_t check_core_state(LinphoneCore *lc, SalOp *op) { if (linphone_core_get_global_state(lc) != LinphoneGlobalOn) { ms_warning("Linphone core global state is not on. Please check if the core is started."); op->replyMessage(SalReasonServiceUnavailable); op->release(); return FALSE; } return TRUE; } static void call_received(SalCallOp *h) { LinphoneCore *lc = reinterpret_cast(h->getSal()->getUserPointer()); if (!check_core_state(lc, h)) return; /* Look if this INVITE is for a call that has already been notified but broken because of network failure */ if (L_GET_PRIVATE_FROM_C_OBJECT(lc)->inviteReplacesABrokenCall(h)) return; LinphoneAddress *fromAddr = nullptr; const char *pAssertedId = sal_custom_header_find(h->getRecvCustomHeaders(), "P-Asserted-Identity"); /* In some situation, better to trust the network rather than the UAC */ if (lp_config_get_int(linphone_core_get_config(lc), "sip", "call_logs_use_asserted_id_instead_of_from", 0)) { if (pAssertedId) { LinphoneAddress *pAssertedIdAddr = linphone_address_new(pAssertedId); if (pAssertedIdAddr) { ms_message("Using P-Asserted-Identity [%s] instead of from [%s] for op [%p]", pAssertedId, h->getFrom().c_str(), h); fromAddr = pAssertedIdAddr; } else{ ms_warning("Unsupported P-Asserted-Identity header for op [%p] ", h); } } else{ ms_warning("No P-Asserted-Identity header found so cannot use it for op [%p] instead of from", h); } } if (!fromAddr) fromAddr = linphone_address_new(h->getFrom().c_str()); LinphoneAddress *toAddr = linphone_address_new(h->getTo().c_str()); if (_linphone_core_is_conference_creation(lc, toAddr)) { #ifdef HAVE_ADVANCED_IM linphone_address_unref(toAddr); linphone_address_unref(fromAddr); if (sal_address_has_param(h->getRemoteContactAddress(), "text")) { string oneToOneChatRoom = L_C_TO_STRING(sal_custom_header_find(h->getRecvCustomHeaders(), "One-To-One-Chat-Room")); if (oneToOneChatRoom == "true") { bool_t oneToOneChatRoomEnabled = linphone_config_get_bool(linphone_core_get_config(lc), "misc", "enable_one_to_one_chat_room", TRUE); if (!oneToOneChatRoomEnabled) { h->decline(SalReasonNotAcceptable); h->release(); return; } IdentityAddress from(h->getFrom()); list identAddresses = ServerGroupChatRoom::parseResourceLists(h->getRemoteBody()); if (identAddresses.size() != 1) { h->decline(SalReasonNotAcceptable); h->release(); return; } const char *endToEndEncryptedStr = sal_custom_header_find(h->getRecvCustomHeaders(), "End-To-End-Encrypted"); bool encrypted = endToEndEncryptedStr && strcmp(endToEndEncryptedStr, "true") == 0; IdentityAddress confAddr = L_GET_PRIVATE_FROM_C_OBJECT(lc)->mainDb->findOneToOneConferenceChatRoomAddress(from, identAddresses.front(), encrypted); if (confAddr.isValid()) { shared_ptr chatRoom = L_GET_CPP_PTR_FROM_C_OBJECT(lc)->findChatRoom(ConferenceId(confAddr, confAddr)); L_GET_PRIVATE(static_pointer_cast(chatRoom))->confirmRecreation(h); return; } } _linphone_core_create_server_group_chat_room(lc, h); } // TODO: handle media conference creation if the "text" feature tag is not present return; #else ms_warning("Advanced IM such as group chat is disabled!"); return; #endif } if (sal_address_has_param(h->getRemoteContactAddress(), "text")) { #ifdef HAVE_ADVANCED_IM linphone_address_unref(toAddr); linphone_address_unref(fromAddr); if (linphone_core_conference_server_enabled(lc)) { shared_ptr chatRoom = L_GET_CPP_PTR_FROM_C_OBJECT(lc)->findChatRoom( ConferenceId(IdentityAddress(h->getTo()), IdentityAddress(h->getTo())) ); if (chatRoom) { L_GET_PRIVATE(static_pointer_cast(chatRoom))->confirmJoining(h); } else { //invite is for an unknown chatroom h->decline(SalReasonNotFound); h->release(); } } else { shared_ptr chatRoom = L_GET_CPP_PTR_FROM_C_OBJECT(lc)->findChatRoom( ConferenceId(IdentityAddress(h->getFrom()), IdentityAddress(h->getTo())) ); if (chatRoom && chatRoom->getCapabilities() & ChatRoom::Capabilities::Basic) { lError() << "Invalid basic chat room found. It should have been a ClientGroupChatRoom... Recreating it..."; chatRoom->deleteFromDb(); chatRoom.reset(); } if (!chatRoom) { string endToEndEncrypted = L_C_TO_STRING(sal_custom_header_find(h->getRecvCustomHeaders(), "End-To-End-Encrypted")); chatRoom = L_GET_PRIVATE_FROM_C_OBJECT(lc)->createClientGroupChatRoom( h->getSubject(), ConferenceId(IdentityAddress(h->getRemoteContact()), IdentityAddress(h->getTo())), h->getRemoteBody(), endToEndEncrypted == "true" ); } const char *oneToOneChatRoomStr = sal_custom_header_find(h->getRecvCustomHeaders(), "One-To-One-Chat-Room"); if (oneToOneChatRoomStr && (strcmp(oneToOneChatRoomStr, "true") == 0)) L_GET_PRIVATE(static_pointer_cast(chatRoom))->addOneToOneCapability(); L_GET_PRIVATE(static_pointer_cast(chatRoom))->confirmJoining(h); } return; #else ms_warning("Advanced IM such as group chat is disabled!"); return; #endif } else { // TODO: handle media conference joining if the "text" feature tag is not present } /* First check if we can answer successfully to this invite */ LinphonePresenceActivity *activity = nullptr; if ((linphone_presence_model_get_basic_status(lc->presence_model) == LinphonePresenceBasicStatusClosed) && (activity = linphone_presence_model_get_activity(lc->presence_model))) { char *altContact = nullptr; switch (linphone_presence_activity_get_type(activity)) { case LinphonePresenceActivityPermanentAbsence: altContact = linphone_presence_model_get_contact(lc->presence_model); if (altContact) { SalErrorInfo sei; memset(&sei, 0, sizeof(sei)); sal_error_info_set(&sei, SalReasonRedirect, "SIP", 0, nullptr, nullptr); SalAddress *altAddr = sal_address_new(altContact); h->declineWithErrorInfo(&sei, altAddr); ms_free(altContact); sal_address_unref(altAddr); LinphoneErrorInfo *ei = linphone_error_info_new(); linphone_error_info_set(ei, nullptr, LinphoneReasonMovedPermanently, 302, "Moved permanently", nullptr); linphone_core_report_early_failed_call(lc, LinphoneCallIncoming, fromAddr, toAddr, ei, L_STRING_TO_C(h->getCallId())); h->release(); sal_error_info_reset(&sei); return; } break; default: /* Nothing special to be done */ break; } } if (!L_GET_PRIVATE_FROM_C_OBJECT(lc)->canWeAddCall()) { /* Busy */ h->decline(SalReasonBusy); LinphoneErrorInfo *ei = linphone_error_info_new(); linphone_error_info_set(ei, nullptr, LinphoneReasonBusy, 486, "Busy - too many calls", nullptr); linphone_core_report_early_failed_call(lc, LinphoneCallIncoming, fromAddr, toAddr, ei, L_STRING_TO_C(h->getCallId())); h->release(); return; } if (lp_config_get_int(linphone_core_get_config(lc), "sip", "reject_duplicated_calls", 1)){ /* Check if I'm the caller */ LinphoneAddress *fromAddressToSearchIfMe = nullptr; if (h->getPrivacy() == SalPrivacyNone) fromAddressToSearchIfMe = linphone_address_clone(fromAddr); else if (pAssertedId) fromAddressToSearchIfMe = linphone_address_new(pAssertedId); else ms_warning("Hidden from identity, don't know if it's me"); if (fromAddressToSearchIfMe && L_GET_PRIVATE_FROM_C_OBJECT(lc)->isAlreadyInCallWithAddress(*L_GET_CPP_PTR_FROM_C_OBJECT(fromAddressToSearchIfMe))) { char *addr = linphone_address_as_string(fromAddr); ms_warning("Receiving a call while one with same address [%s] is initiated, refusing this one with busy message", addr); h->decline(SalReasonBusy); LinphoneErrorInfo *ei = linphone_error_info_new(); linphone_error_info_set(ei, nullptr, LinphoneReasonBusy, 486, "Busy - duplicated call", nullptr); linphone_core_report_early_failed_call(lc, LinphoneCallIncoming, fromAddr, toAddr, ei, L_STRING_TO_C(h->getCallId())); h->release(); linphone_address_unref(fromAddressToSearchIfMe); ms_free(addr); return; } if (fromAddressToSearchIfMe) linphone_address_unref(fromAddressToSearchIfMe); } LinphoneCall *call = linphone_call_new_incoming(lc, fromAddr, toAddr, h); linphone_address_unref(fromAddr); linphone_address_unref(toAddr); L_GET_PRIVATE_FROM_C_OBJECT(call)->startIncomingNotification(); } static void call_rejected(SalCallOp *h){ LinphoneCore *lc = reinterpret_cast(h->getSal()->getUserPointer()); LinphoneErrorInfo *ei = linphone_error_info_new(); linphone_error_info_from_sal_op(ei, h); linphone_core_report_early_failed_call(lc, LinphoneCallIncoming, linphone_address_new(h->getFrom().c_str()), linphone_address_new(h->getTo().c_str()), ei, L_STRING_TO_C(h->getCallId())); } static void call_ringing(SalOp *h) { LinphonePrivate::CallSession *session = reinterpret_cast(h->getUserPointer()); if (!session) return; auto sessionRef = session->getSharedFromThis(); L_GET_PRIVATE(sessionRef)->remoteRinging(); } /* * could be reach : * - when the call is accepted * - when a request is accepted (pause, resume) */ static void call_accepted(SalOp *op) { LinphonePrivate::CallSession *session = reinterpret_cast(op->getUserPointer()); if (!session) { ms_warning("call_accepted: CallSession no longer exists"); return; } auto sessionRef = session->getSharedFromThis(); L_GET_PRIVATE(sessionRef)->accepted(); } static void call_refreshed(SalOp *op) { LinphonePrivate::CallSession *session = reinterpret_cast(op->getUserPointer()); if (!session) { ms_warning("call_updating: CallSession no longer exists"); return; } auto sessionRef = session->getSharedFromThis(); L_GET_PRIVATE(sessionRef)->setState(CallSession::State::UpdatedByRemote, "Session refresh"); L_GET_PRIVATE(sessionRef)->setState(CallSession::State::StreamsRunning, "Session refresh"); } /* Used to set the CallSession state to Updating */ static void call_refreshing(SalOp *op) { LinphonePrivate::CallSession *session = reinterpret_cast(op->getUserPointer()); if (!session) { ms_warning("call_updating: CallSession no longer exists"); return; } auto sessionRef = session->getSharedFromThis(); L_GET_PRIVATE(sessionRef)->setState(CallSession::State::Updating, "Session refreshing"); } /* this callback is called when an incoming re-INVITE/ SIP UPDATE modifies the session*/ static void call_updating(SalOp *op, bool_t is_update) { LinphonePrivate::CallSession *session = reinterpret_cast(op->getUserPointer()); if (!session) { ms_warning("call_updating: CallSession no longer exists"); return; } auto sessionRef = session->getSharedFromThis(); L_GET_PRIVATE(sessionRef)->updating(!!is_update); } static void call_ack_received(SalOp *op, SalCustomHeader *ack) { LinphonePrivate::CallSession *session = reinterpret_cast(op->getUserPointer()); if (!session) { ms_warning("call_ack_received(): no CallSession for which an ack is expected"); return; } auto sessionRef = session->getSharedFromThis(); L_GET_PRIVATE(sessionRef)->ackReceived(reinterpret_cast(ack)); } static void call_ack_being_sent(SalOp *op, SalCustomHeader *ack) { LinphonePrivate::CallSession *session = reinterpret_cast(op->getUserPointer()); if (!session) { ms_warning("call_ack_being_sent(): no CallSession for which an ack is supposed to be sent"); return; } auto sessionRef = session->getSharedFromThis(); L_GET_PRIVATE(sessionRef)->ackBeingSent(reinterpret_cast(ack)); } static void call_terminated(SalOp *op, const char *from) { LinphonePrivate::CallSession *session = reinterpret_cast(op->getUserPointer()); if (!session) return; auto sessionRef = session->getSharedFromThis(); L_GET_PRIVATE(sessionRef)->terminated(); } static void call_failure(SalOp *op) { LinphonePrivate::CallSession *session = reinterpret_cast(op->getUserPointer()); if (!session) { ms_warning("Failure reported on already terminated CallSession"); return; } auto sessionRef = session->getSharedFromThis(); L_GET_PRIVATE(sessionRef)->failure(); } static void call_released(SalOp *op) { LinphonePrivate::CallSession *session = reinterpret_cast(op->getUserPointer()); if (!session) { /* We can get here when the core manages call at Sal level without creating a Call object. Typicially, * when declining an incoming call with busy because maximum number of calls is reached. */ return; } auto sessionRef = session->getSharedFromThis(); L_GET_PRIVATE(sessionRef)->setState(LinphonePrivate::CallSession::State::Released, "Call released"); } static void call_cancel_done(SalOp *op) { LinphonePrivate::CallSession *session = reinterpret_cast(op->getUserPointer()); if (!session) { ms_warning("Cancel done reported on already terminated CallSession"); return; } auto sessionRef = session->getSharedFromThis(); L_GET_PRIVATE(sessionRef)->cancelDone(); } static void auth_failure(SalOp *op, SalAuthInfo* info) { LinphoneCore *lc = reinterpret_cast(op->getSal()->getUserPointer()); LinphoneAuthInfo *ai = NULL; if (info != NULL) { ai = (LinphoneAuthInfo*)_linphone_core_find_auth_info(lc, info->realm, info->username, info->domain, info->algorithm, TRUE); if (ai){ LinphoneAuthMethod method = info->mode == SalAuthModeHttpDigest ? LinphoneAuthHttpDigest : LinphoneAuthTls; LinphoneAuthInfo *auth_info = linphone_core_create_auth_info(lc, info->username, NULL, NULL, NULL, info->realm, info->domain); ms_message("%s/%s/%s/%s authentication fails.", info->realm, info->username, info->domain, info->mode == SalAuthModeHttpDigest ? "HttpDigest" : "Tls"); if (method == LinphoneAuthHttpDigest){ /*ask again for password if auth info was already supplied but apparently not working*/ L_GET_PRIVATE_FROM_C_OBJECT(lc)->getAuthStack().pushAuthRequested(AuthInfo::toCpp(ai)->getSharedFromThis()); }else{ linphone_core_notify_authentication_requested(lc, auth_info, method); // Deprecated linphone_core_notify_auth_info_requested(lc, info->realm, info->username, info->domain); } linphone_auth_info_unref(auth_info); } } } static void register_success(SalOp *op, bool_t registered){ LinphoneProxyConfig *cfg=(LinphoneProxyConfig *)op->getUserPointer(); if (!cfg){ ms_message("Registration success for deleted proxy config, ignored"); return; } linphone_proxy_config_set_state(cfg, registered ? LinphoneRegistrationOk : LinphoneRegistrationCleared , registered ? "Registration successful" : "Unregistration done"); } static void register_failure(SalOp *op){ LinphoneProxyConfig *cfg=(LinphoneProxyConfig*)op->getUserPointer(); const SalErrorInfo *ei=op->getErrorInfo(); const char *details=ei->full_string; if (cfg==NULL){ ms_warning("Registration failed for unknown proxy config."); return ; } if (details==NULL) details="no response timeout"; if ((ei->reason == SalReasonServiceUnavailable || ei->reason == SalReasonIOError) && linphone_proxy_config_get_state(cfg) == LinphoneRegistrationOk) { linphone_proxy_config_set_state(cfg,LinphoneRegistrationProgress,"Service unavailable, retrying"); } else if (ei->protocol_code == 401 || ei->protocol_code == 407){ /* Do nothing. There will be an auth_requested() callback. If the callback doesn't provide an AuthInfo, then * the proxy config will transition to the failed state.*/ } else { linphone_proxy_config_set_state(cfg,LinphoneRegistrationFailed, details); } if (cfg->presence_publish_event){ /*prevent publish to be sent now until registration gets successful*/ linphone_event_terminate(cfg->presence_publish_event); cfg->presence_publish_event=NULL; cfg->send_publish=cfg->publish; } } static void vfu_request(SalOp *op) { LinphonePrivate::CallSession *session = reinterpret_cast(op->getUserPointer()); if (!session) return; auto sessionRef = session->getSharedFromThis(); auto mediaSessionRef = dynamic_pointer_cast(sessionRef); if (!mediaSessionRef) { ms_warning("VFU request but no MediaSession!"); return; } L_GET_PRIVATE(mediaSessionRef)->sendVfu(); } static void dtmf_received(SalOp *op, char dtmf) { LinphonePrivate::CallSession *session = reinterpret_cast(op->getUserPointer()); if (!session) return; auto sessionRef = session->getSharedFromThis(); auto mediaSessionRef = dynamic_pointer_cast(sessionRef); if (!mediaSessionRef) { ms_warning("DTMF received but no MediaSession!"); return; } L_GET_PRIVATE(mediaSessionRef)->dtmfReceived(dtmf); } static void call_refer_received(SalOp *op, const SalAddress *referTo) { LinphonePrivate::CallSession *session = reinterpret_cast(op->getUserPointer()); char *addrStr = sal_address_as_string_uri_only(referTo); Address referToAddr(addrStr); string method; if (referToAddr.isValid()) method = referToAddr.getMethodParam(); if (session && (method.empty() || (method == "INVITE"))) { auto sessionRef = session->getSharedFromThis(); L_GET_PRIVATE(sessionRef)->referred(referToAddr); } else { LinphoneCore *lc = reinterpret_cast(op->getSal()->getUserPointer()); linphone_core_notify_refer_received(lc, addrStr); } bctbx_free(addrStr); } static void message_received(SalOp *op, const SalMessage *msg){ LinphoneCore *lc=(LinphoneCore *)op->getSal()->getUserPointer(); if (!check_core_state(lc, op)) return; LinphoneCall *call=(LinphoneCall*)op->getUserPointer(); LinphoneReason reason = lc->chat_deny_code; if (reason == LinphoneReasonNone) { reason = linphone_core_message_received(lc, op, msg); } auto messageOp = dynamic_cast(op); messageOp->reply(linphone_reason_to_sal(reason)); if (!call) op->release(); } static void parse_presence_requested(SalOp *op, const char *content_type, const char *content_subtype, const char *body, SalPresenceModel **result) { linphone_notify_parse_presence(content_type, content_subtype, body, result); } static void convert_presence_to_xml_requested(SalOp *op, SalPresenceModel *presence, const char *contact, char **content) { /*for backward compatibility because still used by notify. No loguer used for publish*/ if(linphone_presence_model_get_presentity((LinphonePresenceModel*)presence) == NULL) { LinphoneAddress * presentity = linphone_address_new(contact); linphone_presence_model_set_presentity((LinphonePresenceModel*)presence, presentity); linphone_address_unref(presentity); } *content = linphone_presence_model_to_xml((LinphonePresenceModel*)presence); } static void notify_presence(SalOp *op, SalSubscribeStatus ss, SalPresenceModel *model, const char *msg){ LinphoneCore *lc=(LinphoneCore *)op->getSal()->getUserPointer(); linphone_notify_recv(lc,op,ss,model); } static void subscribe_presence_received(SalPresenceOp *op, const char *from){ LinphoneCore *lc=(LinphoneCore *)op->getSal()->getUserPointer(); if (!check_core_state(lc, op)) return; linphone_subscription_new(lc,op,from); } static void subscribe_presence_closed(SalPresenceOp *op, const char *from){ LinphoneCore *lc=(LinphoneCore *)op->getSal()->getUserPointer(); linphone_subscription_closed(lc,op); } static void ping_reply(SalOp *op) { LinphonePrivate::CallSession *session = reinterpret_cast(op->getUserPointer()); if (!session) { ms_warning("Ping reply without CallSession attached..."); return; } auto sessionRef = session->getSharedFromThis(); L_GET_PRIVATE(sessionRef)->pingReply(); } static bool_t fill_auth_info_with_client_certificate(LinphoneCore *lc, SalAuthInfo* sai) { const char *chain_file = linphone_core_get_tls_cert_path(lc); const char *key_file = linphone_core_get_tls_key_path(lc); if (key_file && chain_file) { #ifndef _WIN32 // optinal check for files struct stat st; if (stat(key_file, &st)) { ms_warning("No client certificate key found in %s", key_file); return FALSE; } if (stat(chain_file, &st)) { ms_warning("No client certificate chain found in %s", chain_file); return FALSE; } #endif sal_certificates_chain_parse_file(sai, chain_file, SAL_CERTIFICATE_RAW_FORMAT_PEM); sal_signing_key_parse_file(sai, key_file, ""); } else if (lc->tls_cert && lc->tls_key) { sal_certificates_chain_parse(sai, lc->tls_cert, SAL_CERTIFICATE_RAW_FORMAT_PEM); sal_signing_key_parse(sai, lc->tls_key, ""); } return sai->certificates && sai->key; } static bool_t fill_auth_info(LinphoneCore *lc, SalAuthInfo* sai) { LinphoneAuthInfo *ai = NULL; if (sai->mode == SalAuthModeTls) { ai = (LinphoneAuthInfo*)_linphone_core_find_tls_auth_info(lc); } else { ai = (LinphoneAuthInfo*)_linphone_core_find_auth_info(lc,sai->realm,sai->username,sai->domain, sai->algorithm, FALSE); } if (ai) { if (sai->mode == SalAuthModeHttpDigest) { sai->userid = ms_strdup(linphone_auth_info_get_userid(ai) ? linphone_auth_info_get_userid(ai) : linphone_auth_info_get_username(ai)); sai->password = linphone_auth_info_get_passwd(ai)?ms_strdup(linphone_auth_info_get_passwd(ai)) : NULL; sai->ha1 = linphone_auth_info_get_ha1(ai) ? ms_strdup(linphone_auth_info_get_ha1(ai)) : NULL; AuthStack & as = L_GET_PRIVATE_FROM_C_OBJECT(lc)->getAuthStack(); /* We have to construct the auth info as it was originally requested in auth_requested() below, * so that the matching is made correctly. */ as.authFound(AuthInfo::create(sai->username, "", "", "", sai->realm, sai->domain)); } else if (sai->mode == SalAuthModeTls) { if (linphone_auth_info_get_tls_cert(ai) && linphone_auth_info_get_tls_key(ai)) { sal_certificates_chain_parse(sai, linphone_auth_info_get_tls_cert(ai), SAL_CERTIFICATE_RAW_FORMAT_PEM); sal_signing_key_parse(sai, linphone_auth_info_get_tls_key(ai), ""); } else if (linphone_auth_info_get_tls_cert_path(ai) && linphone_auth_info_get_tls_key_path(ai)) { sal_certificates_chain_parse_file(sai, linphone_auth_info_get_tls_cert_path(ai), SAL_CERTIFICATE_RAW_FORMAT_PEM); sal_signing_key_parse_file(sai, linphone_auth_info_get_tls_key_path(ai), ""); } else { fill_auth_info_with_client_certificate(lc, sai); } } if (sai->realm && (!linphone_auth_info_get_realm(ai) || !linphone_auth_info_get_algorithm(ai))) { /*if realm was not known, then set it so that ha1 may eventually be calculated and clear text password dropped*/ linphone_auth_info_set_realm(ai, sai->realm); linphone_auth_info_set_algorithm(ai, sai->algorithm); linphone_core_write_auth_info(lc, ai); } return TRUE; } else { if (sai->mode == SalAuthModeTls) { return fill_auth_info_with_client_certificate(lc, sai); } return FALSE; } } static bool_t auth_requested(Sal* sal, SalAuthInfo* sai) { LinphoneCore *lc = (LinphoneCore *)sal->getUserPointer(); if (fill_auth_info(lc,sai)) { return TRUE; } else { LinphoneAuthMethod method = sai->mode == SalAuthModeHttpDigest ? LinphoneAuthHttpDigest : LinphoneAuthTls; LinphoneAuthInfo *ai = linphone_core_create_auth_info(lc, sai->username, NULL, NULL, NULL, sai->realm, sai->domain); if (method == LinphoneAuthHttpDigest){ /* Request app for new authentication information, but later. */ L_GET_PRIVATE_FROM_C_OBJECT(lc)->getAuthStack().pushAuthRequested(AuthInfo::toCpp(ai)->getSharedFromThis()); }else{ linphone_core_notify_authentication_requested(lc, ai, method); // Deprecated callback linphone_core_notify_auth_info_requested(lc, sai->realm, sai->username, sai->domain); } linphone_auth_info_unref(ai); if (fill_auth_info(lc, sai)) { return TRUE; } return FALSE; } } static void notify_refer(SalOp *op, SalReferStatus status) { LinphonePrivate::CallSession *session = reinterpret_cast(op->getUserPointer()); if (!session) { ms_warning("Receiving notify_refer for unknown CallSession"); return; } auto sessionRef = session->getSharedFromThis(); LinphonePrivate::CallSession::State cstate; switch (status) { case SalReferTrying: cstate = LinphonePrivate::CallSession::State::OutgoingProgress; break; case SalReferSuccess: cstate = LinphonePrivate::CallSession::State::Connected; break; case SalReferFailed: default: cstate = LinphonePrivate::CallSession::State::Error; break; } L_GET_PRIVATE(sessionRef)->setTransferState(cstate); if (cstate == LinphonePrivate::CallSession::State::Connected) sessionRef->terminate(); // Automatically terminate the call as the transfer is complete } static LinphoneChatMessageState chatStatusSal2Linphone(SalMessageDeliveryStatus status){ switch(status){ case SalMessageDeliveryInProgress: return LinphoneChatMessageStateInProgress; case SalMessageDeliveryDone: return LinphoneChatMessageStateDelivered; case SalMessageDeliveryFailed: return LinphoneChatMessageStateNotDelivered; } return LinphoneChatMessageStateIdle; } static void message_delivery_update(SalOp *op, SalMessageDeliveryStatus status) { auto lc = reinterpret_cast(op->getSal()->getUserPointer()); if (linphone_core_get_global_state(lc) != LinphoneGlobalOn && linphone_core_get_global_state(lc) != LinphoneGlobalShutdown) { static_cast(op)->reply(SalReasonDeclined); return; } LinphonePrivate::ChatMessage *msg = reinterpret_cast(op->getUserPointer()); if (!msg) return; // Do not handle delivery status for isComposing messages. // Check that the message does not belong to an already destroyed chat room - if so, do not invoke callbacks if (msg->getChatRoom()) L_GET_PRIVATE(msg)->setState((LinphonePrivate::ChatMessage::State)chatStatusSal2Linphone(status)); } static void info_received(SalOp *op, SalBodyHandler *body_handler) { LinphonePrivate::CallSession *session = reinterpret_cast(op->getUserPointer()); if (!session) return; auto sessionRef = session->getSharedFromThis(); L_GET_PRIVATE(sessionRef)->infoReceived(body_handler); } static void subscribe_response(SalOp *op, SalSubscribeStatus status, int will_retry){ LinphoneEvent *lev=(LinphoneEvent*)op->getUserPointer(); LinphoneCore *lc=(LinphoneCore *)op->getSal()->getUserPointer(); if (lev==NULL) return; if (status==SalSubscribeActive){ linphone_event_set_state(lev,LinphoneSubscriptionActive); }else if (status==SalSubscribePending){ linphone_event_set_state(lev,LinphoneSubscriptionPending); }else{ if (will_retry && linphone_core_get_global_state(lc) != LinphoneGlobalShutdown ){ linphone_event_set_state(lev,LinphoneSubscriptionOutgoingProgress); } else linphone_event_set_state(lev,LinphoneSubscriptionError); } } static void notify(SalSubscribeOp *op, SalSubscribeStatus st, const char *eventname, SalBodyHandler *body_handler){ LinphoneEvent *lev=(LinphoneEvent*)op->getUserPointer(); LinphoneCore *lc=(LinphoneCore *)op->getSal()->getUserPointer(); bool_t out_of_dialog = (lev==NULL); if (out_of_dialog) { /*out of dialog notify */ lev = linphone_event_new_with_out_of_dialog_op(lc,op,LinphoneSubscriptionOutgoing,eventname); } { LinphoneContent *ct = linphone_content_from_sal_body_handler(body_handler); if (ct) { linphone_core_notify_notify_received(lc,lev,eventname,ct); linphone_content_unref(ct); } } if (out_of_dialog){ /*out of dialog NOTIFY do not create an implicit subscription*/ linphone_event_set_state(lev, LinphoneSubscriptionTerminated); }else if (st!=SalSubscribeNone){ linphone_event_set_state(lev,linphone_subscription_state_from_sal(st)); } } static void subscribe_received(SalSubscribeOp *op, const char *eventname, const SalBodyHandler *body_handler){ LinphoneEvent *lev=(LinphoneEvent*)op->getUserPointer(); LinphoneCore *lc=(LinphoneCore *)op->getSal()->getUserPointer(); if (!check_core_state(lc, op)) return; if (lev==NULL) { lev=linphone_event_new_with_op(lc,op,LinphoneSubscriptionIncoming,eventname); linphone_event_set_state(lev,LinphoneSubscriptionIncomingReceived); LinphoneContent *ct = linphone_content_from_sal_body_handler(body_handler); linphone_core_notify_subscribe_received(lc,lev,eventname,ct); if (ct) linphone_content_unref(ct); } else { /*subscribe refresh, unhandled*/ } } static void incoming_subscribe_closed(SalOp *op){ LinphoneEvent *lev=(LinphoneEvent*)op->getUserPointer(); linphone_event_set_state(lev,LinphoneSubscriptionTerminated); } static void on_publish_response(SalOp* op){ LinphoneEvent *lev=(LinphoneEvent*)op->getUserPointer(); const SalErrorInfo *ei=op->getErrorInfo(); if (lev==NULL) return; if (ei->reason==SalReasonNone){ if (!lev->terminating) linphone_event_set_publish_state(lev,LinphonePublishOk); else linphone_event_set_publish_state(lev,LinphonePublishCleared); }else{ if (lev->publish_state==LinphonePublishOk){ linphone_event_set_publish_state(lev,LinphonePublishProgress); }else{ linphone_event_set_publish_state(lev,LinphonePublishError); } } } static void on_expire(SalOp *op){ LinphoneEvent *lev=(LinphoneEvent*)op->getUserPointer(); if (lev==NULL) return; if (linphone_event_get_publish_state(lev)==LinphonePublishOk){ linphone_event_set_publish_state(lev,LinphonePublishExpiring); }else if (linphone_event_get_subscription_state(lev)==LinphoneSubscriptionActive){ linphone_event_set_state(lev,LinphoneSubscriptionExpiring); } } static void on_notify_response(SalOp *op){ LinphoneEvent *lev=(LinphoneEvent*)op->getUserPointer(); if (!lev) return; if (lev->is_out_of_dialog_op) { switch (linphone_event_get_subscription_state(lev)) { case LinphoneSubscriptionIncomingReceived: if (op->getErrorInfo()->reason == SalReasonNone) linphone_event_set_state(lev, LinphoneSubscriptionTerminated); else linphone_event_set_state(lev, LinphoneSubscriptionError); break; default: ms_warning("Unhandled on_notify_response() case %s", linphone_subscription_state_to_string(linphone_event_get_subscription_state(lev))); break; } } else { ms_warning("on_notify_response in dialog"); _linphone_event_notify_notify_response(lev); } } static void refer_received(SalOp *op, const SalAddress *refer_to){ if (sal_address_has_param(refer_to, "text")) { char *refer_uri = sal_address_as_string(refer_to); LinphonePrivate::Address addr(refer_uri); bctbx_free(refer_uri); if (addr.isValid()) { LinphoneCore *lc = reinterpret_cast(op->getSal()->getUserPointer()); if (linphone_core_get_global_state(lc) != LinphoneGlobalOn) { static_cast(op)->reply(SalReasonDeclined); return; } if (addr.hasUriParam("method") && (addr.getUriParamValue("method") == "BYE")) { if (linphone_core_conference_server_enabled(lc)) { // Removal of a participant at the server side shared_ptr chatRoom = L_GET_CPP_PTR_FROM_C_OBJECT(lc)->findChatRoom( ConferenceId(IdentityAddress(op->getTo()), IdentityAddress(op->getTo())) ); if (chatRoom) { std::shared_ptr participant = chatRoom->findParticipant(IdentityAddress(op->getFrom())); if (!participant || !participant->isAdmin()) { static_cast(op)->reply(SalReasonDeclined); return; } participant = chatRoom->findParticipant(addr); if (participant) chatRoom->removeParticipant(participant); static_cast(op)->reply(SalReasonNone); return; } } else { // The server asks a participant to leave a chat room LinphoneChatRoom *cr = L_GET_C_BACK_PTR( L_GET_CPP_PTR_FROM_C_OBJECT(lc)->findChatRoom(ConferenceId(addr, IdentityAddress(op->getTo()))) ); if (cr) { L_GET_CPP_PTR_FROM_C_OBJECT(cr)->leave(); static_cast(op)->reply(SalReasonNone); return; } static_cast(op)->reply(SalReasonDeclined); } } else { if (linphone_core_conference_server_enabled(lc)) { #ifdef HAVE_ADVANCED_IM shared_ptr chatRoom = L_GET_CPP_PTR_FROM_C_OBJECT(lc)->findChatRoom( ConferenceId(IdentityAddress(op->getTo()), IdentityAddress(op->getTo())) ); LinphoneChatRoom *cr = L_GET_C_BACK_PTR(chatRoom); if (cr) { Address fromAddr(op->getFrom()); shared_ptr participant = chatRoom->findParticipant(fromAddr); if (!participant || !participant->isAdmin()) { static_cast(op)->reply(SalReasonForbidden); return; } if (addr.hasParam("admin")) { participant = chatRoom->findParticipant(addr); if (participant) { bool value = Utils::stob(addr.getParamValue("admin")); chatRoom->setParticipantAdminStatus(participant, value); static_cast(op)->reply(SalReasonNone); return; } } else { participant = L_GET_PRIVATE(static_pointer_cast(chatRoom))->findAuthorizedParticipant(addr); if (!participant) { bool ret = static_pointer_cast(chatRoom)->addParticipant( IdentityAddress(addr), nullptr, false); static_cast(op)->reply(ret ? SalReasonNone : SalReasonNotAcceptable); return; } } } #else ms_warning("Advanced IM such as group chat is disabled!"); #endif } } } } static_cast(op)->reply(SalReasonDeclined); } Sal::Callbacks linphone_sal_callbacks={ call_received, call_rejected, call_ringing, call_accepted, call_ack_received, call_ack_being_sent, call_updating, call_refreshed, call_refreshing, call_terminated, call_failure, call_released, call_cancel_done, call_refer_received, auth_failure, register_success, register_failure, vfu_request, dtmf_received, message_received, message_delivery_update, notify_refer, subscribe_received, incoming_subscribe_closed, subscribe_response, notify, subscribe_presence_received, subscribe_presence_closed, parse_presence_requested, convert_presence_to_xml_requested, notify_presence, ping_reply, auth_requested, info_received, on_publish_response, on_expire, on_notify_response, refer_received, }; liblinphone-4.4.21/coreapi/carddav.c000066400000000000000000000676131376572216100173260ustar00rootroot00000000000000/* * Copyright (c) 2010-2019 Belledonne Communications SARL. * * This file is part of Liblinphone. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "linphone/core.h" #include "private.h" #include "linphone/api/c-auth-info.h" LinphoneCardDavContext* linphone_carddav_context_new(LinphoneFriendList *lfl) { LinphoneCardDavContext *carddav_context = NULL; if (!linphone_core_vcard_supported()) { ms_error("[carddav] vCard isn't available (maybe it wasn't compiled), can't do CardDAV sync"); return NULL; } if (!lfl || !lfl->uri) { return NULL; } carddav_context = (LinphoneCardDavContext *)ms_new0(LinphoneCardDavContext, 1); carddav_context->friend_list = linphone_friend_list_ref(lfl); return carddav_context; } void linphone_carddav_context_destroy(LinphoneCardDavContext *cdc) { if (cdc) { if (cdc->friend_list) { linphone_friend_list_unref(cdc->friend_list); cdc->friend_list = NULL; } if (cdc->auth_info) { linphone_auth_info_unref(cdc->auth_info); cdc->auth_info = NULL; } ms_free(cdc); } } void linphone_carddav_set_user_data(LinphoneCardDavContext *cdc, void *ud) { cdc->user_data = ud; } void* linphone_carddav_get_user_data(LinphoneCardDavContext *cdc) { return cdc->user_data; } void linphone_carddav_synchronize(LinphoneCardDavContext *cdc) { cdc->ctag = cdc->friend_list->revision; linphone_carddav_get_current_ctag(cdc); } static void linphone_carddav_client_to_server_sync_done(LinphoneCardDavContext *cdc, bool_t success, const char *msg) { if (!success) { ms_error("[carddav] CardDAV client to server sync failure: %s", msg); } if (cdc->sync_done_cb) { cdc->sync_done_cb(cdc, success, msg); } } static void linphone_carddav_server_to_client_sync_done(LinphoneCardDavContext *cdc, bool_t success, const char *msg) { if (success) { ms_debug("CardDAV sync successful, saving new cTag: %i", cdc->ctag); linphone_friend_list_update_revision(cdc->friend_list, cdc->ctag); } else { ms_error("[carddav] CardDAV server to client sync failure: %s", msg); } if (cdc->sync_done_cb) { cdc->sync_done_cb(cdc, success, msg); } } static int find_matching_friend(LinphoneFriend *lf1, LinphoneFriend *lf2) { LinphoneVcard *lvc1 = linphone_friend_get_vcard(lf1); LinphoneVcard *lvc2 = linphone_friend_get_vcard(lf2); const char *uid1 = NULL, *uid2 = NULL; if (!lvc1 || !lvc2) { return 1; } uid1 = linphone_vcard_get_uid(lvc1); uid2 = linphone_vcard_get_uid(lvc2); if (!uid1 || !uid2) { return 1; } return strcmp(uid1, uid2); } static void linphone_carddav_response_free(LinphoneCardDavResponse *response) { if (response->etag) ms_free(response->etag); if (response->url) ms_free(response->url); if (response->vcard) ms_free(response->vcard); ms_free(response); } static void linphone_carddav_vcards_pulled(LinphoneCardDavContext *cdc, bctbx_list_t *vCards) { bctbx_list_t *vCards_remember = vCards; if (vCards != NULL && bctbx_list_size(vCards) > 0) { bctbx_list_t *friends = cdc->friend_list->friends; while (vCards) { LinphoneCardDavResponse *vCard = (LinphoneCardDavResponse *)vCards->data; if (vCard) { LinphoneVcard *lvc = linphone_vcard_context_get_vcard_from_buffer(cdc->friend_list->lc->vcard_context, vCard->vcard); LinphoneFriend *lf = NULL; bctbx_list_t *local_friend = NULL; if (lvc) { // Compute downloaded vCards' URL and save it (+ eTag) char *vCard_name = strrchr(vCard->url, '/'); char full_url[300]; snprintf(full_url, sizeof(full_url), "%s%s", cdc->friend_list->uri, vCard_name); linphone_vcard_set_url(lvc, full_url); linphone_vcard_set_etag(lvc, vCard->etag); ms_debug("Downloaded vCard etag/url are %s and %s", vCard->etag, full_url); lf = linphone_friend_new_from_vcard(lvc); linphone_vcard_unref(lvc); /*ref is now owned by friend*/ if (lf) { local_friend = bctbx_list_find_custom(friends, (int (*)(const void*, const void*))find_matching_friend, lf); if (local_friend) { LinphoneFriend *lf2 = (LinphoneFriend *)local_friend->data; lf->storage_id = lf2->storage_id; lf->pol = lf2->pol; lf->subscribe = lf2->subscribe; lf->refkey = ms_strdup(lf2->refkey); lf->presence_received = lf2->presence_received; lf->lc = lf2->lc; lf->friend_list = lf2->friend_list; if (cdc->contact_updated_cb) { ms_debug("Contact updated: %s", linphone_friend_get_name(lf)); cdc->contact_updated_cb(cdc, lf, lf2); } } else { if (cdc->contact_created_cb) { ms_debug("Contact created: %s", linphone_friend_get_name(lf)); cdc->contact_created_cb(cdc, lf); } } linphone_friend_unref(lf); } else { ms_error("[carddav] Couldn't create a friend from vCard"); } } else { ms_error("[carddav] Couldn't parse vCard %s", vCard->vcard); } } vCards = bctbx_list_next(vCards); } bctbx_list_free_with_data(vCards_remember, (void (*)(void *))linphone_carddav_response_free); } linphone_carddav_server_to_client_sync_done(cdc, TRUE, NULL); } static bctbx_list_t* parse_vcards_from_xml_response(const char *body) { bctbx_list_t *result = NULL; xmlparsing_context_t *xml_ctx = linphone_xmlparsing_context_new(); xmlSetGenericErrorFunc(xml_ctx, linphone_xmlparsing_genericxml_error); xml_ctx->doc = xmlReadDoc((const unsigned char*)body, 0, NULL, 0); if (xml_ctx->doc != NULL) { if (linphone_create_xml_xpath_context(xml_ctx) < 0) goto end; linphone_xml_xpath_context_init_carddav_ns(xml_ctx); { xmlXPathObjectPtr responses = linphone_get_xml_xpath_object_for_node_list(xml_ctx, "/d:multistatus/d:response"); if (responses != NULL && responses->nodesetval != NULL) { xmlNodeSetPtr responses_nodes = responses->nodesetval; if (responses_nodes->nodeNr >= 1) { int i; for (i = 0; i < responses_nodes->nodeNr; i++) { xmlNodePtr response_node = responses_nodes->nodeTab[i]; xml_ctx->xpath_ctx->node = response_node; { char *etag = linphone_get_xml_text_content(xml_ctx, "d:propstat/d:prop/d:getetag"); char *url = linphone_get_xml_text_content(xml_ctx, "d:href"); char *vcard = linphone_get_xml_text_content(xml_ctx, "d:propstat/d:prop/card:address-data"); LinphoneCardDavResponse *response = ms_new0(LinphoneCardDavResponse, 1); response->etag = ms_strdup(etag); response->url = ms_strdup(url); response->vcard = ms_strdup(vcard); result = bctbx_list_append(result, response); ms_debug("Added vCard object with eTag %s, URL %s and vCard %s", etag, url, vcard); linphone_free_xml_text_content(etag); linphone_free_xml_text_content(url); linphone_free_xml_text_content(vcard); } } } xmlXPathFreeObject(responses); } } } end: linphone_xmlparsing_context_destroy(xml_ctx); return result; } static int find_matching_vcard(LinphoneCardDavResponse *response, LinphoneFriend *lf) { if (!response->url || !lf || !lf->vcard || !linphone_vcard_get_url(lf->vcard)) { return 1; } return strcmp(response->url, linphone_vcard_get_url(lf->vcard)); } static void linphone_carddav_vcards_fetched(LinphoneCardDavContext *cdc, bctbx_list_t *vCards) { if (vCards != NULL && bctbx_list_size(vCards) > 0) { bctbx_list_t *friends = cdc->friend_list->friends; bctbx_list_t *friends_to_remove = NULL; bctbx_list_t *temp_list = NULL; while (friends) { LinphoneFriend *lf = (LinphoneFriend *)friends->data; if (lf) { bctbx_list_t *vCard = bctbx_list_find_custom(vCards, (int (*)(const void*, const void*))find_matching_vcard, lf); if (!vCard) { ms_debug("Local friend %s isn't in the remote vCard list, delete it", linphone_friend_get_name(lf)); temp_list = bctbx_list_append(temp_list, linphone_friend_ref(lf)); } else { LinphoneCardDavResponse *response = (LinphoneCardDavResponse *)vCard->data; ms_debug("Local friend %s is in the remote vCard list, check eTag", linphone_friend_get_name(lf)); if (response) { LinphoneVcard *lvc = linphone_friend_get_vcard(lf); const char *etag = linphone_vcard_get_etag(lvc); ms_debug("Local friend eTag is %s, remote vCard eTag is %s", etag, response->etag); if (lvc && etag && strcmp(etag, response->etag) == 0) { bctbx_list_remove(vCards, vCard); linphone_carddav_response_free(response); } } } } friends = bctbx_list_next(friends); } friends_to_remove = temp_list; while(friends_to_remove) { LinphoneFriend *lf = (LinphoneFriend *)friends_to_remove->data; if (lf) { if (cdc->contact_removed_cb) { ms_debug("Contact removed: %s", linphone_friend_get_name(lf)); cdc->contact_removed_cb(cdc, lf); } } friends_to_remove = bctbx_list_next(friends_to_remove); } temp_list = bctbx_list_free_with_data(temp_list, (void (*)(void *))linphone_friend_unref); linphone_carddav_pull_vcards(cdc, vCards); bctbx_list_free_with_data(vCards, (void (*)(void *))linphone_carddav_response_free); } } static bctbx_list_t* parse_vcards_etags_from_xml_response(const char *body) { bctbx_list_t *result = NULL; xmlparsing_context_t *xml_ctx = linphone_xmlparsing_context_new(); xmlSetGenericErrorFunc(xml_ctx, linphone_xmlparsing_genericxml_error); xml_ctx->doc = xmlReadDoc((const unsigned char*)body, 0, NULL, 0); if (xml_ctx->doc != NULL) { if (linphone_create_xml_xpath_context(xml_ctx) < 0) goto end; linphone_xml_xpath_context_init_carddav_ns(xml_ctx); { xmlXPathObjectPtr responses = linphone_get_xml_xpath_object_for_node_list(xml_ctx, "/d:multistatus/d:response"); if (responses != NULL && responses->nodesetval != NULL) { xmlNodeSetPtr responses_nodes = responses->nodesetval; if (responses_nodes->nodeNr >= 1) { int i; for (i = 0; i < responses_nodes->nodeNr; i++) { xmlNodePtr response_node = responses_nodes->nodeTab[i]; xml_ctx->xpath_ctx->node = response_node; { char *etag = linphone_get_xml_text_content(xml_ctx, "d:propstat/d:prop/d:getetag"); char *url = linphone_get_xml_text_content(xml_ctx, "d:href"); LinphoneCardDavResponse *response = ms_new0(LinphoneCardDavResponse, 1); response->etag = ms_strdup(etag); response->url = ms_strdup(url); result = bctbx_list_append(result, response); ms_debug("Added vCard object with eTag %s and URL %s", etag, url); linphone_free_xml_text_content(etag); linphone_free_xml_text_content(url); } } } xmlXPathFreeObject(responses); } } } end: linphone_xmlparsing_context_destroy(xml_ctx); return result; } static void linphone_carddav_ctag_fetched(LinphoneCardDavContext *cdc, int ctag) { ms_debug("Remote cTag for CardDAV addressbook is %i, local one is %i", ctag, cdc->ctag); if (ctag == -1 || ctag > cdc->ctag) { cdc->ctag = ctag; linphone_carddav_fetch_vcards(cdc); } else { ms_message("No changes found on server, skipping sync"); linphone_carddav_server_to_client_sync_done(cdc, TRUE, "Synchronization skipped because cTag already up to date"); } } static int parse_ctag_value_from_xml_response(const char *body) { int result = -1; xmlparsing_context_t *xml_ctx = linphone_xmlparsing_context_new(); xmlSetGenericErrorFunc(xml_ctx, linphone_xmlparsing_genericxml_error); xml_ctx->doc = xmlReadDoc((const unsigned char*)body, 0, NULL, 0); if (xml_ctx->doc != NULL) { char *response = NULL; if (linphone_create_xml_xpath_context(xml_ctx) < 0) goto end; linphone_xml_xpath_context_init_carddav_ns(xml_ctx); response = linphone_get_xml_text_content(xml_ctx, "/d:multistatus/d:response/d:propstat/d:prop/x1:getctag"); if (response) { result = atoi(response); linphone_free_xml_text_content(response); } } end: linphone_xmlparsing_context_destroy(xml_ctx); return result; } static void linphone_carddav_query_free(LinphoneCardDavQuery *query) { if (!query) { return; } if (query->http_request_listener) { belle_sip_object_unref(query->http_request_listener); query->http_request_listener = NULL; } // Context will be freed later (in sync_done) query->context = NULL; if (query->url) { ms_free(query->url); } if (query->body) { ms_free(query->body); } ms_free(query); } static bool_t is_query_client_to_server_sync(LinphoneCardDavQuery *query) { if (!query) { ms_error("[carddav] query is NULL..."); return FALSE; } switch(query->type) { case LinphoneCardDavQueryTypePropfind: case LinphoneCardDavQueryTypeAddressbookQuery: case LinphoneCardDavQueryTypeAddressbookMultiget: return FALSE; case LinphoneCardDavQueryTypePut: case LinphoneCardDavQueryTypeDelete: return TRUE; default: ms_error("[carddav] Unknown request: %i", query->type); } return FALSE; } static void process_response_from_carddav_request(void *data, const belle_http_response_event_t *event) { LinphoneCardDavQuery *query = (LinphoneCardDavQuery *)data; if (event->response) { int code = belle_http_response_get_status_code(event->response); if (code == 207 || code == 200 || code == 201 || code == 204) { const char *body = belle_sip_message_get_body((belle_sip_message_t *)event->response); switch(query->type) { case LinphoneCardDavQueryTypePropfind: linphone_carddav_ctag_fetched(query->context, parse_ctag_value_from_xml_response(body)); break; case LinphoneCardDavQueryTypeAddressbookQuery: linphone_carddav_vcards_fetched(query->context, parse_vcards_etags_from_xml_response(body)); break; case LinphoneCardDavQueryTypeAddressbookMultiget: linphone_carddav_vcards_pulled(query->context, parse_vcards_from_xml_response(body)); break; case LinphoneCardDavQueryTypePut: { belle_sip_header_t *header = belle_sip_message_get_header((belle_sip_message_t *)event->response, "ETag"); LinphoneFriend *lf = (LinphoneFriend *)query->user_data; LinphoneVcard *lvc = linphone_friend_get_vcard(lf); if (lf && lvc) { if (header) { const char *etag = belle_sip_header_get_unparsed_value(header); if (!linphone_vcard_get_etag(lvc)) { ms_debug("eTag for newly created vCard is: %s", etag); } else { ms_debug("eTag for updated vCard is: %s", etag); } linphone_vcard_set_etag(lvc, etag); linphone_carddav_client_to_server_sync_done(query->context, TRUE, NULL); linphone_friend_unref(lf); } else { // For some reason, server didn't return the eTag of the updated/created vCard // We need to do a GET on the vCard to get the correct one bctbx_list_t *vcard = NULL; LinphoneCardDavResponse *response = (LinphoneCardDavResponse *)ms_new0(LinphoneCardDavResponse, 1); response->url = ms_strdup(linphone_vcard_get_url(lvc)); vcard = bctbx_list_append(vcard, response); linphone_carddav_pull_vcards(query->context, vcard); bctbx_list_free_with_data(vcard, (void (*)(void *))linphone_carddav_response_free); } } else { linphone_carddav_client_to_server_sync_done(query->context, FALSE, "No LinphoneFriend found in user_data field of query"); } } break; case LinphoneCardDavQueryTypeDelete: linphone_carddav_client_to_server_sync_done(query->context, TRUE, NULL); break; default: ms_error("[carddav] Unknown request: %i", query->type); break; } } else { char msg[100]; snprintf(msg, sizeof(msg), "Unexpected HTTP response code: %i", code); if (is_query_client_to_server_sync(query)) { linphone_carddav_client_to_server_sync_done(query->context, FALSE, msg); } else { linphone_carddav_server_to_client_sync_done(query->context, FALSE, msg); } } } else { if (is_query_client_to_server_sync(query)) { linphone_carddav_client_to_server_sync_done(query->context, FALSE, "No response found"); } else { linphone_carddav_server_to_client_sync_done(query->context, FALSE, "No response found"); } } linphone_carddav_query_free(query); } static void process_io_error_from_carddav_request(void *data, const belle_sip_io_error_event_t *event) { LinphoneCardDavQuery *query = (LinphoneCardDavQuery *)data; ms_error("[carddav] I/O error during CardDAV request sending"); if (is_query_client_to_server_sync(query)) { linphone_carddav_client_to_server_sync_done(query->context, FALSE, "I/O error during CardDAV request sending"); } else { linphone_carddav_server_to_client_sync_done(query->context, FALSE, "I/O error during CardDAV request sending"); } linphone_carddav_query_free(query); } static void process_auth_requested_from_carddav_request(void *data, belle_sip_auth_event_t *event) { LinphoneCardDavQuery *query = (LinphoneCardDavQuery *)data; LinphoneCardDavContext *cdc = query->context; const char *realm = belle_sip_auth_event_get_realm(event); belle_generic_uri_t *uri = belle_generic_uri_parse(query->url); const char *domain = belle_generic_uri_get_host(uri); if (cdc->auth_info) { linphone_auth_info_fill_belle_sip_event(cdc->auth_info, event); } else { LinphoneCore *lc = cdc->friend_list->lc; const bctbx_list_t *auth_infos = linphone_core_get_auth_info_list(lc); ms_debug("Looking for auth info for domain %s and realm %s", domain, realm); while (auth_infos) { LinphoneAuthInfo *auth_info = (LinphoneAuthInfo *)auth_infos->data; if (linphone_auth_info_get_domain(auth_info) && strcmp(domain, linphone_auth_info_get_domain(auth_info)) == 0) { if (!linphone_auth_info_get_realm(auth_info) || strcmp(realm, linphone_auth_info_get_realm(auth_info)) == 0) { linphone_auth_info_fill_belle_sip_event(auth_info, event); cdc->auth_info = linphone_auth_info_clone(auth_info); break; } } auth_infos = bctbx_list_next(auth_infos); } if (!auth_infos) { ms_error("[carddav] Authentication requested during CardDAV request sending, and username/password weren't provided"); if (is_query_client_to_server_sync(query)) { linphone_carddav_client_to_server_sync_done(query->context, FALSE, "Authentication requested during CardDAV request sending, and username/password weren't provided"); } else { linphone_carddav_server_to_client_sync_done(query->context, FALSE, "Authentication requested during CardDAV request sending, and username/password weren't provided"); } linphone_carddav_query_free(query); } } } static void linphone_carddav_send_query(LinphoneCardDavQuery *query) { belle_http_request_listener_callbacks_t cbs = { 0 }; belle_generic_uri_t *uri = NULL; belle_http_request_t *req = NULL; belle_sip_memory_body_handler_t *bh = NULL; LinphoneCardDavContext *cdc = query->context; char* ua = NULL; uri = belle_generic_uri_parse(query->url); if (!uri) { if (cdc && cdc->sync_done_cb) { cdc->sync_done_cb(cdc, FALSE, "Could not send request, URL is invalid"); } belle_sip_error("Could not send request, URL %s is invalid", query->url); linphone_carddav_query_free(query); return; } req = belle_http_request_create(query->method, uri, belle_sip_header_content_type_create("application", "xml; charset=utf-8"), NULL); if (!req) { if (cdc && cdc->sync_done_cb) { cdc->sync_done_cb(cdc, FALSE, "Could not create belle_http_request_t"); } belle_sip_object_unref(uri); belle_sip_error("Could not create belle_http_request_t"); linphone_carddav_query_free(query); return; } ua = ms_strdup_printf("%s/%s", linphone_core_get_user_agent(cdc->friend_list->lc), linphone_core_get_version()); belle_sip_message_add_header((belle_sip_message_t *)req, belle_sip_header_create("User-Agent", ua)); ms_free(ua); if (query->depth) { belle_sip_message_add_header((belle_sip_message_t *)req, belle_sip_header_create("Depth", query->depth)); } else if (query->ifmatch) { belle_sip_message_add_header((belle_sip_message_t *)req, belle_sip_header_create("If-Match", query->ifmatch)); } else if (strcmp(query->method, "PUT")) { belle_sip_message_add_header((belle_sip_message_t *)req, belle_sip_header_create("If-None-Match", "*")); } if (query->body) { bh = belle_sip_memory_body_handler_new_copy_from_buffer(query->body, strlen(query->body), NULL, NULL); belle_sip_message_set_body_handler(BELLE_SIP_MESSAGE(req), bh ? BELLE_SIP_BODY_HANDLER(bh) : NULL); } cbs.process_response = process_response_from_carddav_request; cbs.process_io_error = process_io_error_from_carddav_request; cbs.process_auth_requested = process_auth_requested_from_carddav_request; query->http_request_listener = belle_http_request_listener_create_from_callbacks(&cbs, query); belle_http_provider_send_request(query->context->friend_list->lc->http_provider, req, query->http_request_listener); } static LinphoneCardDavQuery* linphone_carddav_create_put_query(LinphoneCardDavContext *cdc, LinphoneVcard *lvc) { LinphoneCardDavQuery *query = (LinphoneCardDavQuery *)ms_new0(LinphoneCardDavQuery, 1); query->context = cdc; query->depth = NULL; query->ifmatch = linphone_vcard_get_etag(lvc); query->body = ms_strdup(linphone_vcard_as_vcard4_string(lvc)); query->method = "PUT"; query->url = ms_strdup(linphone_vcard_get_url(lvc)); query->type = LinphoneCardDavQueryTypePut; return query; } static char* generate_url_from_server_address_and_uid(const char *server_url) { char *result = NULL; if (server_url) { char *uuid = sal_generate_uuid(); char *url = (char *)(ms_malloc(300)); snprintf(url, 300, "%s/linphone-%s.vcf", server_url, uuid); ms_debug("Generated url is %s", url); result = ms_strdup(url); ms_free(url); ms_free(uuid); } return result; } void linphone_carddav_put_vcard(LinphoneCardDavContext *cdc, LinphoneFriend *lf) { LinphoneVcard *lvc = linphone_friend_get_vcard(lf); if (lvc) { LinphoneCardDavQuery *query = NULL; if (!linphone_vcard_get_uid(lvc)) { linphone_vcard_generate_unique_id(lvc); } if (!linphone_vcard_get_url(lvc)) { char *url = generate_url_from_server_address_and_uid(cdc->friend_list->uri); if (url) { linphone_vcard_set_url(lvc, url); ms_free(url); } else { const char *msg = "vCard doesn't have an URL, and friendlist doesn't have a CardDAV server set either, can't push it"; ms_warning("%s", msg); if (cdc && cdc->sync_done_cb) { cdc->sync_done_cb(cdc, FALSE, msg); } return; } } query = linphone_carddav_create_put_query(cdc, lvc); query->user_data = linphone_friend_ref(lf); linphone_carddav_send_query(query); } else { const char *msg = NULL; if (!lvc) { msg = "LinphoneVcard is NULL"; } else { msg = "Unknown error"; } if (msg) { ms_error("[carddav] %s", msg); } if (cdc && cdc->sync_done_cb) { cdc->sync_done_cb(cdc, FALSE, msg); } } } static LinphoneCardDavQuery* linphone_carddav_create_delete_query(LinphoneCardDavContext *cdc, LinphoneVcard *lvc) { LinphoneCardDavQuery *query = (LinphoneCardDavQuery *)ms_new0(LinphoneCardDavQuery, 1); query->context = cdc; query->depth = NULL; query->ifmatch = linphone_vcard_get_etag(lvc); query->body = NULL; query->method = "DELETE"; query->url = ms_strdup(linphone_vcard_get_url(lvc)); query->type = LinphoneCardDavQueryTypeDelete; return query; } void linphone_carddav_delete_vcard(LinphoneCardDavContext *cdc, LinphoneFriend *lf) { LinphoneVcard *lvc = linphone_friend_get_vcard(lf); if (lvc && linphone_vcard_get_uid(lvc) && linphone_vcard_get_etag(lvc)) { LinphoneCardDavQuery *query = NULL; if (!linphone_vcard_get_url(lvc)) { char *url = generate_url_from_server_address_and_uid(cdc->friend_list->uri); if (url) { linphone_vcard_set_url(lvc, url); ms_free(url); } else { const char *msg = "vCard doesn't have an URL, and friendlist doesn't have a CardDAV server set either, can't delete it"; ms_warning("%s", msg); if (cdc && cdc->sync_done_cb) { cdc->sync_done_cb(cdc, FALSE, msg); } return; } } query = linphone_carddav_create_delete_query(cdc, lvc); linphone_carddav_send_query(query); } else { const char *msg = NULL; if (!lvc) { msg = "LinphoneVcard is NULL"; } else if (!linphone_vcard_get_uid(lvc)) { msg = "LinphoneVcard doesn't have an UID"; } else if (!linphone_vcard_get_etag(lvc)) { msg = "LinphoneVcard doesn't have an eTag"; } if (msg) { ms_error("[carddav] %s", msg); } if (cdc && cdc->sync_done_cb) { cdc->sync_done_cb(cdc, FALSE, msg); } } } void linphone_carddav_set_synchronization_done_callback(LinphoneCardDavContext *cdc, LinphoneCardDavSynchronizationDoneCb cb) { cdc->sync_done_cb = cb; } void linphone_carddav_set_new_contact_callback(LinphoneCardDavContext *cdc, LinphoneCardDavContactCreatedCb cb) { cdc->contact_created_cb = cb; } void linphone_carddav_set_updated_contact_callback(LinphoneCardDavContext *cdc, LinphoneCardDavContactUpdatedCb cb) { cdc->contact_updated_cb = cb; } void linphone_carddav_set_removed_contact_callback(LinphoneCardDavContext *cdc, LinphoneCardDavContactRemovedCb cb) { cdc->contact_removed_cb = cb; } static LinphoneCardDavQuery* linphone_carddav_create_propfind_query(LinphoneCardDavContext *cdc) { LinphoneCardDavQuery *query = (LinphoneCardDavQuery *)ms_new0(LinphoneCardDavQuery, 1); query->context = cdc; query->depth = "0"; query->ifmatch = NULL; query->body = ms_strdup(""); query->method = "PROPFIND"; query->url = ms_strdup(cdc->friend_list->uri); query->type = LinphoneCardDavQueryTypePropfind; return query; } void linphone_carddav_get_current_ctag(LinphoneCardDavContext *cdc) { LinphoneCardDavQuery *query = linphone_carddav_create_propfind_query(cdc); linphone_carddav_send_query(query); } static LinphoneCardDavQuery* linphone_carddav_create_addressbook_query(LinphoneCardDavContext *cdc) { LinphoneCardDavQuery *query = (LinphoneCardDavQuery *)ms_new0(LinphoneCardDavQuery, 1); query->context = cdc; query->depth = "1"; query->ifmatch = NULL; query->body = ms_strdup(""); query->method = "REPORT"; query->url = ms_strdup(cdc->friend_list->uri); query->type = LinphoneCardDavQueryTypeAddressbookQuery; return query; } void linphone_carddav_fetch_vcards(LinphoneCardDavContext *cdc) { LinphoneCardDavQuery *query = linphone_carddav_create_addressbook_query(cdc); linphone_carddav_send_query(query); } static LinphoneCardDavQuery* linphone_carddav_create_addressbook_multiget_query(LinphoneCardDavContext *cdc, bctbx_list_t *vcards) { LinphoneCardDavQuery *query = (LinphoneCardDavQuery *)ms_new0(LinphoneCardDavQuery, 1); char *body = (char *)ms_malloc((bctbx_list_size(vcards) + 1) * 300 * sizeof(char)); bctbx_list_t *iterator = vcards; query->context = cdc; query->depth = "1"; query->ifmatch = NULL; query->method = "REPORT"; query->url = ms_strdup(cdc->friend_list->uri); query->type = LinphoneCardDavQueryTypeAddressbookMultiget; sprintf(body, "%s", ""); while (iterator) { LinphoneCardDavResponse *response = (LinphoneCardDavResponse *)iterator->data; if (response) { char temp_body[300]; snprintf(temp_body, sizeof(temp_body), "%s", response->url); strcat(body, temp_body); iterator = bctbx_list_next(iterator); } } strcat(body, ""); query->body = ms_strdup(body); ms_free(body); return query; } void linphone_carddav_pull_vcards(LinphoneCardDavContext *cdc, bctbx_list_t *vcards_to_pull) { LinphoneCardDavQuery *query = linphone_carddav_create_addressbook_multiget_query(cdc, vcards_to_pull); linphone_carddav_send_query(query); } liblinphone-4.4.21/coreapi/carddav.h000066400000000000000000000132131376572216100173160ustar00rootroot00000000000000/* * Copyright (c) 2010-2019 Belledonne Communications SARL. * * This file is part of Liblinphone. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef LINPHONE_CARDDAV_H #define LINPHONE_CARDDAV_H #include "linphone/core.h" #ifdef __cplusplus extern "C" { #endif /** * @addtogroup carddav_vcard * @{ */ typedef struct _LinphoneCardDavContext LinphoneCardDavContext; typedef enum _LinphoneCardDavQueryType { LinphoneCardDavQueryTypePropfind, LinphoneCardDavQueryTypeAddressbookQuery, LinphoneCardDavQueryTypeAddressbookMultiget, LinphoneCardDavQueryTypePut, LinphoneCardDavQueryTypeDelete } LinphoneCardDavQueryType; typedef struct _LinphoneCardDavQuery LinphoneCardDavQuery; typedef struct _LinphoneCardDavResponse LinphoneCardDavResponse; /** * Callback used to notify a new contact has been created on the CardDAV server **/ typedef void (*LinphoneCardDavContactCreatedCb)(LinphoneCardDavContext *cdc, LinphoneFriend *lf); /** * Callback used to notify a contact has been updated on the CardDAV server **/ typedef void (*LinphoneCardDavContactUpdatedCb)(LinphoneCardDavContext *cdc, LinphoneFriend *new_friend, LinphoneFriend *old_friend); /** * Callback used to notify a contact has been removed on the CardDAV server **/ typedef void (*LinphoneCardDavContactRemovedCb)(LinphoneCardDavContext *cdc, LinphoneFriend *lf); /** * Callback used to notify a contact has been removed on the CardDAV server **/ typedef void (*LinphoneCardDavSynchronizationDoneCb)(LinphoneCardDavContext *cdc, bool_t success, const char *message); /** * Creates a CardDAV context for all related operations * @param lfl LinphoneFriendList object * @return LinphoneCardDavContext object if vCard support is enabled and server URL is available, NULL otherwise */ LINPHONE_PUBLIC LinphoneCardDavContext* linphone_carddav_context_new(LinphoneFriendList *lfl); /** * Deletes a LinphoneCardDavContext object * @param cdc LinphoneCardDavContext object */ LINPHONE_PUBLIC void linphone_carddav_context_destroy(LinphoneCardDavContext *cdc); /** * Sets a user pointer to the LinphoneCardDAVContext object * @param cdc LinphoneCardDavContext object * @param ud The user data pointer */ LINPHONE_PUBLIC void linphone_carddav_set_user_data(LinphoneCardDavContext *cdc, void *ud); /** * Gets the user pointer set in the LinphoneCardDAVContext object * @param cdc LinphoneCardDavContext object * @return The user data pointer if set, NULL otherwise */ LINPHONE_PUBLIC void* linphone_carddav_get_user_data(LinphoneCardDavContext *cdc); /** * Starts a synchronization with the remote server to update local friends with server changes * @param cdc LinphoneCardDavContext object */ LINPHONE_PUBLIC void linphone_carddav_synchronize(LinphoneCardDavContext *cdc); /** * Sends a LinphoneFriend to the CardDAV server for update or creation * @param cdc LinphoneCardDavContext object * @param lf a LinphoneFriend object to update/create on the server */ LINPHONE_PUBLIC void linphone_carddav_put_vcard(LinphoneCardDavContext *cdc, LinphoneFriend *lf); /** * Deletes a LinphoneFriend on the CardDAV server * @param cdc LinphoneCardDavContext object * @param lf a LinphoneFriend object to delete on the server */ LINPHONE_PUBLIC void linphone_carddav_delete_vcard(LinphoneCardDavContext *cdc, LinphoneFriend *lf); /** * Set the synchronization done callback. * @param cdc LinphoneCardDavContext object * @param cb The synchronization done callback to be used. */ LINPHONE_PUBLIC void linphone_carddav_set_synchronization_done_callback(LinphoneCardDavContext *cdc, LinphoneCardDavSynchronizationDoneCb cb); /** * Set the new contact callback. * @param cdc LinphoneCardDavContext object * @param cb The new contact callback to be used. */ LINPHONE_PUBLIC void linphone_carddav_set_new_contact_callback(LinphoneCardDavContext *cdc, LinphoneCardDavContactCreatedCb cb); /** * Set the updated contact callback. * @param cdc LinphoneCardDavContext object * @param cb The updated contact callback to be used. */ LINPHONE_PUBLIC void linphone_carddav_set_updated_contact_callback(LinphoneCardDavContext *cdc, LinphoneCardDavContactUpdatedCb cb); /** * Set the removed contact callback. * @param cdc LinphoneCardDavContext object * @param cb The removed contact callback to be used. */ LINPHONE_PUBLIC void linphone_carddav_set_removed_contact_callback(LinphoneCardDavContext *cdc, LinphoneCardDavContactRemovedCb cb); /** * Retrieves the current cTag value for the remote server * @param cdc LinphoneCardDavContext object */ void linphone_carddav_get_current_ctag(LinphoneCardDavContext *cdc); /** * Retrieves a list of all the vCards on server side to be able to detect changes * @param cdc LinphoneCardDavContext object */ void linphone_carddav_fetch_vcards(LinphoneCardDavContext *cdc); /** * Download asked vCards from the server * @param cdc LinphoneCardDavContext object * @param vcards_to_pull a MSList of LinphoneCardDavResponse objects with at least the url field filled */ void linphone_carddav_pull_vcards(LinphoneCardDavContext *cdc, MSList *vcards_to_pull); /** * @} */ #ifdef __cplusplus } #endif #endif liblinphone-4.4.21/coreapi/chat.c000066400000000000000000000246151376572216100166340ustar00rootroot00000000000000/* * Copyright (c) 2010-2019 Belledonne Communications SARL. * * This file is part of Liblinphone. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ /* * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include #include #include #include #include "linphone/core.h" #include "private.h" #include "linphone/lpconfig.h" #include "belle-sip/belle-sip.h" #include "ortp/b64.h" #include "linphone/wrapper_utils.h" #include "c-wrapper/c-wrapper.h" #include "call/call.h" #include "chat/chat-room/abstract-chat-room-p.h" #include "chat/chat-room/chat-room-params.h" #include "chat/chat-room/basic-chat-room.h" #ifdef HAVE_ADVANCED_IM #include "chat/chat-room/client-group-chat-room.h" #include "chat/chat-room/client-group-to-basic-chat-room.h" #endif #include "content/content-type.h" #include "core/core-p.h" #include "linphone/api/c-chat-room-params.h" using namespace std; void linphone_core_disable_chat(LinphoneCore *lc, LinphoneReason deny_reason) { lc->chat_deny_code = deny_reason; } void linphone_core_enable_chat(LinphoneCore *lc) { lc->chat_deny_code = LinphoneReasonNone; } bool_t linphone_core_chat_enabled(const LinphoneCore *lc) { return lc->chat_deny_code != LinphoneReasonNone; } const bctbx_list_t *linphone_core_get_chat_rooms (LinphoneCore *lc) { if (lc->chat_rooms) bctbx_list_free_with_data(lc->chat_rooms, (bctbx_list_free_func)linphone_chat_room_unref); lc->chat_rooms = L_GET_RESOLVED_C_LIST_FROM_CPP_LIST(L_GET_CPP_PTR_FROM_C_OBJECT(lc)->getChatRooms()); return lc->chat_rooms; } static LinphoneChatRoom *linphone_chat_room_new (LinphoneCore *core, const LinphoneAddress *addr) { return L_GET_C_BACK_PTR(L_GET_CPP_PTR_FROM_C_OBJECT(core)->getOrCreateBasicChatRoom( *L_GET_CPP_PTR_FROM_C_OBJECT(addr) )); } LinphoneChatRoom *_linphone_core_create_chat_room_from_call(LinphoneCall *call){ LinphoneChatRoom *cr = linphone_chat_room_new(linphone_call_get_core(call), linphone_address_clone(linphone_call_get_remote_address(call))); linphone_chat_room_set_call(cr, call); return cr; } LinphoneChatRoom *linphone_core_get_chat_room (LinphoneCore *lc, const LinphoneAddress *peerAddr) { return L_GET_C_BACK_PTR(L_GET_CPP_PTR_FROM_C_OBJECT(lc)->getOrCreateBasicChatRoom(*L_GET_CPP_PTR_FROM_C_OBJECT(peerAddr))); } LinphoneChatRoom *linphone_core_get_chat_room_2 ( LinphoneCore *lc, const LinphoneAddress *peer_addr, const LinphoneAddress *local_addr ) { return L_GET_C_BACK_PTR(L_GET_CPP_PTR_FROM_C_OBJECT(lc)->getOrCreateBasicChatRoom(LinphonePrivate::ConferenceId( LinphonePrivate::IdentityAddress(*L_GET_CPP_PTR_FROM_C_OBJECT(peer_addr)), LinphonePrivate::IdentityAddress(*L_GET_CPP_PTR_FROM_C_OBJECT(local_addr)) ))); } //Deprecated LinphoneChatRoom *linphone_core_create_client_group_chat_room(LinphoneCore *lc, const char *subject, bool_t fallback) { return linphone_core_create_client_group_chat_room_2(lc, subject, fallback, FALSE); } //Deprecated LinphoneChatRoom *linphone_core_create_client_group_chat_room_2(LinphoneCore *lc, const char *subject, bool_t fallback, bool_t encrypted) { return L_GET_C_BACK_PTR(L_GET_PRIVATE_FROM_C_OBJECT(lc)->createClientGroupChatRoom(L_C_TO_STRING(subject), !!fallback, !!encrypted)); } LinphoneChatRoom *linphone_core_create_chat_room(LinphoneCore *lc, const LinphoneChatRoomParams *params, const LinphoneAddress *localAddr, const char *subject, const bctbx_list_t *participants) { return L_GET_C_BACK_PTR(L_GET_PRIVATE_FROM_C_OBJECT(lc)->createChatRoom(LinphonePrivate::ChatRoomParams::toCpp(params)->clone()->toSharedPtr(), LinphonePrivate::IdentityAddress(*L_GET_CPP_PTR_FROM_C_OBJECT(localAddr)), L_C_TO_STRING(subject), L_GET_CPP_LIST_FROM_C_LIST_2(participants, LinphoneAddress *, LinphonePrivate::IdentityAddress, [] (LinphoneAddress *addr) { return LinphonePrivate::IdentityAddress(*L_GET_CPP_PTR_FROM_C_OBJECT(addr)); }))); } LinphoneChatRoom *linphone_core_create_chat_room_2(LinphoneCore *lc, const LinphoneChatRoomParams *params, const char *subject, const bctbx_list_t *participants) { return L_GET_C_BACK_PTR(L_GET_PRIVATE_FROM_C_OBJECT(lc)->createChatRoom(LinphonePrivate::ChatRoomParams::toCpp(params)->clone()->toSharedPtr(), L_C_TO_STRING(subject), L_GET_CPP_LIST_FROM_C_LIST_2(participants, LinphoneAddress *, LinphonePrivate::IdentityAddress, [] (LinphoneAddress *addr) { return LinphonePrivate::IdentityAddress(*L_GET_CPP_PTR_FROM_C_OBJECT(addr)); }))); } LinphoneChatRoom *linphone_core_create_chat_room_3(LinphoneCore *lc, const char *subject, const bctbx_list_t *participants) { return L_GET_C_BACK_PTR(L_GET_PRIVATE_FROM_C_OBJECT(lc)->createChatRoom(L_C_TO_STRING(subject), L_GET_CPP_LIST_FROM_C_LIST_2(participants, LinphoneAddress *, LinphonePrivate::IdentityAddress, [] (LinphoneAddress *addr) { return LinphonePrivate::IdentityAddress(*L_GET_CPP_PTR_FROM_C_OBJECT(addr)); }))); } LinphoneChatRoom *linphone_core_create_chat_room_4(LinphoneCore *lc, const LinphoneChatRoomParams *params, const LinphoneAddress *localAddr, const LinphoneAddress *participant) { return L_GET_C_BACK_PTR(L_GET_PRIVATE_FROM_C_OBJECT(lc)->createChatRoom(LinphonePrivate::ChatRoomParams::toCpp(params)->clone()->toSharedPtr(), LinphonePrivate::IdentityAddress(*L_GET_CPP_PTR_FROM_C_OBJECT(localAddr)), LinphonePrivate::IdentityAddress(*L_GET_CPP_PTR_FROM_C_OBJECT(participant)))); } LinphoneChatRoom *linphone_core_create_chat_room_5(LinphoneCore *lc, const LinphoneAddress *participant) { return L_GET_C_BACK_PTR(L_GET_PRIVATE_FROM_C_OBJECT(lc)->createChatRoom(LinphonePrivate::IdentityAddress(*L_GET_CPP_PTR_FROM_C_OBJECT(participant)))); } LinphoneChatRoomParams *linphone_core_create_default_chat_room_params(LinphoneCore *lc) { auto params = LinphonePrivate::ChatRoomParams::getDefaults(L_GET_CPP_PTR_FROM_C_OBJECT(lc)->getSharedFromThis()); params->ref(); return params->toC(); } LinphoneChatRoom *_linphone_core_create_server_group_chat_room (LinphoneCore *lc, LinphonePrivate::SalCallOp *op) { return _linphone_server_group_chat_room_new(lc, op); } void linphone_core_delete_chat_room (LinphoneCore *, LinphoneChatRoom *cr) { L_GET_CPP_PTR_FROM_C_OBJECT(cr)->deleteFromDb(); } LinphoneChatRoom *linphone_core_get_chat_room_from_uri(LinphoneCore *lc, const char *to) { return L_GET_C_BACK_PTR(L_GET_CPP_PTR_FROM_C_OBJECT(lc)->getOrCreateBasicChatRoomFromUri(L_C_TO_STRING(to))); } LinphoneChatRoom *linphone_core_find_chat_room( const LinphoneCore *lc, const LinphoneAddress *peer_addr, const LinphoneAddress *local_addr ) { return L_GET_C_BACK_PTR(L_GET_CPP_PTR_FROM_C_OBJECT(lc)->findChatRoom(LinphonePrivate::ConferenceId( LinphonePrivate::IdentityAddress(*L_GET_CPP_PTR_FROM_C_OBJECT(peer_addr)), LinphonePrivate::IdentityAddress(*L_GET_CPP_PTR_FROM_C_OBJECT(local_addr)) ))); } LinphoneChatRoom *linphone_core_find_one_to_one_chat_room ( const LinphoneCore *lc, const LinphoneAddress *local_addr, const LinphoneAddress *participant_addr ) { return L_GET_C_BACK_PTR(L_GET_CPP_PTR_FROM_C_OBJECT(lc)->findOneToOneChatRoom( LinphonePrivate::IdentityAddress(*L_GET_CPP_PTR_FROM_C_OBJECT(local_addr)), LinphonePrivate::IdentityAddress(*L_GET_CPP_PTR_FROM_C_OBJECT(participant_addr)), false, false) ); } LinphoneChatRoom *linphone_core_find_one_to_one_chat_room_2 ( const LinphoneCore *lc, const LinphoneAddress *local_addr, const LinphoneAddress *participant_addr, bool_t encrypted ) { return L_GET_C_BACK_PTR(L_GET_CPP_PTR_FROM_C_OBJECT(lc)->findOneToOneChatRoom( LinphonePrivate::IdentityAddress(*L_GET_CPP_PTR_FROM_C_OBJECT(local_addr)), LinphonePrivate::IdentityAddress(*L_GET_CPP_PTR_FROM_C_OBJECT(participant_addr)), false, !!encrypted) ); } LinphoneReason linphone_core_message_received(LinphoneCore *lc, LinphonePrivate::SalOp *op, const SalMessage *sal_msg) { LinphoneReason reason = LinphoneReasonNotAcceptable; std::string peerAddress; std::string localAddress; const char *session_mode = sal_custom_header_find(op->getRecvCustomHeaders(), "Session-mode"); if (linphone_core_conference_server_enabled(lc)) { localAddress = peerAddress = op->getTo(); } else { peerAddress = op->getFrom(); localAddress = op->getTo(); } LinphonePrivate::ConferenceId conferenceId{ LinphonePrivate::IdentityAddress(peerAddress), LinphonePrivate::IdentityAddress(localAddress) }; shared_ptr chatRoom = L_GET_CPP_PTR_FROM_C_OBJECT(lc)->findChatRoom(conferenceId); if (chatRoom) reason = L_GET_PRIVATE(chatRoom)->onSipMessageReceived(op, sal_msg); else if (!linphone_core_conference_server_enabled(lc)) { /* Client mode but check that it is really for basic chatroom before creating it.*/ if (session_mode && strcasecmp(session_mode, "true") == 0) { lError() << "Message is received in the context of a client chatroom for which we have no context."; reason = LinphoneReasonNotAcceptable; } else { chatRoom = L_GET_CPP_PTR_FROM_C_OBJECT(lc)->getOrCreateBasicChatRoom(conferenceId); if (chatRoom) reason = L_GET_PRIVATE(chatRoom)->onSipMessageReceived(op, sal_msg); } } else { /* Server mode but chatroom not found. */ reason = LinphoneReasonNotFound; } return reason; } unsigned int linphone_chat_message_store(LinphoneChatMessage *msg) { // DO nothing, just for old JNI compat... return 1; } liblinphone-4.4.21/coreapi/conference.cc000066400000000000000000001170411376572216100201630ustar00rootroot00000000000000/* * Copyright (c) 2010-2019 Belledonne Communications SARL. * * This file is part of Liblinphone. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include #include "linphone/core.h" #include "conference_private.h" #include "c-wrapper/c-wrapper.h" #include "call/call-p.h" #include "conference/params/media-session-params-p.h" #include "core/core-p.h" // TODO: From coreapi. Remove me later. #include "private.h" using namespace std; LINPHONE_BEGIN_NAMESPACE namespace MediaConference{ template inline list<_type> toStd(const bctbx_list_t *l){ list<_type> ret; for(; l != NULL; l = l->next){ ret.push_back(static_cast<_type>(l->data)); } return ret; } class Conference; class LocalConference; class RemoteConference; class ConferenceParams : public bellesip::HybridObject{ friend class Conference; friend class LocalConference; friend class RemoteConference; public: ConferenceParams(const ConferenceParams& params) = default; ConferenceParams(const LinphoneCore *core = NULL) { m_enableVideo = false; if(core) { const LinphoneVideoPolicy *policy = linphone_core_get_video_policy(core); if(policy->automatically_initiate) m_enableVideo = true; } m_stateChangedCb = NULL; m_userData = NULL; } void enableVideo(bool enable) {m_enableVideo = enable;} bool videoRequested() const {return m_enableVideo;} void setStateChangedCallback(LinphoneConferenceStateChangedCb cb, void *userData) { m_stateChangedCb = cb; m_userData = userData; } void enableLocalParticipant(bool enable){ mLocalParticipantEnabled = enable; } bool localParticipantEnabled()const { return mLocalParticipantEnabled; } Object *clone()const override{ return new ConferenceParams(*this); } private: LinphoneConferenceStateChangedCb m_stateChangedCb; void *m_userData; bool m_enableVideo; bool mLocalParticipantEnabled = true; }; class Conference { public: class Participant { public: Participant(LinphoneCall *call) { m_uri = linphone_address_clone(linphone_call_get_remote_address(call)); m_call = call; } ~Participant() { linphone_address_unref(m_uri); } const LinphoneAddress *getUri() const { return m_uri; } LinphoneCall *getCall() const { return m_call; } private: Participant(const Participant &src); Participant &operator=(const Participant &src); private: LinphoneAddress *m_uri; LinphoneCall *m_call; friend class RemoteConference; }; Conference(LinphoneCore *core, LinphoneConference *conf, const ConferenceParams *params = NULL); virtual ~Conference() {} const ConferenceParams &getCurrentParams() const {return *m_currentParams;} virtual int inviteAddresses(const list &addresses, const LinphoneCallParams *params) = 0; virtual int addParticipant(LinphoneCall *call) = 0; virtual int removeParticipant(LinphoneCall *call) = 0; virtual int removeParticipant(const LinphoneAddress *uri) = 0; virtual int terminate() = 0; virtual int enter() = 0; virtual int leave() = 0; virtual bool isIn() const = 0; AudioStream *getAudioStream() const {return m_localParticipantStream;} int muteMicrophone(bool val); bool microphoneIsMuted() const {return m_isMuted;} float getInputVolume() const; virtual int getSize() const {return (int)m_participants.size() + (isIn()?1:0);} const list &getParticipants() const {return m_participants;} virtual int startRecording(const char *path) = 0; virtual int stopRecording() = 0; virtual void onCallStreamStarting(LinphoneCall *call, bool isPausedByRemote) {}; virtual void onCallStreamStopping(LinphoneCall *call) {}; virtual void onCallTerminating(LinphoneCall *call) {}; LinphoneConferenceState getState() const {return m_state;} LinphoneCore *getCore()const{ return m_core; } static const char *stateToString(LinphoneConferenceState state); void setID(const char *conferenceID) { m_conferenceID = conferenceID; } const char *getID() { return m_conferenceID.c_str(); } protected: void setState(LinphoneConferenceState state); Participant *findParticipant(const LinphoneCall *call) const; Participant *findParticipant(const LinphoneAddress *uri) const; protected: string m_conferenceID; LinphoneCore *m_core; AudioStream *m_localParticipantStream; bool m_isMuted; list m_participants; shared_ptr m_currentParams; LinphoneConferenceState m_state; LinphoneConference *m_conference; }; class LocalConference: public Conference { public: LocalConference(LinphoneCore *core, LinphoneConference *conf, const ConferenceParams *params = NULL); virtual ~LocalConference(); virtual int inviteAddresses(const list &addresses, const LinphoneCallParams *params) override; virtual int addParticipant(LinphoneCall *call) override; virtual int removeParticipant(LinphoneCall *call) override; virtual int removeParticipant(const LinphoneAddress *uri) override; virtual int terminate() override; virtual int enter() override; virtual int leave() override; virtual bool isIn() const override { return m_localParticipantStream!=NULL; } virtual int getSize() const override; virtual int startRecording(const char *path) override; virtual int stopRecording() override; virtual void onCallStreamStarting(LinphoneCall *call, bool isPausedByRemote) override; virtual void onCallStreamStopping(LinphoneCall *call) override; virtual void onCallTerminating(LinphoneCall *call) override; private: void addLocalEndpoint(); int remoteParticipantsCount(); void removeLocalEndpoint(); int removeFromConference(LinphoneCall *call, bool_t active); int convertConferenceToCall(); void participantUnplugged(LinphoneCall *call); static RtpProfile *sMakeDummyProfile(int samplerate); MSAudioConference *m_conf; MSAudioEndpoint *m_localEndpoint; MSAudioEndpoint *m_recordEndpoint; RtpProfile *m_localDummyProfile; bool_t m_terminating; }; class RemoteConference: public Conference { public: RemoteConference(LinphoneCore *core, LinphoneConference *conf, const ConferenceParams *params = NULL); virtual ~RemoteConference(); virtual int inviteAddresses(const list &addresses, const LinphoneCallParams *params) override; virtual int addParticipant(LinphoneCall *call) override; virtual int removeParticipant(LinphoneCall *call) override { return -1; } virtual int removeParticipant(const LinphoneAddress *uri) override; virtual int terminate() override; virtual int enter() override; virtual int leave() override; virtual bool isIn() const override; virtual int startRecording (const char *path) override { return 0; } virtual int stopRecording() override { return 0; } private: bool focusIsReady() const; bool transferToFocus(LinphoneCall *call); void reset(); void onFocusCallSateChanged(LinphoneCallState state); void onPendingCallStateChanged(LinphoneCall *call, LinphoneCallState state); void onTransferingCallStateChanged(LinphoneCall *transfered, LinphoneCallState newCallState); static void callStateChangedCb(LinphoneCore *lc, LinphoneCall *call, LinphoneCallState cstate, const char *message); static void transferStateChanged(LinphoneCore *lc, LinphoneCall *transfered, LinphoneCallState new_call_state); const char *m_focusAddr; char *m_focusContact; LinphoneCall *m_focusCall; LinphoneCoreCbs *m_coreCbs; list m_pendingCalls; list m_transferingCalls; }; Conference::Conference(LinphoneCore *core, LinphoneConference *conf, const ConferenceParams *params) : m_core(core), m_localParticipantStream(nullptr), m_isMuted(false), m_currentParams((new ConferenceParams(*params))->toSharedPtr()), m_state(LinphoneConferenceStopped), m_conference(conf) { } int Conference::addParticipant (LinphoneCall *call) { Participant *p = new Participant(call); m_participants.push_back(p); return 0; } int Conference::removeParticipant (LinphoneCall *call) { Participant *p = findParticipant(call); if (!p) return -1; delete p; m_participants.remove(p); return 0; } int Conference::removeParticipant (const LinphoneAddress *uri) { Participant *p = findParticipant(uri); if (!p) return -1; delete p; m_participants.remove(p); return 0; } int Conference::terminate () { for (auto it = m_participants.begin(); it != m_participants.end(); it++) delete *it; m_participants.clear(); return 0; } int Conference::muteMicrophone (bool val) { if (val) audio_stream_set_mic_gain(m_localParticipantStream, 0); else audio_stream_set_mic_gain_db(m_localParticipantStream, m_core->sound_conf.soft_mic_lev); if (linphone_core_get_rtp_no_xmit_on_audio_mute(m_core)) audio_stream_mute_rtp(m_localParticipantStream, val); m_isMuted = val; return 0; } float Conference::getInputVolume () const { AudioStream *st = m_localParticipantStream; if (st && st->volsend && !m_isMuted) { float vol = 0; ms_filter_call_method(st->volsend, MS_VOLUME_GET, &vol); return vol; } return LINPHONE_VOLUME_DB_LOWEST; } const char *Conference::stateToString (LinphoneConferenceState state) { switch(state) { case LinphoneConferenceStopped: return "Stopped"; case LinphoneConferenceStarting: return "Starting"; case LinphoneConferenceRunning: return "Ready"; case LinphoneConferenceStartingFailed: return "Starting failed"; default: return "Invalid state"; } } void Conference::setState (LinphoneConferenceState state) { if (m_state != state) { ms_message("Switching conference [%p] into state '%s'", this, stateToString(state)); m_state = state; if (m_currentParams->m_stateChangedCb) m_currentParams->m_stateChangedCb(m_conference, state, m_currentParams->m_userData); } } Conference::Participant *Conference::findParticipant (const LinphoneCall *call) const { for (auto it = m_participants.begin(); it != m_participants.end(); it++) { if ((*it)->getCall() == call) return *it; } return nullptr; } Conference::Participant *Conference::findParticipant (const LinphoneAddress *uri) const { for (auto it = m_participants.begin(); it != m_participants.end(); it++) { if (linphone_address_equal((*it)->getUri(), uri)) return *it; } return nullptr; } LocalConference::LocalConference (LinphoneCore *core, LinphoneConference *conf, const ConferenceParams *params) : Conference(core, conf, params), m_conf(nullptr), m_localEndpoint(nullptr), m_recordEndpoint(nullptr), m_localDummyProfile(nullptr), m_terminating(FALSE) { MSAudioConferenceParams ms_conf_params; ms_conf_params.samplerate = lp_config_get_int(m_core->config, "sound", "conference_rate", 16000); m_conf = ms_audio_conference_new(&ms_conf_params, core->factory); m_state = LinphoneConferenceRunning; } LocalConference::~LocalConference() { ms_audio_conference_destroy(m_conf); } RtpProfile *LocalConference::sMakeDummyProfile (int samplerate) { RtpProfile *prof = rtp_profile_new("dummy"); PayloadType *pt = payload_type_clone(&payload_type_l16_mono); pt->clock_rate = samplerate; rtp_profile_set_payload(prof, 0, pt); return prof; } void LocalConference::addLocalEndpoint () { if (!m_currentParams->localParticipantEnabled()) return; if (m_localEndpoint) return; // Local endpoint is already created and active in the conference. /* Create a dummy audiostream in order to extract the local part of it */ /* network address and ports have no meaning and are not used here. */ AudioStream *st = audio_stream_new(m_core->factory, 65000, 65001, FALSE); MSSndCard *playcard = m_core->sound_conf.lsd_card ? m_core->sound_conf.lsd_card : m_core->sound_conf.play_sndcard; MSSndCard *captcard = m_core->sound_conf.capt_sndcard; const MSAudioConferenceParams *params = ms_audio_conference_get_params(m_conf); m_localDummyProfile = sMakeDummyProfile(params->samplerate); audio_stream_start_full(st, m_localDummyProfile, "127.0.0.1", 65000, "127.0.0.1", 65001, 0, 40, nullptr, nullptr, playcard, captcard, linphone_core_echo_cancellation_enabled(m_core) ); _post_configure_audio_stream(st, m_core, FALSE); m_localParticipantStream = st; m_localEndpoint = ms_audio_endpoint_get_from_stream(st, FALSE); ms_message("Conference: adding local endpoint"); ms_audio_conference_add_member(m_conf, m_localEndpoint); } int LocalConference::inviteAddresses (const list &addresses, const LinphoneCallParams *params) { for (const auto &address : addresses) { LinphoneCall *call = linphone_core_get_call_by_remote_address2(m_core, address); if (!call) { /* Start a new call by indicating that it has to be put into the conference directly */ LinphoneCallParams *new_params = params ? linphone_call_params_copy(params) : linphone_core_create_call_params(m_core, nullptr); linphone_call_params_set_in_conference(new_params, TRUE); linphone_call_params_enable_video(new_params, FALSE); // Video not supported for conferences. call = linphone_core_invite_address_with_params(m_core, address, new_params); if (!call){ ms_error("LocalConference::inviteAddresses(): could not invite participant"); }else{ addParticipant(call); } linphone_call_params_unref(new_params); } else { /* There is already a call to this address, so simply join it to the local conference if not already done */ if (!linphone_call_params_get_in_conference(linphone_call_get_current_params(call))) addParticipant(call); } /* If the local participant is not yet created, created it and it to the conference */ addLocalEndpoint(); } return 0; } int LocalConference::addParticipant (LinphoneCall *call) { LinphoneCallState state = linphone_call_get_state(call); if (linphone_call_params_get_in_conference(linphone_call_get_current_params(call))) { ms_error("Already in conference"); return -1; } switch(state){ case LinphoneCallOutgoingInit: case LinphoneCallOutgoingProgress: case LinphoneCallIncomingReceived: const_cast( L_GET_PRIVATE(L_GET_CPP_PTR_FROM_C_OBJECT(call)->getParams()))->setInConference(true); break; case LinphoneCallPaused: const_cast( L_GET_PRIVATE(L_GET_CPP_PTR_FROM_C_OBJECT(call)->getParams()))->setInConference(true); linphone_call_resume(call); break; case LinphoneCallStreamsRunning: { LinphoneCallParams *params = linphone_core_create_call_params(m_core, call); linphone_call_params_set_in_conference(params, TRUE); linphone_call_params_enable_video(params, FALSE); if (L_GET_PRIVATE_FROM_C_OBJECT(call)->getMediaStream(LinphoneStreamTypeAudio) || L_GET_PRIVATE_FROM_C_OBJECT(call)->getMediaStream(LinphoneStreamTypeVideo)) { linphone_call_stop_media_streams(call); /* Free the audio & video local resources */ linphone_call_init_media_streams(call); } /* This will trigger a reINVITE that will later redraw the streams */ /* FIXME: probably a bit too much to just redraw streams! */ linphone_call_update(call, params); linphone_call_params_unref(params); addLocalEndpoint(); } break; default: ms_error("Call is in state %s, it cannot be added to the conference", linphone_call_state_to_string(linphone_call_get_state(call))); return -1; break; } setState(LinphoneConferenceRunning); Conference::addParticipant(call); if (call == linphone_core_get_current_call(m_core)) L_GET_PRIVATE_FROM_C_OBJECT(m_core)->setCurrentCall(nullptr); const_cast( L_GET_CPP_PTR_FROM_C_OBJECT(call)->getParams())->enableVideo(false); _linphone_call_set_conf_ref(call, m_conference); return 0; } int LocalConference::removeFromConference (LinphoneCall *call, bool_t active_after_removed) { bool noMediaRestartRequired = false; /* Special handling for already terminated calls.*/ switch(linphone_call_get_state(call)){ case LinphoneCallEnd: case LinphoneCallError: Conference::removeParticipant(call); return 0; break; default: break; } if (!linphone_call_params_get_in_conference(linphone_call_get_current_params(call))) { if (linphone_call_params_get_in_conference(linphone_call_get_params(call))) { /* Another special case for calls that have not yet started their streams, * they are not physically connected to the conference. * */ ms_message("Call was not yet attached to the conference, it can be removed immediately."); noMediaRestartRequired = true; } else { ms_error("Not in a conference"); return -1; } } Conference::removeParticipant(call); const_cast( L_GET_PRIVATE(L_GET_CPP_PTR_FROM_C_OBJECT(call)->getParams()))->setInConference(false); if (noMediaRestartRequired){ _linphone_call_set_conf_ref(call, nullptr); return 0; } char *str = linphone_call_get_remote_address_as_string(call); ms_message("%s will be removed from conference", str); ms_free(str); int err = 0; if (active_after_removed) { LinphoneCallParams *params = linphone_call_params_copy(linphone_call_get_current_params(call)); linphone_call_params_set_in_conference(params, FALSE); // Reconnect local audio with this call if (isIn()){ ms_message("Leaving conference for reconnecting with unique call"); leave(); } ms_message("Updating call to actually remove from conference"); err = linphone_call_update(call, params); linphone_call_params_unref(params); } else { ms_message("Pausing call to actually remove from conference"); err = _linphone_call_pause(call); } return err; } int LocalConference::remoteParticipantsCount () { int count = getSize(); if (count == 0) return 0; if (!m_localParticipantStream) return count; return count - 1; } int LocalConference::convertConferenceToCall () { if (remoteParticipantsCount() != 1) { ms_error("No unique call remaining in conference"); return -1; } lInfo() << "in LocalConference::convertConferenceToCall ()"; list> calls = L_GET_CPP_PTR_FROM_C_OBJECT(m_core)->getCalls(); for (auto it = calls.begin(); it != calls.end(); it++) { shared_ptr call(*it); if (L_GET_PRIVATE(call->getParams())->getInConference()) { bool_t active_after_removed = isIn(); return removeFromConference(L_GET_C_BACK_PTR(call), active_after_removed); } } return 0; } int LocalConference::removeParticipant (LinphoneCall *call) { char *str = linphone_call_get_remote_address_as_string(call); ms_message("Removing call %s from the conference", str); ms_free(str); int err = removeFromConference(call, FALSE); if (err) { ms_error("Error removing participant from conference"); return err; } if (remoteParticipantsCount() == 1 && m_currentParams->localParticipantEnabled() && !m_terminating) { ms_message("Conference size is 1: need to be converted to plain call"); err = convertConferenceToCall(); } else ms_message("The conference need not to be converted as size is %i", remoteParticipantsCount()); return err; } int LocalConference::removeParticipant (const LinphoneAddress *uri) { const Participant *participant = findParticipant(uri); if (!participant) return -1; LinphoneCall *call = participant->getCall(); if (!call) return -1; return removeParticipant(call); } int LocalConference::terminate () { m_terminating = TRUE; list> calls = L_GET_CPP_PTR_FROM_C_OBJECT(m_core)->getCalls(); for (auto it = calls.begin(); it != calls.end(); it++) { shared_ptr call(*it); if (L_GET_PRIVATE(call->getCurrentParams())->getInConference()) call->terminate(); } Conference::terminate(); m_terminating = FALSE; return 0; } int LocalConference::enter () { if (linphone_core_sound_resources_locked(m_core)) return -1; if (linphone_core_get_current_call(m_core)) _linphone_call_pause(linphone_core_get_current_call(m_core)); addLocalEndpoint(); return 0; } void LocalConference::removeLocalEndpoint () { if (m_localEndpoint) { ms_audio_conference_remove_member(m_conf, m_localEndpoint); ms_audio_endpoint_release_from_stream(m_localEndpoint); m_localEndpoint = nullptr; audio_stream_stop(m_localParticipantStream); m_localParticipantStream = nullptr; rtp_profile_destroy(m_localDummyProfile); } } int LocalConference::leave () { if (isIn()) removeLocalEndpoint(); return 0; } int LocalConference::getSize () const { return Conference::getSize(); } int LocalConference::startRecording (const char *path) { if (!m_conf) { ms_warning("linphone_core_start_conference_recording(): no conference now"); return -1; } if (!m_recordEndpoint) { m_recordEndpoint = ms_audio_endpoint_new_recorder(m_core->factory); ms_audio_conference_add_member(m_conf, m_recordEndpoint); } ms_audio_recorder_endpoint_start(m_recordEndpoint, path); return 0; } int LocalConference::stopRecording () { if (!m_conf) { ms_warning("linphone_core_stop_conference_recording(): no conference now"); return -1; } if (!m_recordEndpoint) { ms_warning("linphone_core_stop_conference_recording(): no record active"); return -1; } ms_audio_recorder_endpoint_stop(m_recordEndpoint); return 0; } void LocalConference::onCallStreamStarting (LinphoneCall *call, bool isPausedByRemote) { const_cast( L_GET_CPP_PTR_FROM_C_OBJECT(call)->getParams())->enableVideo(false); L_GET_CPP_PTR_FROM_C_OBJECT(call)->enableCamera(false); ms_message("LocalConference::onCallStreamStarting(): joining AudioStream [%p] of call [%p] into conference", L_GET_PRIVATE_FROM_C_OBJECT(call)->getMediaStream(LinphoneStreamTypeAudio), call); MSAudioEndpoint *ep = ms_audio_endpoint_get_from_stream( reinterpret_cast(L_GET_PRIVATE_FROM_C_OBJECT(call)->getMediaStream(LinphoneStreamTypeAudio)), TRUE); ms_audio_conference_add_member(m_conf, ep); ms_audio_conference_mute_member(m_conf, ep, isPausedByRemote); _linphone_call_set_endpoint(call, ep); } void LocalConference::onCallStreamStopping (LinphoneCall *call) { ms_audio_conference_remove_member(m_conf, _linphone_call_get_endpoint(call)); ms_audio_endpoint_release_from_stream(_linphone_call_get_endpoint(call)); _linphone_call_set_endpoint(call, nullptr); if (!linphone_call_params_get_in_conference(linphone_call_get_params(call))){ participantUnplugged(call); } } void LocalConference::participantUnplugged(LinphoneCall *call){ int mixer_connected_remote_participants_count = ms_audio_conference_get_size(m_conf); if (m_recordEndpoint) mixer_connected_remote_participants_count--; if (m_localParticipantStream) mixer_connected_remote_participants_count--; ms_message("participantUnplugged(): remote participant count still connected = %i", mixer_connected_remote_participants_count); _linphone_call_set_conf_ref(call, nullptr); if (mixer_connected_remote_participants_count == 0) { if (m_localParticipantStream){ removeLocalEndpoint(); linphone_core_soundcard_hint_check(m_core); } if (m_recordEndpoint) { ms_audio_conference_remove_member(m_conf, m_recordEndpoint); ms_audio_endpoint_destroy(m_recordEndpoint); } setState(LinphoneConferenceStopped); } } void LocalConference::onCallTerminating (LinphoneCall *call) { removeParticipant(call); participantUnplugged(call); } RemoteConference::RemoteConference (LinphoneCore *core, LinphoneConference *conf, const ConferenceParams *params) : Conference(core, conf, params) { m_focusAddr = nullptr; m_focusContact = nullptr; m_focusCall = nullptr; m_coreCbs = nullptr; m_focusAddr = lp_config_get_string(m_core->config, "misc", "conference_focus_addr", ""); m_coreCbs = linphone_factory_create_core_cbs(linphone_factory_get()); linphone_core_cbs_set_call_state_changed(m_coreCbs, callStateChangedCb); linphone_core_cbs_set_transfer_state_changed(m_coreCbs, transferStateChanged); linphone_core_cbs_set_user_data(m_coreCbs, this); _linphone_core_add_callbacks(m_core, m_coreCbs, TRUE); } RemoteConference::~RemoteConference () { terminate(); linphone_core_remove_callbacks(m_core, m_coreCbs); linphone_core_cbs_unref(m_coreCbs); } int RemoteConference::inviteAddresses (const list &addresses, const LinphoneCallParams *params) { ms_error("RemoteConference::inviteAddresses() not implemented"); return -1; } int RemoteConference::addParticipant (LinphoneCall *call) { LinphoneAddress *addr; LinphoneCallParams *params; LinphoneCallLog *callLog; switch (m_state) { case LinphoneConferenceStopped: case LinphoneConferenceStartingFailed: Conference::addParticipant(call); ms_message("Calling the conference focus (%s)", m_focusAddr); addr = linphone_address_new(m_focusAddr); if (!addr) return -1; params = linphone_core_create_call_params(m_core, nullptr); linphone_call_params_enable_video(params, m_currentParams->videoRequested()); m_focusCall = linphone_core_invite_address_with_params(m_core, addr, params); m_localParticipantStream = reinterpret_cast( L_GET_PRIVATE_FROM_C_OBJECT(m_focusCall)->getMediaStream(LinphoneStreamTypeAudio)); m_pendingCalls.push_back(call); callLog = linphone_call_get_call_log(m_focusCall); callLog->was_conference = TRUE; linphone_address_unref(addr); linphone_call_params_unref(params); setState(LinphoneConferenceStarting); return 0; case LinphoneConferenceStarting: Conference::addParticipant(call); if(focusIsReady()) transferToFocus(call); else m_pendingCalls.push_back(call); return 0; case LinphoneConferenceRunning: Conference::addParticipant(call); transferToFocus(call); return 0; default: ms_error("Could not add call %p to the conference. Bad conference state (%s)", call, stateToString(m_state)); return -1; } } int RemoteConference::removeParticipant (const LinphoneAddress *uri) { char *refer_to; LinphoneAddress *refer_to_addr; int res; switch (m_state) { case LinphoneConferenceRunning: if(!findParticipant(uri)) { char *tmp = linphone_address_as_string(uri); ms_error("Conference: could not remove participant '%s': not in the participants list", tmp); ms_free(tmp); return -1; } refer_to_addr = linphone_address_clone(uri); linphone_address_set_method_param(refer_to_addr, "BYE"); refer_to = linphone_address_as_string(refer_to_addr); linphone_address_unref(refer_to_addr); res = linphone_call_get_op(m_focusCall)->refer(refer_to); ms_free(refer_to); if (res == 0) return Conference::removeParticipant(uri); else { char *tmp = linphone_address_as_string(uri); ms_error("Conference: could not remove participant '%s': REFER with BYE has failed", tmp); ms_free(tmp); return -1; } default: ms_error("Cannot remove %s from conference: Bad conference state (%s)", linphone_address_as_string(uri), stateToString(m_state)); return -1; } } int RemoteConference::terminate () { switch (m_state) { case LinphoneConferenceRunning: case LinphoneConferenceStarting: linphone_call_terminate(m_focusCall); break; default: break; } return 0; } int RemoteConference::enter () { if (m_state != LinphoneConferenceRunning) { ms_error("Could not enter in the conference: bad conference state (%s)", stateToString(m_state)); return -1; } LinphoneCallState callState = linphone_call_get_state(m_focusCall); switch (callState) { case LinphoneCallStreamsRunning: break; case LinphoneCallPaused: linphone_call_resume(m_focusCall); break; default: ms_error("Could not join the conference: bad focus call state (%s)", linphone_call_state_to_string(callState)); return -1; } return 0; } int RemoteConference::leave () { if (m_state != LinphoneConferenceRunning) { ms_error("Could not leave the conference: bad conference state (%s)", stateToString(m_state)); return -1; } LinphoneCallState callState = linphone_call_get_state(m_focusCall); switch (callState) { case LinphoneCallPaused: break; case LinphoneCallStreamsRunning: linphone_call_pause(m_focusCall); break; default: ms_error("Could not leave the conference: bad focus call state (%s)", linphone_call_state_to_string(callState)); return -1; } return 0; } bool RemoteConference::isIn () const { if (m_state != LinphoneConferenceRunning) return false; LinphoneCallState callState = linphone_call_get_state(m_focusCall); return callState == LinphoneCallStreamsRunning; } bool RemoteConference::focusIsReady () const { LinphoneCallState focusState; if (!m_focusCall) return false; focusState = linphone_call_get_state(m_focusCall); return (focusState == LinphoneCallStreamsRunning) || (focusState == LinphoneCallPaused); } bool RemoteConference::transferToFocus (LinphoneCall *call) { if (linphone_call_transfer(call, m_focusContact) == 0) { m_transferingCalls.push_back(call); return true; } else { ms_error("Conference: could not transfer call [%p] to %s", call, m_focusContact); return false; } } void RemoteConference::reset () { m_localParticipantStream = nullptr; m_focusAddr = nullptr; if(m_focusContact) { ms_free(m_focusContact); m_focusContact = nullptr; } m_focusCall = nullptr; m_pendingCalls.clear(); m_transferingCalls.clear(); } void RemoteConference::onFocusCallSateChanged (LinphoneCallState state) { list::iterator it; switch (state) { case LinphoneCallConnected: m_focusContact = ms_strdup(linphone_call_get_remote_contact(m_focusCall)); it = m_pendingCalls.begin(); while (it != m_pendingCalls.end()) { LinphoneCall *pendingCall = *it; LinphoneCallState pendingCallState = linphone_call_get_state(pendingCall); if ((pendingCallState == LinphoneCallStreamsRunning) || (pendingCallState == LinphoneCallPaused)) { it = m_pendingCalls.erase(it); transferToFocus(pendingCall); } else it++; } setState(LinphoneConferenceRunning); break; case LinphoneCallError: reset(); Conference::terminate(); setState(LinphoneConferenceStartingFailed); break; case LinphoneCallEnd: reset(); Conference::terminate(); setState(LinphoneConferenceStopped); break; default: break; } } void RemoteConference::onPendingCallStateChanged (LinphoneCall *call, LinphoneCallState state) { switch (state) { case LinphoneCallStreamsRunning: case LinphoneCallPaused: if (m_state == LinphoneConferenceRunning) { m_pendingCalls.remove(call); m_transferingCalls.push_back(call); linphone_call_transfer(call, m_focusContact); } break; case LinphoneCallError: case LinphoneCallEnd: m_pendingCalls.remove(call); Conference::removeParticipant(call); if ((m_participants.size() + m_pendingCalls.size() + m_transferingCalls.size()) == 0) terminate(); break; default: break; } } void RemoteConference::onTransferingCallStateChanged (LinphoneCall* transfered, LinphoneCallState newCallState) { switch (newCallState) { case LinphoneCallConnected: m_transferingCalls.push_back(transfered); findParticipant(transfered)->m_call = nullptr; break; case LinphoneCallError: m_transferingCalls.remove(transfered); Conference::removeParticipant(transfered); if ((m_participants.size() + m_pendingCalls.size() + m_transferingCalls.size()) == 0) terminate(); break; default: break; } } void RemoteConference::callStateChangedCb (LinphoneCore *lc, LinphoneCall *call, LinphoneCallState cstate, const char *message) { LinphoneCoreVTable *vtable = linphone_core_get_current_vtable(lc); RemoteConference *conf = (RemoteConference *)linphone_core_v_table_get_user_data(vtable); if (call == conf->m_focusCall) conf->onFocusCallSateChanged(cstate); else { list::iterator it = find(conf->m_pendingCalls.begin(), conf->m_pendingCalls.end(), call); if (it != conf->m_pendingCalls.end()) conf->onPendingCallStateChanged(call, cstate); } } void RemoteConference::transferStateChanged (LinphoneCore *lc, LinphoneCall *transfered, LinphoneCallState new_call_state) { LinphoneCoreVTable *vtable = linphone_core_get_current_vtable(lc); RemoteConference *conf = (RemoteConference *)linphone_core_v_table_get_user_data(vtable); list::iterator it = find(conf->m_transferingCalls.begin(), conf->m_transferingCalls.end(), transfered); if (it != conf->m_transferingCalls.end()) conf->onTransferingCallStateChanged(transfered, new_call_state); } }//end of namespace MediaConference LINPHONE_END_NAMESPACE using namespace LinphonePrivate::MediaConference; const char *linphone_conference_state_to_string (LinphoneConferenceState state) { return Conference::stateToString(state); } struct _LinphoneConference { belle_sip_object_t base; Conference *conf; }; static void _linphone_conference_uninit (LinphoneConference *conf); BELLE_SIP_DECLARE_NO_IMPLEMENTED_INTERFACES(LinphoneConference); BELLE_SIP_DECLARE_VPTR_NO_EXPORT(LinphoneConference); BELLE_SIP_INSTANCIATE_VPTR(LinphoneConference, belle_sip_object_t, _linphone_conference_uninit, // uninit NULL, // clone NULL, // marshal FALSE // unown ); LinphoneConference *linphone_local_conference_new (LinphoneCore *core) { LinphoneConference *conf = belle_sip_object_new(LinphoneConference); conf->conf = new LocalConference(core, conf); return conf; } LinphoneConference *linphone_local_conference_new_with_params (LinphoneCore *core, const LinphoneConferenceParams *params) { LinphoneConference *conf = belle_sip_object_new(LinphoneConference); conf->conf = new LocalConference(core, conf, ConferenceParams::toCpp(params)); return conf; } LinphoneConference *linphone_remote_conference_new (LinphoneCore *core) { LinphoneConference *conf = belle_sip_object_new(LinphoneConference); conf->conf = new RemoteConference(core, conf); return conf; } LinphoneConference *linphone_remote_conference_new_with_params (LinphoneCore *core, const LinphoneConferenceParams *params) { LinphoneConference *conf = belle_sip_object_new(LinphoneConference); conf->conf = new RemoteConference(core, conf, ConferenceParams::toCpp(params)); return conf; } static void _linphone_conference_uninit (LinphoneConference *conf) { delete conf->conf; } LinphoneConference *linphone_conference_ref (LinphoneConference *conf) { return (LinphoneConference *)belle_sip_object_ref(conf); } void linphone_conference_unref (LinphoneConference *conf) { belle_sip_object_unref(conf); } LinphoneConferenceState linphone_conference_get_state (const LinphoneConference *obj) { return obj->conf->getState(); } int linphone_conference_add_participant (LinphoneConference *obj, LinphoneCall *call) { return obj->conf->addParticipant(call); } LinphoneStatus linphone_conference_remove_participant (LinphoneConference *obj, const LinphoneAddress *uri) { return obj->conf->removeParticipant(uri); } int linphone_conference_remove_participant_with_call (LinphoneConference *obj, LinphoneCall *call) { return obj->conf->removeParticipant(call); } int linphone_conference_terminate (LinphoneConference *obj) { return obj->conf->terminate(); } int linphone_conference_enter (LinphoneConference *obj) { return obj->conf->enter(); } int linphone_conference_leave (LinphoneConference *obj) { return obj->conf->leave(); } bool_t linphone_conference_is_in (const LinphoneConference *obj) { return obj->conf->isIn(); } AudioStream *linphone_conference_get_audio_stream (const LinphoneConference *obj) { return obj->conf->getAudioStream(); } int linphone_conference_mute_microphone (LinphoneConference *obj, bool_t val) { return obj->conf->muteMicrophone(val ? true : false); } bool_t linphone_conference_microphone_is_muted (const LinphoneConference *obj) { return obj->conf->microphoneIsMuted() ? TRUE : FALSE; } float linphone_conference_get_input_volume (const LinphoneConference *obj) { return obj->conf->getInputVolume(); } int linphone_conference_get_size (const LinphoneConference *obj) { return obj->conf->getSize(); } bctbx_list_t *linphone_conference_get_participants (const LinphoneConference *obj) { const list &participants = obj->conf->getParticipants(); bctbx_list_t *participants_list = nullptr; for (auto it = participants.begin(); it != participants.end(); it++) { LinphoneAddress *uri = linphone_address_clone((*it)->getUri()); participants_list = bctbx_list_append(participants_list, uri); } return participants_list; } int linphone_conference_start_recording (LinphoneConference *obj, const char *path) { return obj->conf->startRecording(path); } int linphone_conference_stop_recording (LinphoneConference *obj) { return obj->conf->stopRecording(); } void linphone_conference_on_call_stream_starting (LinphoneConference *obj, LinphoneCall *call, bool_t is_paused_by_remote) { obj->conf->onCallStreamStarting(call, is_paused_by_remote ? true : false); } void linphone_conference_on_call_stream_stopping (LinphoneConference *obj, LinphoneCall *call) { obj->conf->onCallStreamStopping(call); } void linphone_conference_on_call_terminating (LinphoneConference *obj, LinphoneCall *call) { obj->conf->onCallTerminating(call); } bool_t linphone_conference_check_class (LinphoneConference *obj, LinphoneConferenceClass _class) { switch(_class) { case LinphoneConferenceClassLocal: return typeid(obj->conf) == typeid(LocalConference); case LinphoneConferenceClassRemote: return typeid(obj->conf) == typeid(RemoteConference); default: return FALSE; } } LinphoneStatus linphone_conference_invite_participants (LinphoneConference *obj, const bctbx_list_t *addresses, const LinphoneCallParams *params){ return obj->conf->inviteAddresses(toStd(addresses), params); } const char *linphone_conference_get_ID (const LinphoneConference *obj) { return obj->conf->getID(); } void linphone_conference_set_ID (const LinphoneConference *obj, const char *conferenceID) { obj->conf->setID(conferenceID); } LinphoneConferenceParams *linphone_conference_params_new (const LinphoneCore *core) { LinphoneConferenceParams *obj = ConferenceParams::createCObject(core); return obj; } LinphoneConferenceParams *linphone_conference_params_ref (LinphoneConferenceParams *params) { ConferenceParams::toCpp(params)->ref(); return params; } void linphone_conference_params_unref (LinphoneConferenceParams *params) { ConferenceParams::toCpp(params)->unref(); } void linphone_conference_params_free (LinphoneConferenceParams *params) { ConferenceParams::toCpp(params)->unref(); } LinphoneConferenceParams *linphone_conference_params_clone (const LinphoneConferenceParams *params) { return static_cast(ConferenceParams::toCpp(params)->clone())->toC(); } void linphone_conference_params_enable_video (LinphoneConferenceParams *params, bool_t enable) { ConferenceParams::toCpp(params)->enableVideo(enable ? true : false); } bool_t linphone_conference_params_video_enabled (const LinphoneConferenceParams *params) { return ConferenceParams::toCpp(params)->videoRequested() ? TRUE : FALSE; } void linphone_conference_params_enable_local_participant(LinphoneConferenceParams *params, bool_t enable){ ConferenceParams::toCpp(params)->enableLocalParticipant(!!enable); } bool_t linphone_conference_params_local_participant_enabled(const LinphoneConferenceParams *params){ return ConferenceParams::toCpp(params)->localParticipantEnabled(); } void linphone_conference_params_set_state_changed_callback (LinphoneConferenceParams *params, LinphoneConferenceStateChangedCb cb, void *user_data) { ConferenceParams::toCpp(params)->setStateChangedCallback(cb, user_data); } liblinphone-4.4.21/coreapi/conference_private.h000066400000000000000000000121471376572216100215600ustar00rootroot00000000000000/* * Copyright (c) 2010-2019 Belledonne Communications SARL. * * This file is part of Liblinphone. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ /* * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef CONFERENCE_PRIVATE_H #define CONFERENCE_PRIVATE_H #include "linphone/core.h" #include "linphone/conference.h" #ifdef __cplusplus extern "C" { #endif typedef enum { LinphoneConferenceClassLocal, LinphoneConferenceClassRemote } LinphoneConferenceClass; /** * List of states used by #LinphoneConference */ typedef enum { LinphoneConferenceStopped, /*< Initial state */ LinphoneConferenceStarting, /*< A participant has been added but the conference is not running yet */ LinphoneConferenceRunning, /*< The conference is running */ LinphoneConferenceStartingFailed /*< A participant has been added but the initialization of the conference has failed */ } LinphoneConferenceState; /** * Type of the funtion to pass as callback to linphone_conference_params_set_state_changed_callback() * @param conference The conference instance which the state has changed * @param new_state The new state of the conferenece * @param user_data Pointer pass to user_data while linphone_conference_params_set_state_changed_callback() was being called */ typedef void (*LinphoneConferenceStateChangedCb)(LinphoneConference *conference, LinphoneConferenceState new_state, void *user_data); /** * A function to converte a #LinphoneConferenceState into a string */ const char *linphone_conference_state_to_string(LinphoneConferenceState state); /** * Set a callback which will be called when the state of the conferenec is switching * @param params A #LinphoneConferenceParams object * @param cb The callback to call * @param user_data Pointer to pass to the user_data parameter of #LinphoneConferenceStateChangedCb */ void linphone_conference_params_set_state_changed_callback(LinphoneConferenceParams *params, LinphoneConferenceStateChangedCb cb, void *user_data); LinphoneConference *linphone_local_conference_new(LinphoneCore *core); LinphoneConference *linphone_local_conference_new_with_params(LinphoneCore *core, const LinphoneConferenceParams *params); LinphoneConference *linphone_remote_conference_new(LinphoneCore *core); LinphoneConference *linphone_remote_conference_new_with_params(LinphoneCore *core, const LinphoneConferenceParams *params); /** * Get the state of a conference */ LinphoneConferenceState linphone_conference_get_state(const LinphoneConference *obj); int linphone_conference_add_participant(LinphoneConference *obj, LinphoneCall *call); int linphone_conference_remove_participant_with_call(LinphoneConference *obj, LinphoneCall *call); int linphone_conference_terminate(LinphoneConference *obj); int linphone_conference_get_size(const LinphoneConference *obj); int linphone_conference_enter(LinphoneConference *obj); int linphone_conference_leave(LinphoneConference *obj); bool_t linphone_conference_is_in(const LinphoneConference *obj); AudioStream *linphone_conference_get_audio_stream(const LinphoneConference *obj); int linphone_conference_mute_microphone(LinphoneConference *obj, bool_t val); bool_t linphone_conference_microphone_is_muted(const LinphoneConference *obj); float linphone_conference_get_input_volume(const LinphoneConference *obj); int linphone_conference_start_recording(LinphoneConference *obj, const char *path); int linphone_conference_stop_recording(LinphoneConference *obj); void linphone_conference_on_call_stream_starting(LinphoneConference *obj, LinphoneCall *call, bool_t is_paused_by_remote); void linphone_conference_on_call_stream_stopping(LinphoneConference *obj, LinphoneCall *call); void linphone_conference_on_call_terminating(LinphoneConference *obj, LinphoneCall *call); LINPHONE_PUBLIC bool_t linphone_conference_check_class(LinphoneConference *obj, LinphoneConferenceClass _class); #ifdef __cplusplus } #endif #endif //CONFERENCE_PRIVATE_H liblinphone-4.4.21/coreapi/contact_providers_priv.h000066400000000000000000000047551376572216100225150ustar00rootroot00000000000000/* * Copyright (c) 2010-2019 Belledonne Communications SARL. * * This file is part of Liblinphone. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef CONTACT_PROVIDERS_PRIV_H #define CONTACT_PROVIDERS_PRIV_H #include "linphone/core.h" #include "c-wrapper/c-wrapper.h" /* Base for contact search and contact provider */ struct _LinphoneContactSearch{ belle_sip_object_t base; LinphoneContactSearchID id; char* predicate; ContactSearchCallback cb; void* data; }; #define LINPHONE_CONTACT_SEARCH(obj) BELLE_SIP_CAST(obj,LinphoneContactSearch) BELLE_SIP_DECLARE_VPTR_NO_EXPORT(LinphoneContactSearch) struct _LinphoneContactProvider { belle_sip_object_t base; LinphoneCore* lc; }; #define LINPHONE_CONTACT_PROVIDER(obj) BELLE_SIP_CAST(obj,LinphoneContactProvider) typedef LinphoneContactSearch* (*LinphoneContactProviderStartSearchMethod)( LinphoneContactProvider* thiz, const char* predicate, ContactSearchCallback cb, void* data ); typedef unsigned int (*LinphoneContactProviderCancelSearchMethod)( LinphoneContactProvider* thiz, LinphoneContactSearch *request ); BELLE_SIP_DECLARE_CUSTOM_VPTR_BEGIN_NO_EXPORT(LinphoneContactProvider,belle_sip_object_t) const char* name; /*!< Name of the contact provider (LDAP, Google, ...) */ /* pure virtual methods: inheriting objects must implement these */ LinphoneContactProviderStartSearchMethod begin_search; LinphoneContactProviderCancelSearchMethod cancel_search; BELLE_SIP_DECLARE_CUSTOM_VPTR_END /* LDAP search and contact providers */ #define LINPHONE_LDAP_CONTACT_SEARCH(obj) BELLE_SIP_CAST(obj,LinphoneLDAPContactSearch) BELLE_SIP_DECLARE_VPTR_NO_EXPORT(LinphoneLDAPContactSearch) #define LINPHONE_LDAP_CONTACT_PROVIDER(obj) BELLE_SIP_CAST(obj,LinphoneLDAPContactProvider) BELLE_SIP_DECLARE_CUSTOM_VPTR_BEGIN_NO_EXPORT(LinphoneLDAPContactProvider,LinphoneContactProvider) BELLE_SIP_DECLARE_CUSTOM_VPTR_END #endif // CONTACT_PROVIDERS_PRIV_H liblinphone-4.4.21/coreapi/contactprovider.c000066400000000000000000000101601376572216100211110ustar00rootroot00000000000000/* * Copyright (c) 2010-2019 Belledonne Communications SARL. * * This file is part of Liblinphone. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "linphone/contactprovider.h" #include "linphone/core.h" #include "contact_providers_priv.h" /* ############################ * * LinphoneContactSearchRequest * * ############################ */ void linphone_contact_search_init(LinphoneContactSearch* obj, const char* predicate, ContactSearchCallback cb, void* cb_data) { static unsigned int request_id_counter = 1; obj->id = request_id_counter++; // unique id obj->predicate = ms_strdup(predicate?predicate:""); obj->cb = cb; obj->data = cb_data; } static void linphone_contact_search_destroy( LinphoneContactSearch* req) { if( req->predicate ) ms_free(req->predicate); } LinphoneContactSearchID linphone_contact_search_get_id(LinphoneContactSearch* obj) { return obj->id; } const char*linphone_contact_search_get_predicate(LinphoneContactSearch* obj) { return obj->predicate; } void linphone_contact_search_invoke_cb(LinphoneContactSearch* req, MSList* friends) { if( req->cb ) req->cb(req, friends, req->data); } int linphone_contact_search_compare(const void* a, const void* b) { LinphoneContactSearch *ra=((LinphoneContactSearch*)a); LinphoneContactSearch *rb=((LinphoneContactSearch*)b); return !(ra->id == rb->id); // return 0 if id is equal, 1 otherwise } LinphoneContactSearch*linphone_contact_search_ref(void* obj) { return LINPHONE_CONTACT_SEARCH(belle_sip_object_ref(obj)); } void linphone_ldap_contact_search_unref(void* obj) { belle_sip_object_unref(obj); } LinphoneContactSearch* linphone_contact_search_cast(void* obj) { return LINPHONE_CONTACT_SEARCH(obj); } BELLE_SIP_DECLARE_NO_IMPLEMENTED_INTERFACES(LinphoneContactSearch); BELLE_SIP_INSTANCIATE_VPTR(LinphoneContactSearch,belle_sip_object_t, (belle_sip_object_destroy_t)linphone_contact_search_destroy, NULL, // clone NULL, // marshal FALSE ); /* ####################### * * LinphoneContactProvider * * ####################### */ void linphone_contact_provider_init(LinphoneContactProvider* obj, LinphoneCore* lc){ obj->lc = lc; } static void contact_provider_destroy(LinphoneContactProvider* obj){ (void)obj; } LinphoneContactSearch* linphone_contact_provider_begin_search(LinphoneContactProvider* obj, const char* predicate, ContactSearchCallback cb, void* data) { return BELLE_SIP_OBJECT_VPTR(obj,LinphoneContactProvider)->begin_search( LINPHONE_CONTACT_PROVIDER(obj), predicate, cb, data); } unsigned int linphone_contact_provider_cancel_search(LinphoneContactProvider* obj, LinphoneContactSearch* request) { return BELLE_SIP_OBJECT_VPTR(obj,LinphoneContactProvider)->cancel_search( LINPHONE_CONTACT_PROVIDER(obj), request); } LinphoneContactProvider* linphone_contact_provider_ref(void* obj) { return LINPHONE_CONTACT_PROVIDER(belle_sip_object_ref(obj)); } void linphone_contact_provider_unref(void* obj) { belle_sip_object_unref(obj); } LinphoneContactProvider*linphone_contact_provider_cast(void* obj) { return LINPHONE_CONTACT_PROVIDER(obj); } BELLE_SIP_DECLARE_NO_IMPLEMENTED_INTERFACES(LinphoneContactProvider); BELLE_SIP_INSTANCIATE_CUSTOM_VPTR_BEGIN(LinphoneContactProvider) { BELLE_SIP_VPTR_INIT(LinphoneContactProvider,belle_sip_object_t,TRUE), (belle_sip_object_destroy_t) contact_provider_destroy, NULL,/*no clone*/ NULL,/*no marshal*/ BELLE_SIP_DEFAULT_BUFSIZE_HINT }, "", // Pure virtual NULL, /* begin_search -> pure virtual */ NULL /* cancel_search -> pure virtual */ BELLE_SIP_INSTANCIATE_CUSTOM_VPTR_END liblinphone-4.4.21/coreapi/core_private.h000066400000000000000000000021311376572216100203710ustar00rootroot00000000000000/* * Copyright (c) 2010-2019 Belledonne Communications SARL. * * This file is part of Liblinphone. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef _CORE_PRIVATE_H_ #define _CORE_PRIVATE_H_ #include "linphone/types.h" #include "private_structs.h" #include "private_types.h" struct _LinphoneCore { belle_sip_object_t base; std::shared_ptr cppPtr; std::weak_ptr weakCppPtr; int owner; LINPHONE_CORE_STRUCT_FIELDS }; #endif /* _CORE_PRIVATE_H_ */ liblinphone-4.4.21/coreapi/dict.c000066400000000000000000000102531376572216100166310ustar00rootroot00000000000000/* * Copyright (c) 2010-2019 Belledonne Communications SARL. * * This file is part of Liblinphone. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "linphone/core.h" #include "linphone/lpconfig.h" #include "private.h" #include #include #include /** * @addtogroup misc * @{ **/ LinphoneDictionary* linphone_dictionary_new() { return belle_sip_dict_create(); } LinphoneDictionary* linphone_dictionary_clone(const LinphoneDictionary* src) { LinphoneDictionary* cloned = linphone_dictionary_new(); if( cloned ){ belle_sip_dict_clone(src, cloned); } return cloned; } LinphoneDictionary* linphone_dictionary_ref(LinphoneDictionary* obj) { return BELLE_SIP_DICT(belle_sip_object_ref(obj)); } void linphone_dictionary_unref(LinphoneDictionary *obj) { belle_sip_object_unref(obj); } void linphone_dictionary_set_int(LinphoneDictionary* obj, const char* key, int value) { belle_sip_dict_set_int(obj, key, value); } int linphone_dictionary_get_int(LinphoneDictionary* obj, const char* key, int default_value) { return belle_sip_dict_get_int(obj, key, default_value); } void linphone_dictionary_set_string(LinphoneDictionary* obj, const char* key, const char*value) { belle_sip_dict_set_string(obj, key, value); } const char* linphone_dictionary_get_string(LinphoneDictionary* obj, const char* key, const char* default_value) { return belle_sip_dict_get_string(obj, key, default_value); } void linphone_dictionary_set_int64(LinphoneDictionary* obj, const char* key, int64_t value) { belle_sip_dict_set_int64(obj, key, value); } int64_t linphone_dictionary_get_int64(LinphoneDictionary* obj, const char* key, int64_t default_value) { return belle_sip_dict_get_int64(obj, key, default_value); } LinphoneStatus linphone_dictionary_remove(LinphoneDictionary* obj, const char* key) { return belle_sip_dict_remove(obj, key); } void linphone_dictionary_clear(LinphoneDictionary* obj) { belle_sip_dict_clear(obj); } LinphoneStatus linphone_dictionary_haskey(const LinphoneDictionary* obj, const char* key) { return belle_sip_dict_haskey(obj, key); } void linphone_dictionary_foreach(const LinphoneDictionary* obj, void (*apply_func)(const char*, void*, void*), void* userdata) { belle_sip_dict_foreach(obj, apply_func, userdata); } struct lp_config_to_dict { const char* section; const LpConfig* config; LinphoneDictionary* dict; }; static void lp_config_section_to_dict_cb(const char*key, struct lp_config_to_dict* userdata) { const char* value = lp_config_get_string(userdata->config, userdata->section, key, ""); linphone_dictionary_set_string(userdata->dict, key, value); } LinphoneDictionary* lp_config_section_to_dict(const LpConfig* lpconfig, const char* section) { LinphoneDictionary* dict = NULL; struct lp_config_to_dict fd; fd.config = lpconfig; fd.section = section; dict = linphone_dictionary_new(); fd.dict = dict; lp_config_for_each_entry(lpconfig, section, (void (*)(const char*, void*))lp_config_section_to_dict_cb, &fd); return dict; } struct lp_config_from_dict { const char* section; LpConfig* config; }; static void lp_config_dict_dump_cb( const char* key, void* value, void* userdata) { struct lp_config_from_dict* fd= (struct lp_config_from_dict*)userdata; lp_config_set_string(fd->config, fd->section, key, (const char*)value); } void lp_config_load_dict_to_section(LpConfig* lpconfig, const char* section, const LinphoneDictionary* dict) { struct lp_config_from_dict pvdata = { section, lpconfig }; linphone_dictionary_foreach(dict,lp_config_dict_dump_cb, &pvdata); } /** * @} **/ liblinphone-4.4.21/coreapi/ec-calibrator.c000066400000000000000000000314451376572216100204230ustar00rootroot00000000000000/* * Copyright (c) 2010-2019 Belledonne Communications SARL. * * This file is part of Liblinphone. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "private.h" #include "mediastreamer2/mstonedetector.h" #include "mediastreamer2/dtmfgen.h" #include "linphone/lpconfig.h" #include "c-wrapper/c-wrapper.h" static void ecc_init_filters(EcCalibrator *ecc){ unsigned int rate; int channels = 1; int ecc_channels = 1; MSTickerParams params; memset(¶ms, 0, sizeof(params)); params.name="Echo calibrator"; params.prio=MS_TICKER_PRIO_HIGH; ecc->ticker=ms_ticker_new_with_params(¶ms); ecc->sndread=ms_snd_card_create_reader(ecc->capt_card); ms_filter_call_method(ecc->sndread,MS_FILTER_SET_SAMPLE_RATE,&ecc->rate); ms_filter_call_method(ecc->sndread,MS_FILTER_GET_SAMPLE_RATE,&rate); ms_filter_call_method(ecc->sndread,MS_FILTER_SET_NCHANNELS,&ecc_channels); ms_filter_call_method(ecc->sndread,MS_FILTER_GET_NCHANNELS,&channels); ecc->read_resampler=ms_factory_create_filter(ecc->factory, MS_RESAMPLE_ID); ms_filter_call_method(ecc->read_resampler,MS_FILTER_SET_SAMPLE_RATE,&rate); ms_filter_call_method(ecc->read_resampler,MS_FILTER_SET_OUTPUT_SAMPLE_RATE,&ecc->rate); ms_filter_call_method(ecc->read_resampler,MS_FILTER_SET_NCHANNELS,&channels); ms_filter_call_method(ecc->read_resampler,MS_FILTER_SET_OUTPUT_NCHANNELS,&ecc_channels); ecc->det=ms_factory_create_filter(ecc->factory, MS_TONE_DETECTOR_ID); ms_filter_call_method(ecc->det,MS_FILTER_SET_SAMPLE_RATE,&ecc->rate); ecc->rec=ms_factory_create_filter(ecc->factory, MS_VOID_SINK_ID); ms_filter_link(ecc->sndread,0,ecc->read_resampler,0); ms_filter_link(ecc->read_resampler,0,ecc->det,0); ms_filter_link(ecc->det,0,ecc->rec,0); ecc->play=ms_factory_create_filter(ecc->factory, MS_VOID_SOURCE_ID); ecc->gen=ms_factory_create_filter(ecc->factory, MS_DTMF_GEN_ID); ms_filter_call_method(ecc->gen,MS_FILTER_SET_SAMPLE_RATE,&ecc->rate); ecc->write_resampler=ms_factory_create_filter(ecc->factory, MS_RESAMPLE_ID); ecc->sndwrite=ms_snd_card_create_writer(ecc->play_card); ms_filter_call_method(ecc->sndwrite,MS_FILTER_SET_SAMPLE_RATE,&ecc->rate); ms_filter_call_method(ecc->sndwrite,MS_FILTER_GET_SAMPLE_RATE,&rate); ms_filter_call_method(ecc->sndwrite,MS_FILTER_SET_NCHANNELS,&ecc_channels); ms_filter_call_method(ecc->sndwrite,MS_FILTER_GET_NCHANNELS,&channels); ms_filter_call_method(ecc->write_resampler,MS_FILTER_SET_SAMPLE_RATE,&ecc->rate); ms_filter_call_method(ecc->write_resampler,MS_FILTER_SET_OUTPUT_SAMPLE_RATE,&rate); ms_filter_call_method(ecc->write_resampler,MS_FILTER_SET_NCHANNELS,&ecc_channels); ms_filter_call_method(ecc->write_resampler,MS_FILTER_SET_OUTPUT_NCHANNELS,&channels); ms_filter_link(ecc->play,0,ecc->gen,0); ms_filter_link(ecc->gen,0,ecc->write_resampler,0); ms_filter_link(ecc->write_resampler,0,ecc->sndwrite,0); ms_ticker_attach(ecc->ticker,ecc->sndread); ms_ticker_attach(ecc->ticker,ecc->play); if (ecc->audio_init_cb != NULL) { (*ecc->audio_init_cb)(ecc->cb_data); } } static void ecc_deinit_filters(EcCalibrator *ecc){ if (ecc->audio_uninit_cb != NULL) { (*ecc->audio_uninit_cb)(ecc->cb_data); } ms_ticker_detach(ecc->ticker,ecc->sndread); ms_ticker_detach(ecc->ticker,ecc->play); ms_filter_unlink(ecc->play,0,ecc->gen,0); ms_filter_unlink(ecc->gen,0,ecc->write_resampler,0); ms_filter_unlink(ecc->write_resampler,0,ecc->sndwrite,0); ms_filter_unlink(ecc->sndread,0,ecc->read_resampler,0); ms_filter_unlink(ecc->read_resampler,0,ecc->det,0); ms_filter_unlink(ecc->det,0,ecc->rec,0); ms_filter_destroy(ecc->sndread); ms_filter_destroy(ecc->det); ms_filter_destroy(ecc->rec); ms_filter_destroy(ecc->play); ms_filter_destroy(ecc->gen); ms_filter_destroy(ecc->read_resampler); ms_filter_destroy(ecc->write_resampler); ms_filter_destroy(ecc->sndwrite); ms_ticker_destroy(ecc->ticker); } static void on_tone_sent(void *data, MSFilter *f, unsigned int event_id, void *arg){ MSDtmfGenEvent *ev=(MSDtmfGenEvent*)arg; EcCalibrator *ecc=(EcCalibrator*)data; if (ev->tone_name[0] != '\0'){ ecc->acc-=ev->tone_start_time; ms_message("Sent tone at %u",(unsigned int)ev->tone_start_time); } } static bool_t is_valid_tone(EcCalibrator *ecc, MSToneDetectorEvent *ev){ bool_t *toneflag=NULL; if (strcmp(ev->tone_name,"freq1")==0){ toneflag=&ecc->freq1; }else if (strcmp(ev->tone_name,"freq2")==0){ toneflag=&ecc->freq2; }else if (strcmp(ev->tone_name,"freq3")==0){ toneflag=&ecc->freq3; }else{ ms_error("Calibrator bug."); return FALSE; } if (*toneflag){ ms_message("Duplicated tone event, ignored."); return FALSE; } *toneflag=TRUE; return TRUE; } static void on_tone_received(void *data, MSFilter *f, unsigned int event_id, void *arg){ MSToneDetectorEvent *ev=(MSToneDetectorEvent*)arg; EcCalibrator *ecc=(EcCalibrator*)data; if (is_valid_tone(ecc,ev)){ ecc->acc+=ev->tone_start_time; ms_message("Received tone at %u",(unsigned int)ev->tone_start_time); } } static void ecc_play_tones(EcCalibrator *ecc){ MSDtmfGenCustomTone tone; MSToneDetectorDef expected_tone; memset(&tone,0,sizeof(tone)); memset(&expected_tone,0,sizeof(expected_tone)); ms_filter_add_notify_callback(ecc->det,on_tone_received,ecc,TRUE); /* configure the tones to be scanned */ strncpy(expected_tone.tone_name,"freq1",sizeof(expected_tone.tone_name)); expected_tone.frequency=(int)2349.32; expected_tone.min_duration=40; expected_tone.min_amplitude=0.1f; ms_filter_call_method (ecc->det,MS_TONE_DETECTOR_ADD_SCAN,&expected_tone); strncpy(expected_tone.tone_name,"freq2",sizeof(expected_tone.tone_name)); expected_tone.frequency=(int)2637.02; expected_tone.min_duration=40; expected_tone.min_amplitude=0.1f; ms_filter_call_method (ecc->det,MS_TONE_DETECTOR_ADD_SCAN,&expected_tone); strncpy(expected_tone.tone_name,"freq3",sizeof(expected_tone.tone_name)); expected_tone.frequency=(int)2093; expected_tone.min_duration=40; expected_tone.min_amplitude=0.1f; ms_filter_call_method (ecc->det,MS_TONE_DETECTOR_ADD_SCAN,&expected_tone); /*play an initial tone to startup the audio playback/capture*/ tone.frequencies[0]=140; tone.duration=1000; tone.amplitude=0.5; ms_filter_call_method(ecc->gen,MS_DTMF_GEN_PLAY_CUSTOM,&tone); ms_sleep(2); ms_filter_add_notify_callback(ecc->gen,on_tone_sent,ecc,TRUE); /* play the three tones*/ if (ecc->play_cool_tones){ strncpy(tone.tone_name, "D", sizeof(tone.tone_name)); tone.frequencies[0]=(int)2349.32; tone.duration=100; ms_filter_call_method(ecc->gen,MS_DTMF_GEN_PLAY_CUSTOM,&tone); ms_usleep(300000); strncpy(tone.tone_name, "E", sizeof(tone.tone_name)); tone.frequencies[0]=(int)2637.02; tone.duration=100; ms_filter_call_method(ecc->gen,MS_DTMF_GEN_PLAY_CUSTOM,&tone); ms_usleep(300000); strncpy(tone.tone_name, "C", sizeof(tone.tone_name)); tone.frequencies[0]=(int)2093; tone.duration=100; ms_filter_call_method(ecc->gen,MS_DTMF_GEN_PLAY_CUSTOM,&tone); ms_usleep(300000); }else{ strncpy(tone.tone_name, "C", sizeof(tone.tone_name)); tone.frequencies[0]=(int)2093; tone.duration=100; ms_filter_call_method(ecc->gen,MS_DTMF_GEN_PLAY_CUSTOM,&tone); ms_usleep(300000); strncpy(tone.tone_name, "D", sizeof(tone.tone_name)); tone.frequencies[0]=(int)2349.32; tone.duration=100; ms_filter_call_method(ecc->gen,MS_DTMF_GEN_PLAY_CUSTOM,&tone); ms_usleep(300000); strncpy(tone.tone_name, "E", sizeof(tone.tone_name)); tone.frequencies[0]=(int)2637.02; tone.duration=100; ms_filter_call_method(ecc->gen,MS_DTMF_GEN_PLAY_CUSTOM,&tone); ms_usleep(300000); } /*these two next ones are for lyrism*/ if (ecc->play_cool_tones){ tone.tone_name[0]='\0'; tone.frequencies[0]=(int)1046.5; tone.duration=400; ms_filter_call_method(ecc->gen,MS_DTMF_GEN_PLAY_CUSTOM,&tone); ms_usleep(300000); tone.tone_name[0]='\0'; tone.frequencies[0]=(int)1567.98; tone.duration=400; ms_filter_call_method(ecc->gen,MS_DTMF_GEN_PLAY_CUSTOM,&tone); } ms_sleep(1); if (ecc->freq1 && ecc->freq2 && ecc->freq3) { int delay=(int)(ecc->acc/3); if (delay<0){ ms_error("Quite surprising calibration result, delay=%i",delay); ecc->status=LinphoneEcCalibratorFailed; }else{ ms_message("Echo calibration estimated delay to be %i ms",delay); ecc->delay=delay; ecc->status=LinphoneEcCalibratorDone; } } else if ((ecc->freq1 || ecc->freq2 || ecc->freq3)==FALSE) { ms_message("Echo calibration succeeded, no echo has been detected"); ecc->status = LinphoneEcCalibratorDoneNoEcho; } else { ecc->status = LinphoneEcCalibratorFailed; } if (ecc->status == LinphoneEcCalibratorFailed) { ms_error("Echo calibration failed."); } } static void * ecc_thread(void *p){ EcCalibrator *ecc=(EcCalibrator*)p; ecc_init_filters(ecc); ecc_play_tones(ecc); ecc_deinit_filters(ecc); ms_thread_exit(NULL); return NULL; } EcCalibrator * ec_calibrator_new(MSFactory *factory, MSSndCard *play_card, MSSndCard *capt_card, unsigned int rate, LinphoneEcCalibrationCallback cb, LinphoneEcCalibrationAudioInit audio_init_cb, LinphoneEcCalibrationAudioUninit audio_uninit_cb, void *cb_data){ EcCalibrator *ecc=ms_new0(EcCalibrator,1); ecc->rate=rate; ecc->cb=cb; ecc->cb_data=cb_data; ecc->audio_init_cb=audio_init_cb; ecc->audio_uninit_cb=audio_uninit_cb; ecc->capt_card=capt_card; ecc->play_card=play_card; ecc->factory=factory; return ecc; } void ec_calibrator_start(EcCalibrator *ecc){ ms_thread_create(&ecc->thread,NULL,ecc_thread,ecc); } LinphoneEcCalibratorStatus ec_calibrator_get_status(EcCalibrator *ecc){ return ecc->status; } void ec_calibrator_destroy(EcCalibrator *ecc){ if (ecc->thread != 0) ms_thread_join(ecc->thread,NULL); ms_free(ecc); } int linphone_core_start_echo_calibration(LinphoneCore *lc, LinphoneEcCalibrationCallback cb, LinphoneEcCalibrationAudioInit audio_init_cb, LinphoneEcCalibrationAudioUninit audio_uninit_cb, void *cb_data){ unsigned int rate; if (lc->ecc!=NULL){ ms_error("Echo calibration is still on going !"); return -1; } rate = (unsigned int)lp_config_get_int(lc->config,"sound","echo_cancellation_rate",8000); lc->ecc=ec_calibrator_new(lc->factory, lc->sound_conf.play_sndcard,lc->sound_conf.capt_sndcard,rate,cb,audio_init_cb,audio_uninit_cb,cb_data); lc->ecc->play_cool_tones = !!lp_config_get_int(lc->config, "sound", "ec_calibrator_cool_tones", 0); ec_calibrator_start(lc->ecc); return 0; } static void _ec_calibration_result_cb(LinphoneCore *lc, LinphoneEcCalibratorStatus status, int delay_ms, void *user_data) { linphone_core_notify_ec_calibration_result(lc, status, delay_ms); } static void _ec_calibration_audio_init_cb(void *user_data) { LinphoneCore *lc = (LinphoneCore *)user_data; linphone_core_notify_ec_calibration_audio_init(lc); } static void _ec_calibration_audio_uninit_cb(void *user_data) { LinphoneCore *lc = (LinphoneCore *)user_data; linphone_core_notify_ec_calibration_audio_uninit(lc); } LinphoneStatus linphone_core_start_echo_canceller_calibration(LinphoneCore *lc) { unsigned int rate; if (lc->ecc!=NULL){ ms_error("Echo calibration is still on going !"); return -1; } rate = (unsigned int)lp_config_get_int(lc->config,"sound","echo_cancellation_rate",8000); lc->ecc=ec_calibrator_new(lc->factory, lc->sound_conf.play_sndcard, lc->sound_conf.capt_sndcard, rate, _ec_calibration_result_cb, _ec_calibration_audio_init_cb, _ec_calibration_audio_uninit_cb, lc); lc->ecc->play_cool_tones = !!lp_config_get_int(lc->config, "sound", "ec_calibrator_cool_tones", 0); ec_calibrator_start(lc->ecc); return 0; } bool_t linphone_core_has_builtin_echo_canceller(LinphoneCore *lc) { MSFactory * factory = linphone_core_get_ms_factory(lc); MSDevicesInfo *devices = ms_factory_get_devices_info(factory); SoundDeviceDescription *sound_description = ms_devices_info_get_sound_device_description(devices); if (sound_description == NULL) return FALSE; if (sound_description->flags & DEVICE_HAS_BUILTIN_AEC) return TRUE; return FALSE; } bool_t linphone_core_is_echo_canceller_calibration_required(LinphoneCore *lc) { MSFactory * factory = linphone_core_get_ms_factory(lc); MSDevicesInfo *devices = ms_factory_get_devices_info(factory); SoundDeviceDescription *sound_description = ms_devices_info_get_sound_device_description(devices); if (sound_description == NULL) return TRUE; if (sound_description->flags & DEVICE_HAS_BUILTIN_AEC) return FALSE; if (sound_description->delay != 0) return FALSE; return TRUE; } liblinphone-4.4.21/coreapi/echo-tester.c000066400000000000000000000064141376572216100201340ustar00rootroot00000000000000/* * Copyright (c) 2010-2019 Belledonne Communications SARL. * * This file is part of Liblinphone. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "private.h" #include "mediastreamer2/msfilter.h" #include "mediastreamer2/mssndcard.h" #include "mediastreamer2/msticker.h" #include EchoTester* ec_tester_new(MSFactory *factory, MSSndCard *capture_card, MSSndCard *playback_card, unsigned int rate) { EchoTester *ect = ms_new0(EchoTester,1); ect->factory = factory; ect->capture_card = capture_card; ect->playback_card = playback_card; ect->rate = rate; return ect; } static void ect_init_filters(EchoTester *ect) { unsigned int rate; int channels = 1; int ect_channels = 1; MSTickerParams params; memset(¶ms, 0, sizeof(params)); params.name="Echo tester"; params.prio=MS_TICKER_PRIO_HIGH; ect->ticker=ms_ticker_new_with_params(¶ms); ect->in = ms_snd_card_create_reader(ect->capture_card); ect->out = ms_snd_card_create_writer(ect->playback_card); ms_filter_call_method(ect->in,MS_FILTER_SET_SAMPLE_RATE,&ect->rate); ms_filter_call_method(ect->in,MS_FILTER_GET_SAMPLE_RATE,&rate); ms_filter_call_method(ect->in,MS_FILTER_SET_NCHANNELS,&ect_channels); ms_filter_call_method(ect->in,MS_FILTER_GET_NCHANNELS,&channels); ms_filter_call_method(ect->out,MS_FILTER_SET_SAMPLE_RATE,&ect->rate); ms_filter_call_method(ect->out,MS_FILTER_SET_OUTPUT_SAMPLE_RATE,&rate); ms_filter_call_method(ect->out,MS_FILTER_SET_NCHANNELS,&ect_channels); ms_filter_call_method(ect->out,MS_FILTER_SET_OUTPUT_NCHANNELS,&channels); ms_filter_link(ect->in,0,ect->out,0); ms_ticker_attach(ect->ticker,ect->in); ms_ticker_attach(ect->ticker,ect->out); } static void ect_uninit_filters(EchoTester *ect) { ms_ticker_detach(ect->ticker,ect->in); ms_ticker_detach(ect->ticker,ect->out); ms_filter_unlink(ect->in,0,ect->out,0); ms_filter_destroy(ect->in); ms_filter_destroy(ect->out); ms_ticker_destroy(ect->ticker); } void ec_tester_destroy(EchoTester *ect) { ms_free(ect); } LinphoneStatus linphone_core_start_echo_tester(LinphoneCore *lc, unsigned int rate) { if (lc->ect != NULL) { ms_error("Echo tester is still on going !"); return -1; } lc->ect = ec_tester_new(lc->factory, lc->sound_conf.capt_sndcard ,lc->sound_conf.play_sndcard, rate); ect_init_filters(lc->ect); return 1; } LinphoneStatus linphone_core_stop_echo_tester(LinphoneCore *lc) { if (lc->ect == NULL) { ms_error("Echo tester is not running !"); return -1; } ect_uninit_filters(lc->ect); ec_tester_destroy(lc->ect); lc->ect = NULL; return 1; } liblinphone-4.4.21/coreapi/enum.c000066400000000000000000000076501376572216100166610ustar00rootroot00000000000000/* * Copyright (c) 2010-2019 Belledonne Communications SARL. * * This file is part of Liblinphone. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ /* enum lookup code */ #ifndef _WIN32_WCE #include #endif #include #include "enum.h" #define DNS_ANSWER_MAX_SIZE 2048 static char *create_enum_domain(const char *number){ long len=(long)strlen(number); char *domain=reinterpret_cast(ms_malloc((size_t)(len*2)+10)); long i,j; for (i=0,j=len-1;j>=0;j--){ domain[i]=number[j]; i++; domain[i]='.'; i++; } strcpy(&domain[i],"e164.arpa"); ms_message("enum domain for %s is %s",number,domain); return domain; } static bool_t is_a_number(const char *str){ char *p=(char *)str; bool_t res=FALSE; bool_t space_found=FALSE; for(;;p++){ switch(p[0]){ case '9': case '8': case '7': case '6': case '5': case '4': case '3': case '2': case '1': case '0': res=TRUE; if (space_found) return FALSE; /* avoid splited numbers */ break; case '\0': return res; break; case ' ': space_found=TRUE; break; default: return FALSE; } } return FALSE; } //4970072278724 bool_t is_enum(const char *sipaddress, char **enum_domain){ const char *p; p=strstr(sipaddress,"sip:"); if (p==NULL) return FALSE; /* enum should look like sip:4369959250*/ else p+=4; if (is_a_number(p)){ if (enum_domain!=NULL){ *enum_domain=create_enum_domain(p); } return TRUE; } return FALSE; } int enum_lookup(const char *enum_domain, enum_lookup_res_t **res){ int err; //char dns_answer[DNS_ANSWER_MAX_SIZE]; char *begin,*end; char *host_result, *command; int i; bool_t forkok; /* ns_msg handle; int count; memset(&handle,0,sizeof(handle)); *res=NULL; ms_message("Resolving %s...",enum_domain); err=res_search(enum_domain,ns_c_in,ns_t_naptr,dns_answer,DNS_ANSWER_MAX_SIZE); if (err<0){ ms_warning("Error resolving enum:",herror(h_errno)); return -1; } ns_initparse(dns_answer,DNS_ANSWER_MAX_SIZE,&handle); count=ns_msg_count(handle,ns_s_an); for(i=0;i(ms_malloc0(sizeof(enum_lookup_res_t))); err=0; for(i=0;isip_address[i]=ms_strdup(begin); err++; begin=strstr(end+1,"sip:"); if (begin==NULL) break; } ms_free(host_result); return err; parse_error: ms_free(*res); ms_free(host_result); *res=NULL; ms_warning("Parse error in enum_lookup()."); return -1; } void enum_lookup_res_free(enum_lookup_res_t *res){ int i; for (i=0;isip_address[i]!=NULL) ms_free(res->sip_address[i]); } ms_free(res); } liblinphone-4.4.21/coreapi/enum.h000066400000000000000000000021661376572216100166630ustar00rootroot00000000000000/* * Copyright (c) 2010-2019 Belledonne Communications SARL. * * This file is part of Liblinphone. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef ENUM_LOOKUP_H #define ENUM_LOOKUP_H #include "private.h" #define MAX_ENUM_LOOKUP_RESULTS 10 typedef struct enum_lookup_res{ char *sip_address[MAX_ENUM_LOOKUP_RESULTS]; }enum_lookup_res_t; bool_t is_enum(const char *sipaddress, char **enum_domain); int enum_lookup(const char *enum_domain, enum_lookup_res_t **res); void enum_lookup_res_free(enum_lookup_res_t *res); #endif liblinphone-4.4.21/coreapi/error_info.c000066400000000000000000000241451376572216100200570ustar00rootroot00000000000000/* * Copyright (c) 2010-2019 Belledonne Communications SARL. * * This file is part of Liblinphone. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "linphone/core.h" #include "c-wrapper/c-wrapper.h" // TODO: From coreapi. Remove me later. #include "private.h" BELLE_SIP_DECLARE_NO_IMPLEMENTED_INTERFACES(LinphoneErrorInfo); static void linphone_error_info_reset(LinphoneErrorInfo *ei); static void error_info_destroy(LinphoneErrorInfo *ei){ linphone_error_info_reset(ei); } static void error_info_clone(LinphoneErrorInfo *ei, const LinphoneErrorInfo *other){ linphone_error_info_set_reason(ei, linphone_error_info_get_reason(other)); ei->protocol = bctbx_strdup(other->protocol); ei->phrase = bctbx_strdup(other->phrase); ei->warnings = bctbx_strdup(other->warnings); ei->full_string = bctbx_strdup(other->full_string); ei->protocol_code = other->protocol_code; ei->retry_after = other->retry_after; } BELLE_SIP_INSTANCIATE_VPTR(LinphoneErrorInfo, belle_sip_object_t, error_info_destroy, // destroy error_info_clone, // clone NULL, // Marshall FALSE ); LinphoneErrorInfo *linphone_error_info_new(void){ LinphoneErrorInfo *ei = belle_sip_object_new(LinphoneErrorInfo); return ei; } LinphoneErrorInfo* linphone_error_info_ref ( LinphoneErrorInfo* ei ) { return (LinphoneErrorInfo*) belle_sip_object_ref(ei); } void linphone_error_info_unref ( LinphoneErrorInfo* ei ) { belle_sip_object_unref(ei); } const char *linphone_reason_to_string(LinphoneReason err){ switch(err) { case LinphoneReasonNone: return "No error"; case LinphoneReasonNoResponse: return "No response"; case LinphoneReasonForbidden: return "Bad credentials"; case LinphoneReasonDeclined: return "Call declined"; case LinphoneReasonNotFound: return "User not found"; case LinphoneReasonNotAnswered: return "Not answered"; case LinphoneReasonBusy: return "Busy"; case LinphoneReasonMedia: return "Incompatible media capabilities"; case LinphoneReasonIOError: return "IO error"; case LinphoneReasonDoNotDisturb: return "Do not disturb"; case LinphoneReasonUnauthorized: return "Unauthorized"; case LinphoneReasonNotAcceptable: return "Not acceptable here"; case LinphoneReasonNoMatch: return "No match"; case LinphoneReasonMovedPermanently: return "Moved permanently"; case LinphoneReasonGone: return "Gone"; case LinphoneReasonTemporarilyUnavailable: return "Temporarily unavailable"; case LinphoneReasonAddressIncomplete: return "Address incomplete"; case LinphoneReasonNotImplemented: return "Not implemented"; case LinphoneReasonBadGateway: return "Bad gateway"; case LinphoneReasonSessionIntervalTooSmall: return "Session Interval Too Small"; case LinphoneReasonServerTimeout: return "Server timeout"; case LinphoneReasonUnknown: return "Unknown error"; } return "unknown error"; } typedef struct _error_code_reason_map { int error_code; LinphoneReason reason; } error_code_reason_map_t; static const error_code_reason_map_t error_code_reason_map[] = { { 200, LinphoneReasonNone }, { 301, LinphoneReasonMovedPermanently }, { 400, LinphoneReasonUnknown }, { 401, LinphoneReasonUnauthorized }, { 403, LinphoneReasonForbidden }, { 404, LinphoneReasonNotFound }, { 410, LinphoneReasonGone }, { 415, LinphoneReasonUnsupportedContent }, { 480, LinphoneReasonTemporarilyUnavailable }, { 481, LinphoneReasonNoMatch }, { 484, LinphoneReasonAddressIncomplete }, { 486, LinphoneReasonBusy }, { 488, LinphoneReasonNotAcceptable }, { 501, LinphoneReasonNotImplemented }, { 502, LinphoneReasonBadGateway }, { 503, LinphoneReasonIOError }, { 504, LinphoneReasonServerTimeout }, { 600, LinphoneReasonDoNotDisturb }, { 603, LinphoneReasonDeclined } }; static const error_code_reason_map_t retry_after_code_reason_map[] = { { 404, LinphoneReasonNotFound }, { 480, LinphoneReasonTemporarilyUnavailable }, { 486, LinphoneReasonBusy }, { 503, LinphoneReasonIOError }, { 600, LinphoneReasonDoNotDisturb }, { 603, LinphoneReasonDeclined } }; bool_t linphone_error_code_is_retry_after(int err) { size_t i; for (i = 0; i < (sizeof(retry_after_code_reason_map) / sizeof(retry_after_code_reason_map[0])); i++) { if (retry_after_code_reason_map[i].error_code == err) return true; } return false; } LinphoneReason linphone_error_code_to_reason(int err) { size_t i; for (i = 0; i < (sizeof(error_code_reason_map) / sizeof(error_code_reason_map[0])); i++) { if (error_code_reason_map[i].error_code == err) return error_code_reason_map[i].reason; } return LinphoneReasonUnknown; } int linphone_reason_to_error_code(LinphoneReason reason) { size_t i; for (i = 0; i < (sizeof(error_code_reason_map) / sizeof(error_code_reason_map[0])); i++) { if (error_code_reason_map[i].reason == reason) return error_code_reason_map[i].error_code; } return 400; } static void linphone_error_info_reset(LinphoneErrorInfo *ei){ ei->reason = LinphoneReasonNone; STRING_RESET(ei->protocol); STRING_RESET(ei->phrase); STRING_RESET(ei->full_string); STRING_RESET(ei->warnings); ei->protocol_code = 0; ei->retry_after = 0; if (ei->sub_ei) { linphone_error_info_unref(ei->sub_ei); ei->sub_ei = NULL; } } void linphone_error_info_from_sal(LinphoneErrorInfo *ei, const SalErrorInfo *sei){ ei->reason = linphone_reason_from_sal(sei->reason); ei->phrase = bctbx_strdup(sei->status_string); ei->full_string = bctbx_strdup(sei->full_string); ei->warnings = bctbx_strdup(sei->warnings); ei->protocol_code = sei->protocol_code; ei->retry_after = sei->retry_after; ei->protocol = bctbx_strdup(sei->protocol); } /* If a reason header is provided (in reason_ei), then create a sub LinphoneErrorInfo attached to the first one, unless the reason header is in the request, in which case no primary error is given.*/ void linphone_error_info_from_sal_reason_ei(LinphoneErrorInfo *ei, const SalErrorInfo *reason_ei){ if (ei->reason == LinphoneReasonNone){ /*no primary error given*/ linphone_error_info_reset(ei); linphone_error_info_from_sal(ei, reason_ei); return; } if (ei->sub_ei){ if (reason_ei->reason == SalReasonNone){ linphone_error_info_unref(ei->sub_ei); ei->sub_ei = NULL; } }else{ if (reason_ei->reason != SalReasonNone){ ei->sub_ei = linphone_error_info_new(); } } if (reason_ei->reason != SalReasonNone){ linphone_error_info_from_sal(ei->sub_ei, reason_ei); } } void linphone_error_info_from_sal_op(LinphoneErrorInfo *ei, const LinphonePrivate::SalOp *op){ if (op==NULL) { /*leave previous values in LinphoneErrorInfo, the op may have been released already.*/ return; }else{ const SalErrorInfo *sei; linphone_error_info_reset(ei); sei = op->getErrorInfo(); linphone_error_info_from_sal(ei, sei); sei = op->getReasonErrorInfo(); linphone_error_info_from_sal_reason_ei(ei, sei); } } void linphone_error_info_fields_to_sal(const LinphoneErrorInfo* ei, SalErrorInfo* sei){ sei->reason = linphone_reason_to_sal(linphone_error_info_get_reason(ei)); sei->status_string = bctbx_strdup(ei->phrase); sei->full_string = bctbx_strdup(ei->full_string); sei->warnings = bctbx_strdup(ei->warnings); sei->protocol_code = ei->protocol_code; sei->retry_after = ei->retry_after; sei->protocol = bctbx_strdup(ei->protocol); } void linphone_error_info_to_sal(const LinphoneErrorInfo* ei, SalErrorInfo* sei){ linphone_error_info_fields_to_sal(ei, sei); if (ei->sub_ei !=NULL) { linphone_error_info_to_sal(ei->sub_ei, sei->sub_sei); } } void linphone_error_info_set(LinphoneErrorInfo *ei, const char *protocol, LinphoneReason reason, int code, const char *status_string, const char *warning){ linphone_error_info_reset(ei); ei->reason = reason; ei->protocol_code = code; ei->protocol = bctbx_strdup(protocol ? protocol : "SIP"); ei->phrase = bctbx_strdup(status_string); ei->warnings = bctbx_strdup(warning); } int linphone_error_info_get_retry_after(const LinphoneErrorInfo *ei){ return ei->retry_after; } LinphoneReason linphone_error_info_get_reason(const LinphoneErrorInfo *ei) { return ei->reason; } const char *linphone_error_info_get_protocol(const LinphoneErrorInfo *ei){ return ei->protocol; } const char *linphone_error_info_get_phrase(const LinphoneErrorInfo *ei) { return ei->phrase; } /*deprecated, kept for binary compatibility*/ const char *linphone_error_info_get_details(const LinphoneErrorInfo *ei){ return linphone_error_info_get_warnings(ei); } const char *linphone_error_info_get_warnings(const LinphoneErrorInfo *ei) { return ei->warnings; } int linphone_error_info_get_protocol_code(const LinphoneErrorInfo *ei) { return ei->protocol_code; } LinphoneErrorInfo * linphone_error_info_get_sub_error_info(const LinphoneErrorInfo *ei){ return ei->sub_ei; } void linphone_error_info_set_retry_after(LinphoneErrorInfo *ei, int retry_after){ ei->retry_after = retry_after; } void linphone_error_info_set_reason(LinphoneErrorInfo *ei, LinphoneReason reason){ ei->reason = reason; } void linphone_error_info_set_protocol(LinphoneErrorInfo *ei, const char *proto){ STRING_SET(ei->protocol, proto); } void linphone_error_info_set_protocol_code(LinphoneErrorInfo *ei, int code){ ei->protocol_code = code; } void linphone_error_info_set_phrase(LinphoneErrorInfo *ei, const char *phrase){ STRING_SET(ei->phrase, phrase); } void linphone_error_info_set_warnings(LinphoneErrorInfo *ei, const char *warnings){ STRING_SET(ei->warnings, warnings); } void linphone_error_info_set_sub_error_info(LinphoneErrorInfo *ei, LinphoneErrorInfo *appended_ei){ if (appended_ei != NULL){ linphone_error_info_ref(appended_ei); } if (ei->sub_ei){ linphone_error_info_unref(ei->sub_ei); } ei->sub_ei = appended_ei; } liblinphone-4.4.21/coreapi/event.c000066400000000000000000000531421376572216100170330ustar00rootroot00000000000000/* * Copyright (c) 2010-2019 Belledonne Communications SARL. * * This file is part of Liblinphone. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "linphone/event.h" #include "linphone/lpconfig.h" #include "sal/event-op.h" #include "c-wrapper/c-wrapper.h" // TODO: From coreapi. Remove me later. #include "private.h" using namespace LinphonePrivate; const char * linphone_subscription_dir_to_string(LinphoneSubscriptionDir dir){ switch(dir){ case LinphoneSubscriptionIncoming: return "LinphoneSubscriptionIncoming"; case LinphoneSubscriptionOutgoing: return "LinphoneSubscriptionOutgoing"; case LinphoneSubscriptionInvalidDir: return "LinphoneSubscriptionInvalidDir"; } return "INVALID"; } LinphoneSubscriptionState linphone_subscription_state_from_sal(SalSubscribeStatus ss){ switch(ss){ case SalSubscribeNone: return LinphoneSubscriptionNone; case SalSubscribePending: return LinphoneSubscriptionPending; case SalSubscribeTerminated: return LinphoneSubscriptionTerminated; case SalSubscribeActive: return LinphoneSubscriptionActive; } return LinphoneSubscriptionNone; } const char *linphone_subscription_state_to_string(LinphoneSubscriptionState state){ switch(state){ case LinphoneSubscriptionNone: return "LinphoneSubscriptionNone"; case LinphoneSubscriptionIncomingReceived: return "LinphoneSubscriptionIncomingReceived"; case LinphoneSubscriptionOutgoingProgress: return "LinphoneSubscriptionOutgoingProgress"; case LinphoneSubscriptionPending: return "LinphoneSubscriptionPending"; case LinphoneSubscriptionActive: return "LinphoneSubscriptionActive"; case LinphoneSubscriptionTerminated: return "LinphoneSubscriptionTerminated"; case LinphoneSubscriptionError: return "LinphoneSubscriptionError"; case LinphoneSubscriptionExpiring: return "LinphoneSubscriptionExpiring"; } return NULL; } LINPHONE_PUBLIC const char *linphone_publish_state_to_string(LinphonePublishState state){ switch(state){ case LinphonePublishNone: return "LinphonePublishNone"; case LinphonePublishProgress: return "LinphonePublishProgress"; case LinphonePublishOk: return "LinphonePublishOk"; case LinphonePublishError: return "LinphonePublishError"; case LinphonePublishCleared: return "LinphonePublishCleared"; case LinphonePublishExpiring: return "LinphonePublishExpiring"; } return NULL; } BELLE_SIP_DECLARE_NO_IMPLEMENTED_INTERFACES(LinphoneEventCbs); BELLE_SIP_INSTANCIATE_VPTR(LinphoneEventCbs, belle_sip_object_t, NULL, // destroy NULL, // clone NULL, // marshal FALSE ); LinphoneEventCbs *linphone_event_cbs_new(void) { return belle_sip_object_new(LinphoneEventCbs); } LinphoneEventCbs *linphone_event_cbs_ref(LinphoneEventCbs *cbs) { belle_sip_object_ref(cbs); return cbs; } void linphone_event_cbs_unref(LinphoneEventCbs *cbs) { belle_sip_object_unref(cbs); } void *linphone_event_cbs_get_user_data(const LinphoneEventCbs *cbs) { return cbs->user_data; } void linphone_event_cbs_set_user_data(LinphoneEventCbs *cbs, void *ud) { cbs->user_data = ud; } LinphoneEventCbsNotifyResponseCb linphone_event_cbs_get_notify_response(const LinphoneEventCbs *cbs) { return cbs->notify_response_cb; } void linphone_event_cbs_set_notify_response(LinphoneEventCbs *cbs, LinphoneEventCbsNotifyResponseCb cb) { cbs->notify_response_cb = cb; } static void linphone_event_release(LinphoneEvent *lev){ if (lev->op) { /*this will stop the refresher*/ lev->op->stopRefreshing(); } linphone_event_unref(lev); } static LinphoneEvent * linphone_event_new_base(LinphoneCore *lc, LinphoneSubscriptionDir dir, const char *name, LinphonePrivate::SalEventOp *op){ LinphoneEvent *lev=belle_sip_object_new(LinphoneEvent); lev->callbacks = linphone_event_cbs_new(); lev->lc=lc; lev->dir=dir; lev->op=op; lev->name=ms_strdup(name); if (strcmp(lev->name, "conference") == 0) lev->internal = TRUE; lev->op->setUserPointer(lev); return lev; } LinphoneEvent *linphone_event_new(LinphoneCore *lc, LinphoneSubscriptionDir dir, const char *name, int expires){ LinphoneEvent *lev=linphone_event_new_base(lc, dir, name, new SalSubscribeOp(lc->sal)); lev->expires=expires; return lev; } static LinphoneEvent *linphone_event_new_with_op_base(LinphoneCore *lc, SalEventOp *op, LinphoneSubscriptionDir dir, const char *name, bool_t is_out_of_dialog){ LinphoneEvent *lev=linphone_event_new_base(lc, dir, name, op); lev->is_out_of_dialog_op=is_out_of_dialog; return lev; } LinphoneEvent *linphone_event_new_with_op(LinphoneCore *lc, SalEventOp *op, LinphoneSubscriptionDir dir, const char *name) { return linphone_event_new_with_op_base(lc,op,dir,name,FALSE); } LinphoneEvent *linphone_event_new_with_out_of_dialog_op(LinphoneCore *lc, SalEventOp *op, LinphoneSubscriptionDir dir, const char *name) { return linphone_event_new_with_op_base(lc,op,dir,name,TRUE); } void linphone_event_set_internal(LinphoneEvent *lev, bool_t internal) { lev->internal = internal; } bool_t linphone_event_is_internal(LinphoneEvent *lev) { return lev->internal; } void linphone_event_set_state(LinphoneEvent *lev, LinphoneSubscriptionState state){ if (lev->subscription_state!=state){ ms_message("LinphoneEvent [%p] moving to subscription state %s",lev,linphone_subscription_state_to_string(state)); lev->subscription_state=state; linphone_core_notify_subscription_state_changed(lev->lc,lev,state); if (state==LinphoneSubscriptionTerminated || state == LinphoneSubscriptionError){ linphone_event_release(lev); } } } void linphone_event_set_publish_state(LinphoneEvent *lev, LinphonePublishState state){ if (lev->publish_state!=state){ ms_message( "LinphoneEvent [%p] moving from [%s] to publish state %s", lev, linphone_publish_state_to_string(lev->publish_state), linphone_publish_state_to_string(state) ); lev->publish_state=state; linphone_core_notify_publish_state_changed(lev->lc,lev,state); switch(state){ case LinphonePublishNone: /*this state is probably trigered by a network state change to DOWN, we should release the op*/ case LinphonePublishCleared: linphone_event_release(lev); break; case LinphonePublishOk: if (lev->oneshot) linphone_event_release(lev); break; case LinphonePublishError: linphone_event_release(lev); break; case LinphonePublishProgress: case LinphonePublishExpiring: /*nothing special to do*/ break; } } } LinphonePublishState linphone_event_get_publish_state(const LinphoneEvent *lev){ return lev->publish_state; } const LinphoneErrorInfo *linphone_event_get_error_info(const LinphoneEvent *lev){ if (!lev->ei) ((LinphoneEvent*)lev)->ei = linphone_error_info_new(); linphone_error_info_from_sal_op(lev->ei, lev->op); return lev->ei; } LinphoneReason linphone_event_get_reason(const LinphoneEvent *lev){ return linphone_error_info_get_reason(linphone_event_get_error_info(lev)); } LinphoneEvent *linphone_core_create_subscribe(LinphoneCore *lc, const LinphoneAddress *resource, const char *event, int expires){ LinphoneEvent *lev=linphone_event_new(lc, LinphoneSubscriptionOutgoing, event, expires); linphone_configure_op(lc,lev->op,resource,NULL,TRUE); lev->op->setManualRefresherMode(!lp_config_get_int(lc->config,"sip","refresh_generic_subscribe",1)); return lev; } LinphoneEvent *linphone_core_create_subscribe_2(LinphoneCore *lc, const LinphoneAddress *resource, LinphoneProxyConfig *cfg, const char *event, int expires){ LinphoneEvent *lev=linphone_event_new(lc, LinphoneSubscriptionOutgoing, event, expires); linphone_configure_op_with_proxy(lc,lev->op,resource,NULL,TRUE,cfg); lev->op->setManualRefresherMode(!lp_config_get_int(lc->config,"sip","refresh_generic_subscribe",1)); return lev; } LinphoneEvent *linphone_core_create_notify(LinphoneCore *lc, const LinphoneAddress *resource, const char *event){ LinphoneEvent *lev=linphone_event_new(lc, LinphoneSubscriptionIncoming, event, -1); linphone_configure_op(lc,lev->op,resource,NULL,TRUE); lev->subscription_state = LinphoneSubscriptionIncomingReceived; lev->op->setEvent(event); lev->is_out_of_dialog_op = TRUE; return lev; } LinphoneEvent *linphone_core_subscribe(LinphoneCore *lc, const LinphoneAddress *resource, const char *event, int expires, const LinphoneContent *body){ LinphoneEvent *lev=linphone_core_create_subscribe(lc,resource,event,expires); linphone_event_send_subscribe(lev,body); return lev; } LinphoneStatus linphone_event_send_subscribe(LinphoneEvent *lev, const LinphoneContent *body){ SalBodyHandler *body_handler; int err; if (lev->dir!=LinphoneSubscriptionOutgoing){ ms_error("linphone_event_send_subscribe(): cannot send or update something that is not an outgoing subscription."); return -1; } switch (lev->subscription_state){ case LinphoneSubscriptionIncomingReceived: case LinphoneSubscriptionTerminated: case LinphoneSubscriptionOutgoingProgress: ms_error("linphone_event_send_subscribe(): cannot update subscription while in state [%s]", linphone_subscription_state_to_string(lev->subscription_state)); return -1; break; case LinphoneSubscriptionNone: case LinphoneSubscriptionActive: case LinphoneSubscriptionExpiring: case LinphoneSubscriptionError: case LinphoneSubscriptionPending: /*those states are ok*/ break; } if (lev->send_custom_headers){ lev->op->setSentCustomHeaders(lev->send_custom_headers); sal_custom_header_free(lev->send_custom_headers); lev->send_custom_headers=NULL; }else lev->op->setSentCustomHeaders(NULL); body_handler = sal_body_handler_from_content(body); auto subscribeOp = dynamic_cast(lev->op); err=subscribeOp->subscribe(lev->name,lev->expires,body_handler); if (err==0){ if (lev->subscription_state==LinphoneSubscriptionNone) linphone_event_set_state(lev,LinphoneSubscriptionOutgoingProgress); } return err; } LinphoneStatus linphone_event_update_subscribe(LinphoneEvent *lev, const LinphoneContent *body){ return linphone_event_send_subscribe(lev,body); } LinphoneStatus linphone_event_refresh_subscribe(LinphoneEvent *lev) { return lev->op->refresh(); } LinphoneStatus linphone_event_accept_subscription(LinphoneEvent *lev){ int err; if (lev->subscription_state!=LinphoneSubscriptionIncomingReceived){ ms_error("linphone_event_accept_subscription(): cannot accept subscription if subscription wasn't just received."); return -1; } auto subscribeOp = dynamic_cast(lev->op); err=subscribeOp->accept(); if (err==0){ linphone_event_set_state(lev,LinphoneSubscriptionActive); } return err; } LinphoneStatus linphone_event_deny_subscription(LinphoneEvent *lev, LinphoneReason reason){ int err; if (lev->subscription_state!=LinphoneSubscriptionIncomingReceived){ ms_error("linphone_event_deny_subscription(): cannot deny subscription if subscription wasn't just received."); return -1; } auto subscribeOp = dynamic_cast(lev->op); err=subscribeOp->decline(linphone_reason_to_sal(reason)); linphone_event_set_state(lev,LinphoneSubscriptionTerminated); return err; } LinphoneStatus linphone_event_notify(LinphoneEvent *lev, const LinphoneContent *body){ SalBodyHandler *body_handler; if (lev->subscription_state!=LinphoneSubscriptionActive && lev->subscription_state!=LinphoneSubscriptionIncomingReceived){ ms_error("linphone_event_notify(): cannot notify if subscription is not active."); return -1; } if (lev->dir!=LinphoneSubscriptionIncoming){ ms_error("linphone_event_notify(): cannot notify if not an incoming subscription."); return -1; } body_handler = sal_body_handler_from_content(body, false); auto subscribeOp = dynamic_cast(lev->op); return subscribeOp->notify(body_handler); } LinphoneEvent *_linphone_core_create_publish(LinphoneCore *core, LinphoneProxyConfig *cfg, const LinphoneAddress *resource, const char *event, int expires){ LinphoneCore *lc = core; LinphoneEvent *lev; if (!resource && cfg) resource = linphone_proxy_config_get_identity_address(cfg); lev = linphone_event_new_with_op(lc, new SalPublishOp(lc->sal), LinphoneSubscriptionInvalidDir, event); lev->expires = expires; if (!cfg) cfg = linphone_core_lookup_known_proxy(lc, resource); linphone_configure_op_with_proxy(lc,lev->op,resource,NULL, !!lp_config_get_int(lc->config,"sip","publish_msg_with_contact",0),cfg); lev->op->setManualRefresherMode(!lp_config_get_int(lc->config,"sip","refresh_generic_publish",1)); return lev; } LinphoneEvent *linphone_core_create_publish(LinphoneCore *lc, const LinphoneAddress *resource, const char *event, int expires){ return _linphone_core_create_publish(lc, NULL, resource, event, expires); } LinphoneEvent *linphone_core_create_one_shot_publish(LinphoneCore *lc, const LinphoneAddress *resource, const char *event){ LinphoneEvent *lev = linphone_core_create_publish(lc, resource, event, -1); lev->oneshot = TRUE; return lev; } static int _linphone_event_send_publish(LinphoneEvent *lev, const LinphoneContent *body, bool_t notify_err){ SalBodyHandler *body_handler; int err; if (lev->dir!=LinphoneSubscriptionInvalidDir){ ms_error("linphone_event_update_publish(): this is not a PUBLISH event."); return -1; } if (lev->send_custom_headers){ lev->op->setSentCustomHeaders(lev->send_custom_headers); sal_custom_header_free(lev->send_custom_headers); lev->send_custom_headers=NULL; } else lev->op->setSentCustomHeaders(NULL); body_handler = sal_body_handler_from_content(body); auto publishOp = dynamic_cast(lev->op); err=publishOp->publish(lev->name,lev->expires,body_handler); if (err==0){ linphone_event_set_publish_state(lev,LinphonePublishProgress); }else if (notify_err){ linphone_event_set_publish_state(lev,LinphonePublishError); } return err; } LinphoneEvent *linphone_core_publish(LinphoneCore *lc, const LinphoneAddress *resource, const char *event, int expires, const LinphoneContent *body){ int err; LinphoneEvent *lev=linphone_core_create_publish(lc,resource,event,expires); err=_linphone_event_send_publish(lev,body,FALSE); if (err==-1){ linphone_event_unref(lev); lev=NULL; } return lev; } LinphoneStatus linphone_event_send_publish(LinphoneEvent *lev, const LinphoneContent *body){ return _linphone_event_send_publish(lev,body,TRUE); } LinphoneStatus linphone_event_update_publish(LinphoneEvent* lev, const LinphoneContent* body ) { return linphone_event_send_publish(lev,body); } LinphoneStatus linphone_event_refresh_publish(LinphoneEvent *lev) { return lev->op->refresh(); } void linphone_event_pause_publish(LinphoneEvent *lev) { if (lev->op) lev->op->stopRefreshing(); } void linphone_event_unpublish(LinphoneEvent *lev) { lev->terminating = TRUE; /* needed to get clear event*/ if (lev->op) { auto op = dynamic_cast(lev->op); op->unpublish(); } } void linphone_event_set_user_data(LinphoneEvent *ev, void *up){ ev->userdata=up; } void *linphone_event_get_user_data(const LinphoneEvent *ev){ return ev->userdata; } void linphone_event_add_custom_header(LinphoneEvent *ev, const char *name, const char *value){ ev->send_custom_headers=sal_custom_header_append(ev->send_custom_headers, name, value); } const char* linphone_event_get_custom_header(LinphoneEvent* ev, const char* name){ const SalCustomHeader *ch=ev->op->getRecvCustomHeaders(); return sal_custom_header_find(ch,name); } void linphone_event_terminate(LinphoneEvent *lev){ // if event was already terminated (including on error), we should not terminate it again // otherwise it will be unreffed twice. if (lev->publish_state == LinphonePublishError || lev->publish_state == LinphonePublishCleared) { return; } if (lev->subscription_state == LinphoneSubscriptionError || lev->subscription_state == LinphoneSubscriptionTerminated) { return; } lev->terminating=TRUE; if (lev->dir==LinphoneSubscriptionIncoming){ auto op = dynamic_cast(lev->op); op->closeNotify(); }else if (lev->dir==LinphoneSubscriptionOutgoing){ auto op = dynamic_cast(lev->op); op->unsubscribe(); } if (lev->publish_state!=LinphonePublishNone){ if (lev->publish_state==LinphonePublishOk && lev->expires!=-1){ auto op = dynamic_cast(lev->op); op->unpublish(); } linphone_event_set_publish_state(lev,LinphonePublishCleared); return; } if (lev->subscription_state!=LinphoneSubscriptionNone){ linphone_event_set_state(lev,LinphoneSubscriptionTerminated); return; } } LinphoneEvent *linphone_event_ref(LinphoneEvent *lev){ belle_sip_object_ref(lev); return lev; } static void linphone_event_destroy(LinphoneEvent *lev){ if (lev->ei) linphone_error_info_unref(lev->ei); if (lev->op) lev->op->release(); if (lev->send_custom_headers) sal_custom_header_free(lev->send_custom_headers); if (lev->to_address) linphone_address_unref(lev->to_address); if (lev->from_address) linphone_address_unref(lev->from_address); if (lev->remote_contact_address) linphone_address_unref(lev->remote_contact_address); linphone_event_cbs_unref(lev->callbacks); bctbx_list_free_with_data(lev->callbacks_list, (bctbx_list_free_func)linphone_event_cbs_unref); lev->callbacks_list = nullptr; ms_free(lev->name); } void linphone_event_unref(LinphoneEvent *lev){ belle_sip_object_unref(lev); } LinphoneSubscriptionDir linphone_event_get_subscription_dir(LinphoneEvent *lev){ return lev->dir; } LinphoneSubscriptionState linphone_event_get_subscription_state(const LinphoneEvent *lev){ return lev->subscription_state; } const char *linphone_event_get_name(const LinphoneEvent *lev){ return lev->name; } static const LinphoneAddress *_linphone_event_cache_to (const LinphoneEvent *lev) { if (lev->to_address) linphone_address_unref(lev->to_address); char *buf = sal_address_as_string(lev->op->getToAddress()); ((LinphoneEvent *)lev)->to_address = linphone_address_new(buf); ms_free(buf); return lev->to_address; } static const LinphoneAddress *_linphone_event_cache_from (const LinphoneEvent *lev) { if (lev->from_address) linphone_address_unref(lev->from_address); char *buf = sal_address_as_string(lev->op->getFromAddress()); ((LinphoneEvent *)lev)->from_address = linphone_address_new(buf); ms_free(buf); return lev->from_address; } static const LinphoneAddress *_linphone_event_cache_remote_contact (const LinphoneEvent *lev) { if (lev->remote_contact_address) linphone_address_unref(lev->remote_contact_address); char *buf = sal_address_as_string(lev->op->getRemoteContactAddress()); ((LinphoneEvent *)lev)->remote_contact_address = linphone_address_new(buf); ms_free(buf); return lev->remote_contact_address; } const LinphoneAddress *linphone_event_get_from (const LinphoneEvent *lev) { if (lev->is_out_of_dialog_op && lev->dir == LinphoneSubscriptionOutgoing) return _linphone_event_cache_to(lev); return _linphone_event_cache_from(lev); } const LinphoneAddress *linphone_event_get_resource(const LinphoneEvent *lev){ if (lev->is_out_of_dialog_op && lev->dir == LinphoneSubscriptionOutgoing) return _linphone_event_cache_from(lev); return _linphone_event_cache_to(lev); } const LinphoneAddress *linphone_event_get_remote_contact (const LinphoneEvent *lev) { return _linphone_event_cache_remote_contact(lev); } LinphoneCore *linphone_event_get_core(const LinphoneEvent *lev){ return lev->lc; } static belle_sip_error_code _linphone_event_marshall(belle_sip_object_t *obj, char* buff, size_t buff_size, size_t *offset) { LinphoneEvent *ev = (LinphoneEvent*)obj; belle_sip_error_code err = BELLE_SIP_OK; err = belle_sip_snprintf(buff, buff_size, offset, "%s of %s", ev->dir == LinphoneSubscriptionIncoming ? "Incoming Subscribe" : (ev->dir == LinphoneSubscriptionOutgoing ? "Outgoing subscribe" : "Publish"), ev->name); return err; } void _linphone_event_notify_notify_response(LinphoneEvent *lev) { LinphoneEventCbsNotifyResponseCb cb = linphone_event_cbs_get_notify_response(lev->callbacks); if (cb) cb(lev); bctbx_list_t *callbacksCopy = bctbx_list_copy(linphone_event_get_callbacks_list(lev)); for (bctbx_list_t *it = callbacksCopy; it; it = bctbx_list_next(it)) { linphone_event_set_current_callbacks(lev, reinterpret_cast(bctbx_list_get_data(it))); LinphoneEventCbsNotifyResponseCb callback = linphone_event_cbs_get_notify_response(linphone_event_get_current_callbacks(lev)); if (callback) { callback(lev); } } linphone_event_set_current_callbacks(lev, nullptr); bctbx_list_free(callbacksCopy); } LinphoneEventCbs *linphone_event_get_callbacks(const LinphoneEvent *ev) { return ev->callbacks; } void linphone_event_add_callbacks(LinphoneEvent *ev, LinphoneEventCbs *cbs) { ev->callbacks_list = bctbx_list_append(ev->callbacks_list, linphone_event_cbs_ref(cbs)); } void linphone_event_remove_callbacks(LinphoneEvent *ev, LinphoneEventCbs *cbs) { ev->callbacks_list = bctbx_list_remove(ev->callbacks_list, cbs); linphone_event_cbs_unref(cbs); } LinphoneEventCbs *linphone_event_get_current_callbacks(const LinphoneEvent *ev) { return ev->currentCbs; } void linphone_event_set_current_callbacks(LinphoneEvent *ev, LinphoneEventCbs *cbs) { ev->currentCbs = cbs; } const bctbx_list_t *linphone_event_get_callbacks_list(const LinphoneEvent *ev) { return ev->callbacks_list; } BELLE_SIP_DECLARE_NO_IMPLEMENTED_INTERFACES(LinphoneEvent); BELLE_SIP_INSTANCIATE_VPTR(LinphoneEvent, belle_sip_object_t, (belle_sip_object_destroy_t) linphone_event_destroy, NULL, // clone _linphone_event_marshall, FALSE ); liblinphone-4.4.21/coreapi/factory.c000066400000000000000000000476511376572216100173710ustar00rootroot00000000000000/* * Copyright (c) 2010-2019 Belledonne Communications SARL. * * This file is part of Liblinphone. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "linphone/factory.h" #include "c-wrapper/c-wrapper.h" #include "address/address-p.h" #include "core/paths/paths.h" // TODO: From coreapi. Remove me later. #include "private.h" #ifndef PACKAGE_SOUND_DIR #define PACKAGE_SOUND_DIR "." #endif #ifndef PACKAGE_RING_DIR #define PACKAGE_RING_DIR "." #endif #ifndef PACKAGE_DATA_DIR #define PACKAGE_DATA_DIR "." #endif extern LinphoneAddress *_linphone_address_new(const char *addr); typedef belle_sip_object_t_vptr_t LinphoneFactory_vptr_t; struct _LinphoneFactory { belle_sip_object_t base; bctbx_list_t *supported_video_definitions; /*these are the directories set by the application*/ char *top_resources_dir; char *data_resources_dir; char *sound_resources_dir; char *ring_resources_dir; char *image_resources_dir; char *msplugins_dir; /*these are the cached result computed from directories set by the application*/ char *cached_data_resources_dir; char *cached_sound_resources_dir; char *cached_ring_resources_dir; char *cached_image_resources_dir; char *cached_msplugins_dir; LinphoneErrorInfo* ei; void *user_data; }; static void linphone_factory_uninit(LinphoneFactory *obj){ bctbx_list_free_with_data(obj->supported_video_definitions, (bctbx_list_free_func)linphone_video_definition_unref); STRING_RESET(obj->top_resources_dir); STRING_RESET(obj->data_resources_dir); STRING_RESET(obj->sound_resources_dir); STRING_RESET(obj->ring_resources_dir); STRING_RESET(obj->image_resources_dir); STRING_RESET(obj->msplugins_dir); STRING_RESET(obj->cached_data_resources_dir); STRING_RESET(obj->cached_sound_resources_dir); STRING_RESET(obj->cached_ring_resources_dir); STRING_RESET(obj->cached_image_resources_dir); STRING_RESET(obj->cached_msplugins_dir); } BELLE_SIP_DECLARE_NO_IMPLEMENTED_INTERFACES(LinphoneFactory); BELLE_SIP_INSTANCIATE_VPTR(LinphoneFactory, belle_sip_object_t, linphone_factory_uninit, // destroy NULL, // clone NULL, // Marshall FALSE ); static LinphoneFactory *_factory = NULL; static void _linphone_factory_destroying_cb(void) { if (_factory != NULL) { belle_sip_object_unref(_factory); _factory = NULL; } } #define ADD_SUPPORTED_VIDEO_DEFINITION(factory, width, height, name) \ (factory)->supported_video_definitions = bctbx_list_append((factory)->supported_video_definitions, \ linphone_video_definition_new(width, height, name)) static void initialize_supported_video_definitions(LinphoneFactory *factory) { #if !defined(__ANDROID__) && !TARGET_OS_IPHONE ADD_SUPPORTED_VIDEO_DEFINITION(factory, MS_VIDEO_SIZE_1080P_W, MS_VIDEO_SIZE_1080P_H, "1080p"); #endif #if !defined(__ANDROID__) && !TARGET_OS_MAC /*limit to most common sizes because mac video API cannot list supported resolutions*/ ADD_SUPPORTED_VIDEO_DEFINITION(factory, MS_VIDEO_SIZE_UXGA_W, MS_VIDEO_SIZE_UXGA_H, "uxga"); ADD_SUPPORTED_VIDEO_DEFINITION(factory, MS_VIDEO_SIZE_SXGA_MINUS_W, MS_VIDEO_SIZE_SXGA_MINUS_H, "sxga-"); #endif ADD_SUPPORTED_VIDEO_DEFINITION(factory, MS_VIDEO_SIZE_720P_W, MS_VIDEO_SIZE_720P_H, "720p"); #if !defined(__ANDROID__) && !TARGET_OS_MAC ADD_SUPPORTED_VIDEO_DEFINITION(factory, MS_VIDEO_SIZE_XGA_W, MS_VIDEO_SIZE_XGA_H, "xga"); #endif #if !defined(__ANDROID__) && !TARGET_OS_IPHONE ADD_SUPPORTED_VIDEO_DEFINITION(factory, MS_VIDEO_SIZE_SVGA_W, MS_VIDEO_SIZE_SVGA_H, "svga"); ADD_SUPPORTED_VIDEO_DEFINITION(factory, MS_VIDEO_SIZE_4CIF_W, MS_VIDEO_SIZE_4CIF_H, "4cif"); #endif ADD_SUPPORTED_VIDEO_DEFINITION(factory, MS_VIDEO_SIZE_VGA_W, MS_VIDEO_SIZE_VGA_H, "vga"); #if TARGET_OS_IPHONE ADD_SUPPORTED_VIDEO_DEFINITION(factory, MS_VIDEO_SIZE_IOS_MEDIUM_H, MS_VIDEO_SIZE_IOS_MEDIUM_W, "ios-medium"); #endif ADD_SUPPORTED_VIDEO_DEFINITION(factory, MS_VIDEO_SIZE_CIF_W, MS_VIDEO_SIZE_CIF_H, "cif"); #if !TARGET_OS_MAC || TARGET_OS_IPHONE /* OS_MAC is 1 for iPhone, but we need QVGA */ ADD_SUPPORTED_VIDEO_DEFINITION(factory, MS_VIDEO_SIZE_QVGA_W, MS_VIDEO_SIZE_QVGA_H, "qvga"); #endif ADD_SUPPORTED_VIDEO_DEFINITION(factory, MS_VIDEO_SIZE_QCIF_W, MS_VIDEO_SIZE_QCIF_H, "qcif"); } static LinphoneFactory *linphone_factory_new(void){ LinphoneFactory *factory = belle_sip_object_new(LinphoneFactory); factory->top_resources_dir = bctbx_strdup(PACKAGE_DATA_DIR); initialize_supported_video_definitions(factory); return factory; } LinphoneFactory *linphone_factory_get(void) { if (_factory == NULL) { _factory = linphone_factory_new(); atexit(_linphone_factory_destroying_cb); } return _factory; } void linphone_factory_clean(void){ LinphonePrivate::AddressPrivate::clearSipAddressesCache(); if (_factory){ belle_sip_object_unref(_factory); _factory = NULL; } } static LinphoneCore *_linphone_factory_create_core ( const LinphoneFactory *factory, LinphoneCoreCbs *cbs, const char *config_path, const char *factory_config_path, void *user_data, void *system_context, bool_t automatically_start ) { bctbx_init_logger(FALSE); LpConfig *config = lp_config_new_with_factory(config_path, factory_config_path); LinphoneCore *lc = _linphone_core_new_with_config(cbs, config, user_data, system_context, automatically_start); lp_config_unref(config); bctbx_uninit_logger(); return lc; } static LinphoneCore *_linphone_factory_create_shared_core ( const LinphoneFactory *factory, LinphoneCoreCbs *cbs, const char *config_filename, const char *factory_config_path, void *user_data, void *system_context, bool_t automatically_start, const char *app_group_id, bool_t main_core ) { bctbx_init_logger(FALSE); LpConfig *config = linphone_config_new_for_shared_core(app_group_id, config_filename, factory_config_path); LinphoneCore *lc = _linphone_core_new_shared_with_config(cbs, config, user_data, system_context, automatically_start, app_group_id, main_core); lp_config_unref(config); bctbx_uninit_logger(); return lc; } LinphoneCore *linphone_factory_create_core ( const LinphoneFactory *factory, LinphoneCoreCbs *cbs, const char *config_path, const char *factory_config_path ) { return _linphone_factory_create_core(factory, cbs, config_path, factory_config_path, NULL, NULL, TRUE); } LinphoneCore *linphone_factory_create_core_2 ( const LinphoneFactory *factory, LinphoneCoreCbs *cbs, const char *config_path, const char *factory_config_path, void *user_data, void *system_context ) { return _linphone_factory_create_core(factory, cbs, config_path, factory_config_path, user_data, system_context, TRUE); } LinphoneCore *linphone_factory_create_core_3 ( const LinphoneFactory *factory, const char *config_path, const char *factory_config_path, void *system_context ) { return _linphone_factory_create_core(factory, NULL, config_path, factory_config_path, NULL, system_context, FALSE); } LinphoneCore *linphone_factory_create_shared_core ( const LinphoneFactory *factory, const char *config_filename, const char *factory_config_path, void *system_context, const char *app_group_id, bool_t main_core ) { return _linphone_factory_create_shared_core(factory, NULL, factory_config_path, factory_config_path, NULL, system_context, FALSE, app_group_id, main_core); } LinphoneCore *linphone_factory_create_core_with_config ( const LinphoneFactory *factory, LinphoneCoreCbs *cbs, LinphoneConfig *config ) { return _linphone_core_new_with_config(cbs, config, NULL, NULL, TRUE); } LinphoneCore *linphone_factory_create_core_with_config_2 ( const LinphoneFactory *factory, LinphoneCoreCbs *cbs, LinphoneConfig *config, void *user_data, void *system_context ) { return _linphone_core_new_with_config(cbs, config, user_data, system_context, TRUE); } LinphoneCore *linphone_factory_create_core_with_config_3 ( const LinphoneFactory *factory, LinphoneConfig *config, void *system_context ) { return _linphone_core_new_with_config(NULL, config, NULL, system_context, FALSE); } LinphoneCore *linphone_factory_create_shared_core_with_config ( const LinphoneFactory *factory, LinphoneConfig *config, void *system_context, const char *app_group_id, bool_t main_core ) { return _linphone_core_new_shared_with_config(NULL, config, NULL, system_context, FALSE, app_group_id, main_core); } LinphoneCoreCbs *linphone_factory_create_core_cbs(const LinphoneFactory *factory) { return _linphone_core_cbs_new(); } LinphoneAddress *linphone_factory_create_address(const LinphoneFactory *factory, const char *addr) { return linphone_address_new(addr); } LinphoneParticipantDeviceIdentity *linphone_factory_create_participant_device_identity( const LinphoneFactory *factory, const LinphoneAddress *address, const char *name ) { #ifdef HAVE_ADVANCED_IM return linphone_participant_device_identity_new(address, name); #else ms_warning("Advanced IM such as group chat is disabled"); return NULL; #endif } LinphoneAuthInfo *linphone_factory_create_auth_info(const LinphoneFactory *factory, const char *username, const char *userid, const char *passwd, const char *ha1, const char *realm, const char *domain) { return linphone_auth_info_new(username, userid, passwd, ha1, realm, domain); } LinphoneAuthInfo *linphone_factory_create_auth_info_2(const LinphoneFactory *factory, const char *username, const char *userid, const char *passwd, const char *ha1, const char *realm, const char *domain, const char *algorithm) { return linphone_auth_info_new_for_algorithm(username, userid, passwd, ha1, realm, domain, algorithm); } LinphoneCallCbs * linphone_factory_create_call_cbs(const LinphoneFactory *factory) { return _linphone_call_cbs_new(); } LinphoneChatRoomCbs * linphone_factory_create_chat_room_cbs(const LinphoneFactory *factory) { return _linphone_chat_room_cbs_new(); } LinphoneChatMessageCbs * linphone_factory_create_chat_message_cbs(const LinphoneFactory *factory) { return linphone_chat_message_cbs_new(); } LinphoneVcard *linphone_factory_create_vcard(LinphoneFactory *factory) { return _linphone_vcard_new(); } LinphoneVideoDefinition * linphone_factory_create_video_definition(const LinphoneFactory *factory, unsigned int width, unsigned int height) { LinphoneVideoDefinition *supported = linphone_factory_find_supported_video_definition(factory, width, height); return supported ? linphone_video_definition_clone(supported) : linphone_video_definition_new(width, height, NULL); } LinphoneVideoDefinition * linphone_factory_create_video_definition_from_name(const LinphoneFactory *factory, const char *name) { unsigned int width = 0; unsigned int height = 0; LinphoneVideoDefinition *vdef = linphone_factory_find_supported_video_definition_by_name(factory, name); if (vdef != NULL) return linphone_video_definition_ref(vdef); if (sscanf(name, "%ux%u", &width, &height) == 2) { return linphone_video_definition_new(width, height, NULL); } return linphone_video_definition_new(0, 0, NULL); } const bctbx_list_t * linphone_factory_get_supported_video_definitions(const LinphoneFactory *factory) { return factory->supported_video_definitions; } LinphoneVideoDefinition * linphone_factory_find_supported_video_definition(const LinphoneFactory *factory, unsigned int width, unsigned int height) { const bctbx_list_t *item; const bctbx_list_t *supported = linphone_factory_get_supported_video_definitions(factory); LinphoneVideoDefinition *searched_vdef = linphone_video_definition_new(width, height, NULL); LinphoneVideoDefinition *found = NULL; for (item = supported; item != NULL; item = bctbx_list_next(item)) { LinphoneVideoDefinition *svdef = (LinphoneVideoDefinition *)bctbx_list_get_data(item); if (linphone_video_definition_equals(svdef, searched_vdef)) { found = svdef; break; } } linphone_video_definition_unref(searched_vdef); if (!found) { ms_warning("Couldn't find supported video definition for %ux%u", width, height); } return found; } LinphoneVideoDefinition * linphone_factory_find_supported_video_definition_by_name(const LinphoneFactory *factory, const char *name) { const bctbx_list_t *item; const bctbx_list_t *supported = linphone_factory_get_supported_video_definitions(factory); for (item = supported; item != NULL; item = bctbx_list_next(item)) { LinphoneVideoDefinition *svdef = (LinphoneVideoDefinition *)bctbx_list_get_data(item); if (strcmp(linphone_video_definition_get_name(svdef), name) == 0) { return svdef; } } ms_error("Couldn't find a supported video definition for name [%s]", name); return NULL; } const char * linphone_factory_get_top_resources_dir(const LinphoneFactory *factory) { return factory->top_resources_dir; } void linphone_factory_set_top_resources_dir(LinphoneFactory *factory, const char *path) { STRING_SET(factory->top_resources_dir, path); } const char * linphone_factory_get_data_resources_dir(LinphoneFactory *factory) { if (factory->data_resources_dir) return factory->data_resources_dir; if (factory->top_resources_dir){ STRING_TRANSFER(factory->cached_data_resources_dir, bctbx_strdup_printf("%s/linphone", factory->top_resources_dir)); }else{ STRING_TRANSFER(factory->cached_data_resources_dir, bctbx_strdup_printf("%s/linphone", PACKAGE_DATA_DIR)); } return factory->cached_data_resources_dir; } void linphone_factory_set_data_resources_dir(LinphoneFactory *factory, const char *path) { STRING_SET(factory->data_resources_dir, path); } const char * linphone_factory_get_sound_resources_dir(LinphoneFactory *factory) { if (factory->sound_resources_dir) return factory->sound_resources_dir; if (factory->top_resources_dir){ STRING_TRANSFER(factory->cached_sound_resources_dir, bctbx_strdup_printf("%s/sounds/linphone", factory->top_resources_dir)); return factory->cached_sound_resources_dir; } return PACKAGE_SOUND_DIR; } void linphone_factory_set_sound_resources_dir(LinphoneFactory *factory, const char *path) { STRING_SET(factory->sound_resources_dir, path); } const char * linphone_factory_get_ring_resources_dir(LinphoneFactory *factory) { if (factory->ring_resources_dir) return factory->ring_resources_dir; if (factory->sound_resources_dir){ STRING_TRANSFER(factory->cached_ring_resources_dir, bctbx_strdup_printf("%s/rings", factory->sound_resources_dir)); return factory->cached_ring_resources_dir; } if (factory->top_resources_dir) { STRING_TRANSFER(factory->cached_ring_resources_dir, bctbx_strdup_printf("%s/sounds/linphone/rings", factory->top_resources_dir)); return factory->cached_ring_resources_dir; } return PACKAGE_RING_DIR; } void linphone_factory_set_ring_resources_dir(LinphoneFactory *factory, const char *path) { STRING_SET(factory->ring_resources_dir, path); } const char * linphone_factory_get_image_resources_dir(LinphoneFactory *factory) { if (factory->image_resources_dir) return factory->image_resources_dir; if (factory->top_resources_dir) { STRING_TRANSFER(factory->cached_image_resources_dir, bctbx_strdup_printf("%s/images", factory->top_resources_dir)); }else{ STRING_TRANSFER(factory->cached_image_resources_dir, bctbx_strdup_printf("%s/images", PACKAGE_DATA_DIR)); } return factory->cached_image_resources_dir; } void linphone_factory_set_image_resources_dir(LinphoneFactory *factory, const char *path) { STRING_SET(factory->image_resources_dir, path); } const char * linphone_factory_get_msplugins_dir(LinphoneFactory *factory) { return factory->msplugins_dir; } void linphone_factory_set_msplugins_dir(LinphoneFactory *factory, const char *path) { STRING_SET(factory->msplugins_dir, path); } LinphoneErrorInfo *linphone_factory_create_error_info(LinphoneFactory *factory){ return linphone_error_info_new(); } LinphoneRange *linphone_factory_create_range(LinphoneFactory *factory) { return linphone_range_new(); } LinphoneTransports *linphone_factory_create_transports(LinphoneFactory *factory) { return linphone_transports_new(); } LinphoneVideoActivationPolicy *linphone_factory_create_video_activation_policy(LinphoneFactory *factory) { return linphone_video_activation_policy_new(); } LinphoneContent *linphone_factory_create_content(LinphoneFactory *factory) { return linphone_content_new(); } LinphoneBuffer *linphone_factory_create_buffer(LinphoneFactory *factory) { return linphone_buffer_new(); } LinphoneBuffer *linphone_factory_create_buffer_from_data(LinphoneFactory *factory, const uint8_t *data, size_t size) { return linphone_buffer_new_from_data(data, size); } LinphoneBuffer *linphone_factory_create_buffer_from_string(LinphoneFactory *factory, const char *data) { return linphone_buffer_new_from_string(data); } LinphoneConfig *linphone_factory_create_config(LinphoneFactory *factory, const char *path) { return linphone_config_new(path); } LinphoneConfig *linphone_factory_create_config_with_factory(LinphoneFactory *factory, const char *path, const char *factory_path) { return linphone_config_new_with_factory(path, factory_path); } LinphoneConfig *linphone_factory_create_config_from_string(LinphoneFactory *factory, const char *data) { return linphone_config_new_from_buffer(data); } const bctbx_list_t * linphone_factory_get_dial_plans(const LinphoneFactory *factory) { return linphone_dial_plan_get_all_list(); } void *linphone_factory_get_user_data(const LinphoneFactory *factory) { return factory->user_data; } void linphone_factory_set_user_data(LinphoneFactory *factory, void *data) { factory->user_data = data; } void linphone_factory_set_log_collection_path(LinphoneFactory *factory, const char *path) { linphone_core_set_log_collection_path(path); } void linphone_factory_enable_log_collection(LinphoneFactory *factory, LinphoneLogCollectionState state) { linphone_core_enable_log_collection(state); } LinphoneTunnelConfig *linphone_factory_create_tunnel_config(LinphoneFactory *factory) { return linphone_tunnel_config_new(); } LinphoneLoggingServiceCbs *linphone_factory_create_logging_service_cbs(LinphoneFactory *factory) { return linphone_logging_service_cbs_new(); } LinphonePlayerCbs *linphone_factory_create_player_cbs(LinphoneFactory *factory) { return linphone_player_cbs_new(); } LinphoneEventCbs *linphone_factory_create_event_cbs(LinphoneFactory *factory) { return linphone_event_cbs_new(); } LinphoneFriendListCbs *linphone_factory_create_friend_list_cbs(LinphoneFactory *factory) { return linphone_friend_list_cbs_new(); } LinphoneAccountCreatorCbs *linphone_factory_create_account_creator_cbs(LinphoneFactory *factory) { return linphone_account_creator_cbs_new(); } LinphoneXmlRpcRequestCbs *linphone_factory_create_xml_rpc_request_cbs(LinphoneFactory *factory) { return linphone_xml_rpc_request_cbs_new(); } bool_t linphone_factory_is_chatroom_backend_available(LinphoneFactory *factory, LinphoneChatRoomBackend chatroom_backend) { #ifdef HAVE_ADVANCED_IM return TRUE; #else return (chatroom_backend != LinphoneChatRoomBackendFlexisipChat); #endif } bool_t linphone_factory_is_database_storage_available(LinphoneFactory *factory) { #ifdef HAVE_DB_STORAGE return TRUE; #else return FALSE; #endif } bool_t linphone_factory_is_imdn_available(LinphoneFactory *factory) { #ifdef HAVE_ADVANCED_IM return TRUE; #else return FALSE; #endif } const char *linphone_factory_get_config_dir(LinphoneFactory *factory, void *context) { std::string path = LinphonePrivate::Paths::getPath(LinphonePrivate::Paths::Config, context); return ms_strdup(path.c_str()); } const char *linphone_factory_get_data_dir(LinphoneFactory *factory, void *context) { std::string path = LinphonePrivate::Paths::getPath(LinphonePrivate::Paths::Data, context); return ms_strdup(path.c_str()); } const char *linphone_factory_get_download_dir(LinphoneFactory *factory, void *context) { std::string path = LinphonePrivate::Paths::getPath(LinphonePrivate::Paths::Download, context); return ms_strdup(path.c_str()); } liblinphone-4.4.21/coreapi/fonis.c000066400000000000000000000055451376572216100170340ustar00rootroot00000000000000/* * Copyright (c) 2010-2019 Belledonne Communications SARL. * * This file is part of Liblinphone. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "sipsetup.h" #include "p2pproxy.h" static ms_thread_t fonis_thread; static void *fonis_thread_func(void *arg){ if (p2pproxy_application_start(0,NULL)!=0){ ms_error("Fail to start fonis thread !"); } return NULL; } static bool_t fonis_init(void){ static bool_t initd=FALSE; if (!initd){ ms_thread_create(&fonis_thread,NULL,fonis_thread_func,NULL); initd=TRUE; } return TRUE; } static int fonis_create_account(const char *uri, const char *passwd){ int err=p2pproxy_accountmgt_createAccount(uri); if (err<0) return -1; return 0; } static int fonis_login_account(SipSetupContext * ctx,const char *uri, const char *passwd){ if (p2pproxy_accountmgt_isValidAccount==P2PPROXY_ACCOUNTMGT_USER_EXIST) { return 0; } else return -1; } static int fonis_get_proxy(SipSetupContext *ctx, const char *domain, char *proxy, size_t sz){ int err=p2pproxy_resourcemgt_lookup_sip_proxy(proxy,sz,(char*)domain); if (err==0) return 0; else return -1; } static int fonis_get_stun_servers(SipSetupContext *ctx, char *stun1, char *stun2, size_t size){ FonisContext *fc=(FonisContext*)ctx->data; int ret=-1; p2pproxy_resourcemgt_resource_list_t* l=p2pproxy_resourcemgt_new_resource_list(); if (p2pproxy_resourcemgt_lookup_media_resource(l,ctx->domain)==0){ if (l->size>0) strncpy(stun1,l->resource_uri[0],size); if (l->size>1) strncpy(stun2,l->resource_uri[1],size); ret=0; } p2pproxy_resourcemgt_delete_resource_list(l); return ret; } static int fonis_get_stun_relay(SipSetupContext *ctx, char *relay, size_t size){ FonisContext *fc=(FonisContext*)ctx->data; int ret=-1; p2pproxy_resourcemgt_resource_list_t* l=p2pproxy_resourcemgt_new_resource_list(); if (p2pproxy_resourcemgt_lookup_media_resource(l,ctx->domain)==0){ if (l->size>0) strncpy(relay,l->resource_uri[0],size); ret=0; } p2pproxy_resourcemgt_delete_resource_list(l); return ret; } SipSetup fonis_sip_setup={ .name="fonis", .init=fonis_init, .create_account=fonis_create_account, .login_account=fonis_login_account, .get_proxy=fonis_get_proxy, .get_stun_servers=fonis_get_stun_servers, .get_relay=fonis_get_relay, .exit=p2pproxy_application_stop }; liblinphone-4.4.21/coreapi/friend.c000066400000000000000000001716671376572216100171760ustar00rootroot00000000000000/* * Copyright (c) 2010-2019 Belledonne Communications SARL. * * This file is part of Liblinphone. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ /* * 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 Library 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "linphone/core.h" #include "linphone/lpconfig.h" #if !defined(_WIN32) && !defined(__ANDROID__) && !defined(__QNXNTO__) #include #include #include #endif // if !defined(_WIN32) && !defined(__ANDROID__) && !defined(__QNXNTO__) #define MAX_PATH_SIZE 1024 #include "c-wrapper/c-wrapper.h" #include "core/core-p.h" #include "db/main-db.h" // TODO: From coreapi. Remove me later. #include "private.h" using namespace std; using namespace LinphonePrivate; const char *linphone_online_status_to_string(LinphoneOnlineStatus ss){ const char *str=NULL; switch(ss){ case LinphoneStatusOnline: str="Online"; break; case LinphoneStatusBusy: str="Busy"; break; case LinphoneStatusBeRightBack: str="Be right back"; break; case LinphoneStatusAway: str="Away"; break; case LinphoneStatusOnThePhone: str="On the phone"; break; case LinphoneStatusOutToLunch: str="Out to lunch"; break; case LinphoneStatusDoNotDisturb: str="Do not disturb"; break; case LinphoneStatusMoved: str="Moved"; break; case LinphoneStatusAltService: str="Using another messaging service"; break; case LinphoneStatusOffline: str="Offline"; break; case LinphoneStatusPending: str="Pending"; break; case LinphoneStatusVacation: str="Vacation"; break; default: str="Unknown status"; } return str; } static int friend_compare(const void * a, const void * b) { LinphoneFriend *lfa = (LinphoneFriend *)a; LinphoneFriend *lfb = (LinphoneFriend *)b; const bctbx_list_t *addressesa = linphone_friend_get_addresses(lfa); const bctbx_list_t *addressesb = linphone_friend_get_addresses(lfb); bctbx_list_t *iteratora = (bctbx_list_t *)addressesa; bctbx_list_t *iteratorb = (bctbx_list_t *)addressesb; int ret = 1; while (iteratora && (ret == 1)) { LinphoneAddress *fa = (LinphoneAddress *)bctbx_list_get_data(iteratora); while (iteratorb && (ret == 1)) { LinphoneAddress *fb = (LinphoneAddress *)bctbx_list_get_data(iteratorb); if (linphone_address_weak_equal(fa, fb)) ret = 0; iteratorb = bctbx_list_next(iteratorb); } iteratora = bctbx_list_next(iteratora); } return ret; } static LinphoneFriendPresence * find_presence_model_for_uri_or_tel(const LinphoneFriend *lf, const char *uri_or_tel) { bctbx_list_t *iterator = NULL; LinphoneAddress *uri_or_tel_addr = NULL; LinphoneFriendPresence *result=NULL; if (!lf->lc) { ms_warning("Cannot find uri of tel [%s] from friend [%p] because not associated to any Linphone core object",uri_or_tel,lf); return NULL; } if ((iterator = lf->presence_models) == NULL) { /*no need to move forward, just reutn to avoid useless uri parsing*/ return NULL; }; uri_or_tel_addr = linphone_core_interpret_url(lf->lc, uri_or_tel); while (uri_or_tel_addr && iterator) { LinphoneFriendPresence *lfp = (LinphoneFriendPresence *)bctbx_list_get_data(iterator); LinphoneAddress *lfp_addr = linphone_core_interpret_url(lf->lc, lfp->uri_or_tel); if (lfp_addr && linphone_address_weak_equal(uri_or_tel_addr, lfp_addr)) { result = lfp; } if (lfp_addr) linphone_address_unref(lfp_addr); if (result == NULL) iterator = bctbx_list_next(iterator); else break; } if (uri_or_tel_addr) linphone_address_unref(uri_or_tel_addr); return result; } static void add_presence_model_for_uri_or_tel(LinphoneFriend *lf, const char *uri_or_tel, LinphonePresenceModel *presence) { LinphoneFriendPresence *lfp = ms_new0(LinphoneFriendPresence, 1); lfp->uri_or_tel = ms_strdup(uri_or_tel); lfp->presence = linphone_presence_model_ref(presence); lf->presence_models = bctbx_list_append(lf->presence_models, lfp); } static void free_friend_presence(LinphoneFriendPresence *lfp) { ms_free(lfp->uri_or_tel); if (lfp->presence) linphone_presence_model_unref(lfp->presence); ms_free(lfp); } static void free_phone_number_sip_uri(LinphoneFriendPhoneNumberSipUri *lfpnsu) { ms_free(lfpnsu->number); ms_free(lfpnsu->uri); ms_free(lfpnsu); } bctbx_list_t *linphone_find_friend_by_address(bctbx_list_t *fl, const LinphoneAddress *addr, LinphoneFriend **lf){ bctbx_list_t *res=NULL; LinphoneFriend dummy; if (lf!=NULL) *lf=NULL; memset(&dummy, 0, sizeof(LinphoneFriend)); dummy.uri=(LinphoneAddress*)addr; res=bctbx_list_find_custom(fl,friend_compare,&dummy); if (lf!=NULL && res!=NULL) *lf=(LinphoneFriend*)bctbx_list_get_data(res); return res; } void __linphone_friend_do_subscribe(LinphoneFriend *fr){ LinphoneCore *lc=fr->lc; const LinphoneAddress *addr = linphone_friend_get_address(fr); if (addr != NULL) { if (fr->outsub==NULL){ /* people for which we don't have yet an answer should appear as offline */ fr->presence_models = bctbx_list_free_with_data(fr->presence_models, (bctbx_list_free_func)free_friend_presence); /* if (fr->lc->vtable.notify_recv) fr->lc->vtable.notify_recv(fr->lc,(LinphoneFriend*)fr); */ }else{ fr->outsub->release(); fr->outsub=NULL; } fr->outsub=new SalPresenceOp(lc->sal); linphone_configure_op(lc,fr->outsub,addr,NULL,TRUE); fr->outsub->subscribe(lp_config_get_int(lc->config,"sip","subscribe_expires",600)); fr->subscribe_active=TRUE; } else { ms_error("Can't send a SUBSCRIBE for friend [%p] without an address!", fr); } } LinphoneFriend * linphone_friend_new(void){ LinphoneFriend *obj = belle_sip_object_new(LinphoneFriend); obj->pol = LinphoneSPAccept; obj->subscribe = TRUE; obj->vcard = NULL; obj->storage_id = 0; obj->rc_index = -1; return obj; } #if __clang__ || ((__GNUC__ == 4 && __GNUC_MINOR__ >= 6) || __GNUC__ > 4) #pragma GCC diagnostic push #endif #ifdef _MSC_VER #pragma warning(disable : 4996) #else #pragma GCC diagnostic ignored "-Wdeprecated-declarations" #endif LinphoneFriend *linphone_friend_new_with_address(const char *addr){ LinphoneAddress* linphone_address = linphone_address_new(addr); LinphoneFriend *fr; if (linphone_address == NULL) { ms_error("Cannot create friend for address [%s]",addr?addr:"null"); return NULL; } fr=linphone_friend_new(); linphone_friend_set_address(fr,linphone_address); linphone_address_unref(linphone_address); return fr; } #if __clang__ || ((__GNUC__ == 4 && __GNUC_MINOR__ >= 6) || __GNUC__ > 4) #pragma GCC diagnostic pop #endif void linphone_friend_set_user_data(LinphoneFriend *lf, void *data){ lf->user_data=data; } void* linphone_friend_get_user_data(const LinphoneFriend *lf){ return lf->user_data; } bool_t linphone_friend_in_list(const LinphoneFriend *lf) { return lf->friend_list != NULL; } void linphone_core_interpret_friend_uri(LinphoneCore *lc, const char *uri, char **result){ LinphoneAddress *fr=NULL; *result=NULL; fr=linphone_address_new(uri); if (fr==NULL){ char *tmp=NULL; if (strchr(uri,'@')!=NULL){ LinphoneAddress *u; /*try adding sip:*/ tmp=ms_strdup_printf("sip:%s",uri); u=linphone_address_new(tmp); if (u!=NULL){ *result=tmp; } }else if (lc->default_proxy!=NULL){ /*try adding domain part from default current proxy*/ LinphoneAddress * id=linphone_address_new(linphone_core_get_identity(lc)); if ((id!=NULL) && (uri[0] != '\0')){ linphone_address_set_display_name(id,NULL); linphone_address_set_username(id,uri); *result=linphone_address_as_string(id); linphone_address_unref(id); } } if (*result){ /*looks good */ ms_message("%s interpreted as %s",uri,*result); }else{ ms_warning("Fail to interpret friend uri %s",uri); } }else { *result=linphone_address_as_string(fr); linphone_address_unref(fr); } } const LinphoneAddress * linphone_friend_get_address(const LinphoneFriend *lf) { if (linphone_core_vcard_supported()) { if (lf->vcard) { const bctbx_list_t *sip_addresses = linphone_vcard_get_sip_addresses(lf->vcard); if (sip_addresses) { LinphoneAddress *addr = (LinphoneAddress *)bctbx_list_nth_data(sip_addresses, 0); return addr; } } return NULL; } if (lf->uri) return lf->uri; return NULL; } static void add_friend_to_list_map_if_not_in_it_yet(LinphoneFriend *lf, const char *uri) { if (!lf || !lf->friend_list || !uri || strlen(uri) == 0) return; bctbx_iterator_t *it = bctbx_map_cchar_find_key(lf->friend_list->friends_map_uri, uri); bctbx_iterator_t *end = bctbx_map_cchar_end(lf->friend_list->friends_map_uri); bool_t found = FALSE; // Map is sorted, check if next entry matches key otherwise stop while (!found && !bctbx_iterator_cchar_equals(it, end)) { bctbx_pair_t *pair = bctbx_iterator_cchar_get_pair(it); const char *key = bctbx_pair_cchar_get_first(reinterpret_cast(pair)); if (!key || strcmp(uri, key) != 0) break; LinphoneFriend *lf2 = (LinphoneFriend*) bctbx_pair_cchar_get_second(pair); if (lf2 == lf) { found = TRUE; } it = bctbx_iterator_cchar_get_next(it); } bctbx_iterator_cchar_delete(it); bctbx_iterator_cchar_delete(end); if (!found) { bctbx_pair_t *pair = (bctbx_pair_t*) bctbx_pair_cchar_new(uri, linphone_friend_ref(lf)); bctbx_map_cchar_insert_and_delete(lf->friend_list->friends_map_uri, pair); } } static void remove_friend_from_list_map_if_already_in_it(LinphoneFriend *lf, const char *uri) { if (!lf || !lf->friend_list || !uri || strlen(uri) == 0) return; bctbx_iterator_t *it = bctbx_map_cchar_find_key(lf->friend_list->friends_map_uri, uri); bctbx_iterator_t *end = bctbx_map_cchar_end(lf->friend_list->friends_map_uri); // Map is sorted, check if next entry matches key otherwise stop while (!bctbx_iterator_cchar_equals(it, end)) { bctbx_pair_t *pair = bctbx_iterator_cchar_get_pair(it); const char *key = bctbx_pair_cchar_get_first(reinterpret_cast(pair)); if (!key || strcmp(uri, key) != 0) break; LinphoneFriend *lf2 = (LinphoneFriend*) bctbx_pair_cchar_get_second(pair); if (lf2 == lf) { linphone_friend_unref(lf2); bctbx_map_cchar_erase(lf->friend_list->friends_map_uri, it); break; } it = bctbx_iterator_cchar_get_next(it); } bctbx_iterator_cchar_delete(it); bctbx_iterator_cchar_delete(end); } LinphoneStatus linphone_friend_set_address(LinphoneFriend *lf, const LinphoneAddress *addr) { if (!addr) return -1; LinphoneAddress *fr = linphone_address_clone(addr); char *address; const LinphoneAddress *mAddr = linphone_friend_get_address(lf); if (mAddr && lf->friend_list) { char *mainAddress = linphone_address_as_string_uri_only(mAddr); remove_friend_from_list_map_if_already_in_it(lf, mainAddress); } linphone_address_clean(fr); address = linphone_address_as_string_uri_only(fr); if (lf->friend_list) { add_friend_to_list_map_if_not_in_it_yet(lf, address); } if (linphone_core_vcard_supported()) { if (!lf->vcard) { const char *dpname = linphone_address_get_display_name(fr) ? linphone_address_get_display_name(fr) : linphone_address_get_username(fr); linphone_friend_create_vcard(lf, dpname); } linphone_vcard_edit_main_sip_address(lf->vcard, address); linphone_address_unref(fr); } else { if (lf->uri != NULL) linphone_address_unref(lf->uri); lf->uri = fr; } ms_free(address); return 0; } void linphone_friend_add_address(LinphoneFriend *lf, const LinphoneAddress *addr) { LinphoneAddress *fr; char *uri; if (!lf || !addr) return; fr = linphone_address_clone(addr); linphone_address_clean(fr); uri = linphone_address_as_string_uri_only(fr); if (lf->friend_list) { add_friend_to_list_map_if_not_in_it_yet(lf, uri); } if (linphone_core_vcard_supported()) { if (lf->vcard) { linphone_vcard_add_sip_address(lf->vcard, uri); linphone_address_unref(fr); } } else { if (lf->uri == NULL) lf->uri = fr; else linphone_address_unref(fr); } ms_free(uri); } const bctbx_list_t* linphone_friend_get_addresses(const LinphoneFriend *lf) { if (!lf) return NULL; if (linphone_core_vcard_supported()) { const bctbx_list_t * addresses = linphone_vcard_get_sip_addresses(lf->vcard); return addresses; } else { bctbx_list_t *addresses = NULL; return lf->uri ? bctbx_list_append(addresses, lf->uri) : NULL; } } void linphone_friend_remove_address(LinphoneFriend *lf, const LinphoneAddress *addr) { char *address ; if (!lf || !addr || !lf->vcard) return; address = linphone_address_as_string_uri_only(addr); if (lf->friend_list) { remove_friend_from_list_map_if_already_in_it(lf, address); } if (linphone_core_vcard_supported()) { linphone_vcard_remove_sip_address(lf->vcard, address); } ms_free(address); } void linphone_friend_add_phone_number(LinphoneFriend *lf, const char *phone) { if (!lf || !phone) return; if (lf->friend_list) { const char *uri = linphone_friend_phone_number_to_sip_uri(lf, phone); add_friend_to_list_map_if_not_in_it_yet(lf, uri); } if (linphone_core_vcard_supported()) { if (!lf->vcard) { linphone_friend_create_vcard(lf, phone); } linphone_vcard_add_phone_number(lf->vcard, phone); } } bctbx_list_t* linphone_friend_get_phone_numbers(const LinphoneFriend *lf) { if (!lf || !lf->vcard) return NULL; if (linphone_core_vcard_supported()) { return linphone_vcard_get_phone_numbers(lf->vcard); } return NULL; } void linphone_friend_remove_phone_number(LinphoneFriend *lf, const char *phone) { if (!lf || !phone || !lf->vcard) return; if (lf->friend_list) { const char *uri = linphone_friend_phone_number_to_sip_uri(lf, phone); if (uri) { remove_friend_from_list_map_if_already_in_it(lf, uri); } } if (linphone_core_vcard_supported()) { linphone_vcard_remove_phone_number(lf->vcard, phone); } } LinphoneStatus linphone_friend_set_name(LinphoneFriend *lf, const char *name) { if (linphone_core_vcard_supported()) { if (!lf->vcard) linphone_friend_create_vcard(lf, name); linphone_vcard_set_full_name(lf->vcard, name); } else { if (!lf->uri) { ms_warning("linphone_friend_set_address() must be called before linphone_friend_set_name() to be able to set display name."); return -1; } linphone_address_set_display_name(lf->uri, name); } return 0; } LinphoneStatus linphone_friend_enable_subscribes(LinphoneFriend *fr, bool_t val){ fr->subscribe=val; return 0; } LinphoneStatus linphone_friend_set_inc_subscribe_policy(LinphoneFriend *fr, LinphoneSubscribePolicy pol) { fr->pol=pol; return 0; } void linphone_friend_notify(LinphoneFriend *lf, LinphonePresenceModel *presence){ bctbx_list_t *elem; if (lf->insubs){ const LinphoneAddress *addr = linphone_friend_get_address(lf); if (addr) { char *addr_str = linphone_address_as_string(addr); ms_message("Want to notify %s", addr_str); ms_free(addr_str); } } for(elem=lf->insubs; elem!=NULL; elem=bctbx_list_next(elem)){ auto op = reinterpret_cast(bctbx_list_get_data(elem)); op->notifyPresence((SalPresenceModel *)presence); } } void linphone_friend_add_incoming_subscription(LinphoneFriend *lf, SalOp *op){ /*ownership of the op is transfered from sal to the LinphoneFriend*/ lf->insubs = bctbx_list_append(lf->insubs, op); } void linphone_friend_remove_incoming_subscription(LinphoneFriend *lf, SalOp *op){ if (bctbx_list_find(lf->insubs, op)){ op->release(); lf->insubs = bctbx_list_remove(lf->insubs, op); } } static void linphone_friend_unsubscribe(LinphoneFriend *lf){ if (lf->outsub!=NULL) { lf->outsub->unsubscribe(); } /* for friend list there is no necessary outsub*/ lf->subscribe_active=FALSE; } void linphone_friend_invalidate_subscription(LinphoneFriend *lf){ bctbx_list_t *iterator; LinphoneCore *lc = lf->lc; if (lf->outsub!=NULL) { lf->outsub->release(); lf->outsub=NULL; } // To resend a subscribe on the next network_reachable(TRUE) lf->subscribe_active=FALSE; /* Notify application that we no longer know the presence activity */ iterator = lf->presence_models; while (iterator) { LinphoneFriendPresence *lfp = (LinphoneFriendPresence *)bctbx_list_get_data(iterator); linphone_presence_model_unref(lfp->presence); lfp->presence = linphone_presence_model_new(); linphone_presence_model_set_basic_status(lfp->presence, LinphonePresenceBasicStatusClosed); linphone_core_notify_notify_presence_received_for_uri_or_tel(lc, lf, lfp->uri_or_tel, lfp->presence); iterator = bctbx_list_next(iterator); } if (bctbx_list_size(lf->presence_models) > 0) { // Deprecated linphone_core_notify_notify_presence_received(lc, lf); } lf->initial_subscribes_sent=FALSE; } static void close_presence_notification(SalPresenceOp *op) { op->notifyPresenceClose(); } static void release_sal_op(SalOp *op) { op->release(); } static void linphone_friend_close_incoming_subscriptions(LinphoneFriend *lf) { bctbx_list_for_each(lf->insubs, (MSIterateFunc) close_presence_notification); lf->insubs = bctbx_list_free_with_data(lf->insubs, (MSIterateFunc)release_sal_op); } void linphone_friend_close_subscriptions(LinphoneFriend *lf){ linphone_friend_unsubscribe(lf); linphone_friend_close_incoming_subscriptions(lf); } static void _linphone_friend_release_ops(LinphoneFriend *lf){ lf->insubs = bctbx_list_free_with_data(lf->insubs, (MSIterateFunc) release_sal_op); if (lf->outsub){ lf->outsub->release(); lf->outsub=NULL; } } static void _linphone_friend_destroy(LinphoneFriend *lf){ _linphone_friend_release_ops(lf); if (lf->presence_models) bctbx_list_free_with_data(lf->presence_models, (bctbx_list_free_func)free_friend_presence); if (lf->phone_number_sip_uri_map) bctbx_list_free_with_data(lf->phone_number_sip_uri_map, (bctbx_list_free_func)free_phone_number_sip_uri); if (lf->uri!=NULL) linphone_address_unref(lf->uri); if (lf->info!=NULL) buddy_info_free(lf->info); if (lf->vcard != NULL) linphone_vcard_unref(lf->vcard); if (lf->refkey != NULL) ms_free(lf->refkey); } static belle_sip_error_code _linphone_friend_marshall(belle_sip_object_t *obj, char* buff, size_t buff_size, size_t *offset) { LinphoneFriend *lf = (LinphoneFriend*)obj; belle_sip_error_code err = BELLE_SIP_OK; if (lf->uri){ char *tmp = linphone_address_as_string(lf->uri); err = belle_sip_snprintf(buff, buff_size, offset, "%s", tmp); ms_free(tmp); } return err; } const char * linphone_friend_get_name(const LinphoneFriend *lf) { if (!lf) return NULL; const char *fullname = NULL; if (linphone_core_vcard_supported()) { if (lf->vcard) { fullname = linphone_vcard_get_full_name(lf->vcard); } } if (!fullname && lf->uri) { fullname = linphone_address_get_display_name(lf->uri); } return fullname; } bool_t linphone_friend_get_send_subscribe(const LinphoneFriend *lf){ return lf->subscribe; } LinphoneSubscribePolicy linphone_friend_get_inc_subscribe_policy(const LinphoneFriend *lf){ return lf->pol; } LinphoneOnlineStatus linphone_friend_get_status(const LinphoneFriend *lf){ const LinphonePresenceModel *presence = linphone_friend_get_presence_model(lf); LinphoneOnlineStatus online_status = LinphoneStatusOffline; LinphonePresenceBasicStatus basic_status = LinphonePresenceBasicStatusClosed; LinphonePresenceActivity *activity = NULL; const char *description = NULL; unsigned int nb_activities = 0; if (presence != NULL) { basic_status = linphone_presence_model_get_basic_status(presence); nb_activities = linphone_presence_model_get_nb_activities(presence); online_status = (basic_status == LinphonePresenceBasicStatusOpen) ? LinphoneStatusOnline : LinphoneStatusOffline; if (nb_activities > 1) { char *tmp = NULL; const LinphoneAddress *addr = linphone_friend_get_address(lf); if (addr) tmp = linphone_address_as_string(addr); ms_warning("Friend %s has several activities, get status from the first one", tmp ? tmp : "unknown"); if (tmp) { ms_free(tmp); } nb_activities = 1; } if (nb_activities == 1) { activity = linphone_presence_model_get_activity(presence); description = linphone_presence_activity_get_description(activity); switch (linphone_presence_activity_get_type(activity)) { case LinphonePresenceActivityBreakfast: case LinphonePresenceActivityDinner: case LinphonePresenceActivityLunch: case LinphonePresenceActivityMeal: online_status = LinphoneStatusOutToLunch; break; case LinphonePresenceActivityAppointment: case LinphonePresenceActivityMeeting: case LinphonePresenceActivityPerformance: case LinphonePresenceActivityPresentation: case LinphonePresenceActivitySpectator: case LinphonePresenceActivityWorking: case LinphonePresenceActivityWorship: online_status = LinphoneStatusDoNotDisturb; break; case LinphonePresenceActivityAway: case LinphonePresenceActivitySleeping: online_status = LinphoneStatusAway; break; case LinphonePresenceActivityHoliday: case LinphonePresenceActivityTravel: case LinphonePresenceActivityVacation: online_status = LinphoneStatusVacation; break; case LinphonePresenceActivityBusy: if (description && strcmp(description, "Do not disturb") == 0) { // See linphonecore.c linphone_core_set_presence_info() method online_status = LinphoneStatusDoNotDisturb; } else { online_status = LinphoneStatusBusy; } break; case LinphonePresenceActivityLookingForWork: case LinphonePresenceActivityPlaying: case LinphonePresenceActivityShopping: case LinphonePresenceActivityTV: online_status = LinphoneStatusBusy; break; case LinphonePresenceActivityInTransit: case LinphonePresenceActivitySteering: online_status = LinphoneStatusBeRightBack; break; case LinphonePresenceActivityOnThePhone: online_status = LinphoneStatusOnThePhone; break; case LinphonePresenceActivityOther: case LinphonePresenceActivityPermanentAbsence: online_status = LinphoneStatusMoved; break; case LinphonePresenceActivityUnknown: /* Rely on the basic status information. */ break; } } } return online_status; } const LinphonePresenceModel * linphone_friend_get_presence_model(const LinphoneFriend *lf) { const LinphonePresenceModel *presence = NULL; LinphoneFriend* const_lf = (LinphoneFriend*)lf; const bctbx_list_t* addrs = linphone_friend_get_addresses(const_lf); bctbx_list_t* phones = NULL; bctbx_list_t *it; for (it = (bctbx_list_t *)addrs; it!= NULL; it = it->next) { LinphoneAddress *addr = (LinphoneAddress*)it->data; char *uri = linphone_address_as_string_uri_only(addr); presence = linphone_friend_get_presence_model_for_uri_or_tel(const_lf, uri); ms_free(uri); if (presence) break; } if (presence) return presence; phones = linphone_friend_get_phone_numbers(const_lf); for (it = phones; it!= NULL; it = it->next) { presence = linphone_friend_get_presence_model_for_uri_or_tel(const_lf, reinterpret_cast(it->data)); if (presence) break; } bctbx_list_free(phones); return presence; } LinphoneConsolidatedPresence linphone_friend_get_consolidated_presence(const LinphoneFriend *lf) { const LinphonePresenceModel *model = linphone_friend_get_presence_model(lf); if (!model) return LinphoneConsolidatedPresenceOffline; return linphone_presence_model_get_consolidated_presence(model); } const LinphonePresenceModel * linphone_friend_get_presence_model_for_uri_or_tel(const LinphoneFriend *lf, const char *uri_or_tel) { LinphoneFriendPresence *lfp = find_presence_model_for_uri_or_tel(lf, uri_or_tel); if (lfp) return lfp->presence; return NULL; } void linphone_friend_set_presence_model(LinphoneFriend *lf, LinphonePresenceModel *presence) { const LinphoneAddress *addr = linphone_friend_get_address(lf); if (addr) { char *uri = linphone_address_as_string_uri_only(addr); linphone_friend_set_presence_model_for_uri_or_tel(lf, uri, presence); ms_free(uri); } } void linphone_friend_set_presence_model_for_uri_or_tel(LinphoneFriend *lf, const char *uri_or_tel, LinphonePresenceModel *presence) { LinphoneFriendPresence *lfp = find_presence_model_for_uri_or_tel(lf, uri_or_tel); if (lfp) { if (lfp->presence) linphone_presence_model_unref(lfp->presence); lfp->presence = linphone_presence_model_ref(presence); } else { add_presence_model_for_uri_or_tel(lf, uri_or_tel, presence); } } bool_t linphone_friend_is_presence_received(const LinphoneFriend *lf) { return lf->presence_received; } BuddyInfo * linphone_friend_get_info(const LinphoneFriend *lf){ return lf->info; } /* * updates the p2p subscriptions. * If only_when_registered is TRUE, subscribe will be sent only if the friend's corresponding proxy config is in registered. * Otherwise if the proxy config goes to unregistered state, the subscription refresh will be suspended. * An optional proxy whose state has changed can be passed to optimize the processing. **/ void linphone_friend_update_subscribes(LinphoneFriend *fr, bool_t only_when_registered){ int can_subscribe=1; if (only_when_registered && (fr->subscribe || fr->subscribe_active)){ const LinphoneAddress *addr = linphone_friend_get_address(fr); if (addr != NULL) { LinphoneProxyConfig *cfg=linphone_core_lookup_known_proxy(fr->lc, addr); if (cfg && cfg->state!=LinphoneRegistrationOk){ char *tmp=linphone_address_as_string(addr); ms_message("Friend [%s] belongs to proxy config with identity [%s], but this one isn't registered. Subscription is suspended.", tmp,linphone_proxy_config_get_identity(cfg)); ms_free(tmp); can_subscribe=0; } } } if (can_subscribe && fr->subscribe && fr->subscribe_active==FALSE){ ms_message("Sending a new SUBSCRIBE for friend [%p]", fr); __linphone_friend_do_subscribe(fr); }else if (can_subscribe && fr->subscribe_active && !fr->subscribe){ linphone_friend_unsubscribe(fr); }else if (!can_subscribe && fr->outsub){ fr->subscribe_active=FALSE; fr->outsub->stopRefreshing(); } } void linphone_friend_save(LinphoneFriend *fr, LinphoneCore *lc) { if (lc && lc->friends_db_file) linphone_core_store_friend_in_db(lc, fr); } void linphone_friend_apply(LinphoneFriend *fr, LinphoneCore *lc) { LinphonePresenceModel *model; const LinphoneAddress *addr = linphone_friend_get_address(fr); if (!addr) { ms_debug("No sip url defined in friend %s", linphone_friend_get_name(fr)); return; } if (!linphone_core_ready(lc)) { /* lc not ready, deffering subscription */ fr->commit=TRUE; return; } if (fr->inc_subscribe_pending) { switch(fr->pol) { case LinphoneSPWait: model = linphone_presence_model_new_with_activity(LinphonePresenceActivityOther, "Waiting for user acceptance"); linphone_friend_notify(fr, model); linphone_presence_model_unref(model); break; case LinphoneSPAccept: if (fr->lc) linphone_friend_notify(fr, fr->lc->presence_model); break; case LinphoneSPDeny: linphone_friend_notify(fr, NULL); break; } fr->inc_subscribe_pending = FALSE; } if (fr->pol == LinphoneSPDeny && fr->insubs) { linphone_friend_close_incoming_subscriptions(fr); } linphone_friend_update_subscribes(fr, linphone_core_should_subscribe_friends_only_when_registered(lc)); ms_debug("linphone_friend_apply() done."); lc->bl_refresh=TRUE; fr->commit=FALSE; } void linphone_friend_edit(LinphoneFriend *fr) { if (fr && linphone_core_vcard_supported() && fr->vcard) { linphone_vcard_compute_md5_hash(fr->vcard); } } void linphone_friend_done(LinphoneFriend *fr) { ms_return_if_fail(fr); if (!fr->lc) return; if (fr && linphone_core_vcard_supported() && fr->vcard) { if (linphone_vcard_compare_md5_hash(fr->vcard) != 0) { ms_debug("vCard's md5 has changed, mark friend as dirty and clear sip addresses list cache"); linphone_vcard_clean_cache(fr->vcard); if (fr->friend_list) { fr->friend_list->dirty_friends_to_update = bctbx_list_append(fr->friend_list->dirty_friends_to_update, linphone_friend_ref(fr)); } } } linphone_friend_apply(fr, fr->lc); linphone_friend_save(fr, fr->lc); } #if __clang__ || ((__GNUC__ == 4 && __GNUC_MINOR__ >= 6) || __GNUC__ > 4) #pragma GCC diagnostic push #endif #ifdef _MSC_VER #pragma warning(disable : 4996) #else #pragma GCC diagnostic ignored "-Wdeprecated-declarations" #endif LinphoneFriend * linphone_core_create_friend(LinphoneCore *lc) { LinphoneFriend * lf = linphone_friend_new(); lf->lc = lc; return lf; } LinphoneFriend * linphone_core_create_friend_with_address(LinphoneCore *lc, const char *address) { LinphoneFriend * lf = linphone_friend_new_with_address(address); if (lf) lf->lc = lc; return lf; } #if __clang__ || ((__GNUC__ == 4 && __GNUC_MINOR__ >= 6) || __GNUC__ > 4) #pragma GCC diagnostic pop #endif void linphone_core_add_friend(LinphoneCore *lc, LinphoneFriend *lf) { LinphoneFriendList *friendList = linphone_core_get_default_friend_list(lc); if (!friendList) { friendList = linphone_core_create_friend_list(lc); linphone_core_add_friend_list(lc, friendList); linphone_friend_list_unref(friendList); } if (linphone_friend_list_add_friend(friendList, lf) != LinphoneFriendListOK) return; if (bctbx_list_find(lc->subscribers, lf)) { /*if this friend was in the pending subscriber list, now remove it from this list*/ lc->subscribers = bctbx_list_remove(lc->subscribers, lf); linphone_friend_unref(lf); } } void linphone_core_remove_friend(LinphoneCore *lc, LinphoneFriend *lf) { if (lf && lf->friend_list) { if (linphone_friend_list_remove_friend(lf->friend_list, lf) == LinphoneFriendListNonExistentFriend) { ms_error("linphone_core_remove_friend(): friend [%p] is not part of core's list.", lf); } } } void linphone_core_update_friends_subscriptions(LinphoneCore *lc) { bctbx_list_t *lists = lc->friends_lists; while (lists) { LinphoneFriendList *list = (LinphoneFriendList *)bctbx_list_get_data(lists); linphone_friend_list_update_subscriptions(list); lists = bctbx_list_next(lists); } //done here to avoid double initial subscribtion if triggered by proxy or from the app. lc->initial_subscribes_sent=TRUE; } bool_t linphone_core_should_subscribe_friends_only_when_registered(const LinphoneCore *lc){ return !!lp_config_get_int(lc->config,"sip","subscribe_presence_only_when_registered",1); } void linphone_core_send_initial_subscribes(LinphoneCore *lc) { if (lc->initial_subscribes_sent) return; linphone_core_update_friends_subscriptions(lc); } void linphone_core_invalidate_friend_subscriptions(LinphoneCore *lc) { bctbx_list_t *lists = lc->friends_lists; while (lists) { LinphoneFriendList *list = (LinphoneFriendList *)bctbx_list_get_data(lists); linphone_friend_list_invalidate_subscriptions(list); lists = bctbx_list_next(lists); } lc->initial_subscribes_sent=FALSE; } void linphone_friend_set_ref_key(LinphoneFriend *lf, const char *key){ if (lf->refkey != NULL) { ms_free(lf->refkey); lf->refkey = NULL; } if (key) { lf->refkey = ms_strdup(key); } if (lf->lc) { linphone_friend_save(lf, lf->lc); } } const char *linphone_friend_get_ref_key(const LinphoneFriend *lf){ return lf->refkey; } LinphoneFriend *linphone_core_find_friend(const LinphoneCore *lc, const LinphoneAddress *addr) { bctbx_list_t *lists = lc->friends_lists; LinphoneFriend *lf = NULL; while (lists && !lf) { LinphoneFriendList *list = (LinphoneFriendList *)bctbx_list_get_data(lists); lf = linphone_friend_list_find_friend_by_address(list, addr); lists = bctbx_list_next(lists); } return lf; } bctbx_list_t *linphone_core_find_friends(const LinphoneCore *lc, const LinphoneAddress *addr) { bctbx_list_t *result = NULL; bctbx_list_t *lists = lc->friends_lists; while (lists) { LinphoneFriendList *list = (LinphoneFriendList *)bctbx_list_get_data(lists); bctbx_list_t *list_result = linphone_friend_list_find_friends_by_address(list, addr); if (list_result) { bctbx_list_t *elem; for (elem = list_result; elem != NULL; elem = bctbx_list_next(elem)) { LinphoneFriend *lf = (LinphoneFriend *)bctbx_list_get_data(elem); if (lf) { result = bctbx_list_append(result, linphone_friend_ref(lf)); } } bctbx_list_free_with_data(list_result, (void(*)(void *))linphone_friend_unref); } lists = bctbx_list_next(lists); } return result; } LinphoneFriend *linphone_core_get_friend_by_address(const LinphoneCore *lc, const char *uri) { bctbx_list_t *lists = lc->friends_lists; LinphoneFriend *lf = NULL; while (lists && !lf) { LinphoneFriendList *list = (LinphoneFriendList *)bctbx_list_get_data(lists); lf = linphone_friend_list_find_friend_by_uri(list, uri); lists = bctbx_list_next(lists); } return lf; } LinphoneFriend *linphone_core_get_friend_by_ref_key(const LinphoneCore *lc, const char *key) { bctbx_list_t *lists = lc->friends_lists; LinphoneFriend *lf = NULL; while (lists && !lf) { LinphoneFriendList *list = (LinphoneFriendList *)bctbx_list_get_data(lists); lf = linphone_friend_list_find_friend_by_ref_key(list, key); lists = bctbx_list_next(lists); } return lf; } LinphoneFriend *linphone_core_find_friend_by_out_subscribe(const LinphoneCore *lc, SalOp *op) { bctbx_list_t *lists = lc->friends_lists; LinphoneFriend *lf = NULL; while (lists && !lf) { LinphoneFriendList *list = (LinphoneFriendList *)bctbx_list_get_data(lists); lf = linphone_friend_list_find_friend_by_out_subscribe(list, op); lists = bctbx_list_next(lists); } return lf; } LinphoneFriend *linphone_core_find_friend_by_inc_subscribe(const LinphoneCore *lc, SalOp *op) { bctbx_list_t *lists = lc->friends_lists; LinphoneFriend *lf = NULL; while (lists && !lf) { LinphoneFriendList *list = (LinphoneFriendList *)bctbx_list_get_data(lists); lf = linphone_friend_list_find_friend_by_inc_subscribe(list, op); lists = bctbx_list_next(lists); } return lf; } #define key_compare(s1,s2) strcmp(s1,s2) LinphoneSubscribePolicy __policy_str_to_enum(const char* pol){ if (key_compare("accept",pol)==0){ return LinphoneSPAccept; } if (key_compare("deny",pol)==0){ return LinphoneSPDeny; } if (key_compare("wait",pol)==0){ return LinphoneSPWait; } ms_warning("Unrecognized subscribe policy: %s",pol); return LinphoneSPWait; } LinphoneFriend * linphone_friend_new_from_config_file(LinphoneCore *lc, int index){ const char *tmp; char item[50]; int a; LinphoneFriend *lf; LpConfig *config=lc->config; sprintf(item,"friend_%i",index); if (!lp_config_has_section(config,item)){ return NULL; } tmp=lp_config_get_string(config,item,"url",NULL); if (tmp==NULL) { return NULL; } lf=linphone_core_create_friend_with_address(lc, tmp); if (lf==NULL) { return NULL; } tmp=lp_config_get_string(config,item,"pol",NULL); if (tmp==NULL) linphone_friend_set_inc_subscribe_policy(lf,LinphoneSPWait); else{ linphone_friend_set_inc_subscribe_policy(lf,__policy_str_to_enum(tmp)); } a=lp_config_get_int(config,item,"subscribe",0); linphone_friend_send_subscribe(lf,!!a); a = lp_config_get_int(config, item, "presence_received", 0); lf->presence_received = (bool_t)a; lf->rc_index = index; linphone_friend_set_ref_key(lf,lp_config_get_string(config,item,"refkey",NULL)); return lf; } int linphone_friend_get_rc_index(const LinphoneFriend *lf) { return lf->rc_index; } void linphone_friend_remove(LinphoneFriend *lf) { if (!lf) return; if (lf->friend_list) { linphone_friend_list_remove_friend(lf->friend_list, lf); } if (lf->rc_index >= 0) { LinphoneCore *lc = lf->lc; if (lc) { LinphoneConfig *config = linphone_core_get_config(lc); if (config) { char section[50]; sprintf(section, "friend_%i", lf->rc_index); linphone_config_clean_section(config, section); linphone_config_sync(config); lf->rc_index = -1; } } } } LinphoneCore *linphone_friend_get_core(const LinphoneFriend *fr){ return fr->lc; } LinphoneFriend *linphone_friend_ref(LinphoneFriend *lf) { belle_sip_object_ref(lf); return lf; } void linphone_friend_unref(LinphoneFriend *lf) { belle_sip_object_unref(lf); } /* DEPRECATED */ void linphone_friend_destroy(LinphoneFriend *lf) { linphone_friend_unref(lf); } LinphoneVcard* linphone_friend_get_vcard(const LinphoneFriend *fr) { if (fr && linphone_core_vcard_supported()) return fr->vcard; return NULL; } void linphone_friend_set_vcard(LinphoneFriend *fr, LinphoneVcard *vcard) { if (!fr || !linphone_core_vcard_supported()) return; const char *fullname = linphone_vcard_get_full_name(vcard); if (!fullname || strlen(fullname) == 0) { ms_warning("Trying to set an invalid vCard (no fullname) to friend, aborting"); return; } if (fr->vcard) linphone_vcard_unref(fr->vcard); if (vcard) fr->vcard = linphone_vcard_ref(vcard); linphone_friend_save(fr, fr->lc); } bool_t linphone_friend_create_vcard(LinphoneFriend *fr, const char *name) { LinphoneVcard *vcard = NULL; LinphoneCore *lc = NULL; bool_t skip = FALSE; if (!fr || !name) { ms_error("Friend or name is null"); return FALSE; } if (!linphone_core_vcard_supported()) { ms_warning("VCard support is not builtin"); return FALSE; } if (fr->vcard) { ms_error("Friend already has a VCard"); return FALSE; } vcard = linphone_factory_create_vcard(linphone_factory_get()); lc = fr->lc; if (!lc && fr->friend_list) { lc = fr->friend_list->lc; } if (lc) { skip = !lp_config_get_int(fr->lc->config, "misc", "store_friends", 1); linphone_vcard_set_skip_validation(vcard, skip); } linphone_vcard_set_full_name(vcard, name); linphone_friend_set_vcard(fr, vcard); linphone_vcard_unref(vcard); ms_debug("VCard created for friend [%p]", fr); return TRUE; } #if __clang__ || ((__GNUC__ == 4 && __GNUC_MINOR__ >= 6) || __GNUC__ > 4) #pragma GCC diagnostic push #endif #ifdef _MSC_VER #pragma warning(disable : 4996) #else #pragma GCC diagnostic ignored "-Wdeprecated-declarations" #endif LinphoneFriend *linphone_friend_new_from_vcard(LinphoneVcard *vcard) { LinphoneFriend *fr; if (!linphone_core_vcard_supported()) { ms_error("VCard support is not builtin"); return NULL; } if (vcard == NULL) { ms_error("Cannot create friend from null vcard"); return NULL; } fr = linphone_friend_new(); // Currently presence takes too much time when dealing with hundreds of friends, so I disabled it for now fr->pol = LinphoneSPDeny; fr->subscribe = FALSE; linphone_friend_set_vcard(fr, vcard); return fr; } #if __clang__ || ((__GNUC__ == 4 && __GNUC_MINOR__ >= 6) || __GNUC__ > 4) #pragma GCC diagnostic pop #endif /*drops all references to the core and unref*/ void _linphone_friend_release(LinphoneFriend *lf){ lf->lc = NULL; _linphone_friend_release_ops(lf); linphone_friend_unref(lf); } BELLE_SIP_DECLARE_NO_IMPLEMENTED_INTERFACES(LinphoneFriend); BELLE_SIP_INSTANCIATE_VPTR(LinphoneFriend, belle_sip_object_t, (belle_sip_object_destroy_t) _linphone_friend_destroy, NULL, // clone _linphone_friend_marshall, FALSE ); /******************************************************************************* * SQL storage related functions * ******************************************************************************/ static void linphone_create_friends_table(sqlite3* db) { char* errmsg = NULL; int ret; ret = sqlite3_exec(db,"CREATE TABLE IF NOT EXISTS friends (" "id INTEGER PRIMARY KEY AUTOINCREMENT," "friend_list_id INTEGER," "sip_uri TEXT," "subscribe_policy INTEGER," "send_subscribe INTEGER," "ref_key TEXT," "vCard TEXT," "vCard_etag TEXT," "vCard_url TEXT," "presence_received INTEGER" ");", 0, 0, &errmsg); if (ret != SQLITE_OK) { ms_error("Error in creation: %s.", errmsg); sqlite3_free(errmsg); } ret = sqlite3_exec(db,"CREATE TABLE IF NOT EXISTS friends_lists (" "id INTEGER PRIMARY KEY AUTOINCREMENT," "display_name TEXT," "rls_uri TEXT," "uri TEXT," "revision INTEGER" ");", 0, 0, &errmsg); if (ret != SQLITE_OK) { ms_error("Error in creation: %s.", errmsg); sqlite3_free(errmsg); } } static bool_t linphone_update_friends_table(sqlite3* db) { static sqlite3_stmt *stmt_version; int database_user_version = -1; char *errmsg = NULL; if (sqlite3_prepare_v2(db, "PRAGMA user_version;", -1, &stmt_version, NULL) == SQLITE_OK) { while(sqlite3_step(stmt_version) == SQLITE_ROW) { database_user_version = sqlite3_column_int(stmt_version, 0); ms_debug("friends database user version = %i", database_user_version); } } sqlite3_finalize(stmt_version); if (database_user_version != 3100) { // Linphone 3.10.0 int ret = sqlite3_exec(db, "BEGIN TRANSACTION;\n" "ALTER TABLE friends RENAME TO temp_friends;\n" "CREATE TABLE IF NOT EXISTS friends (" "id INTEGER PRIMARY KEY AUTOINCREMENT," "friend_list_id INTEGER," "sip_uri TEXT," "subscribe_policy INTEGER," "send_subscribe INTEGER," "ref_key TEXT," "vCard TEXT," "vCard_etag TEXT," "vCard_url TEXT," "presence_received INTEGER" ");\n" "INSERT INTO friends SELECT id, friend_list_id, sip_uri, subscribe_policy, send_subscribe, ref_key, vCard, vCard_etag, vCard_url, presence_received FROM temp_friends;\n" "DROP TABLE temp_friends;\n" "PRAGMA user_version = 3100;\n" "COMMIT;", 0, 0, &errmsg); if (ret != SQLITE_OK) { ms_error("Error altering table friends: %s.", errmsg); sqlite3_free(errmsg); return FALSE; } return TRUE; } return FALSE; } void linphone_core_friends_storage_init(LinphoneCore *lc) { int ret; const char *errmsg; sqlite3 *db; bctbx_list_t *friends_lists = NULL; linphone_core_friends_storage_close(lc); ret = _linphone_sqlite3_open(lc->friends_db_file, &db); if (ret != SQLITE_OK) { errmsg = sqlite3_errmsg(db); ms_error("Error in the opening: %s.", errmsg); sqlite3_close(db); return; } linphone_create_friends_table(db); if (linphone_update_friends_table(db)) { // After updating schema, database need to be closed/reopenned sqlite3_close(db); _linphone_sqlite3_open(lc->friends_db_file, &db); } lc->friends_db = db; friends_lists = linphone_core_fetch_friends_lists_from_db(lc); if (friends_lists) { const bctbx_list_t *it; ms_warning("Replacing current default friend list by the one(s) from the database"); lc->friends_lists = bctbx_list_free_with_data(lc->friends_lists, (bctbx_list_free_func)linphone_friend_list_unref); for (it=friends_lists;it!=NULL;it=bctbx_list_next(it)) { LinphoneFriendList *list = (LinphoneFriendList *)bctbx_list_get_data(it); linphone_core_add_friend_list(lc, list); } friends_lists = bctbx_list_free_with_data(friends_lists, (bctbx_list_free_func)linphone_friend_list_unref); } } void linphone_core_friends_storage_close(LinphoneCore *lc) { if (lc->friends_db) { sqlite3_close(lc->friends_db); lc->friends_db = NULL; } } /* DB layout: * | 0 | storage_id * | 1 | display_name * | 2 | rls_uri * | 3 | uri * | 4 | revision */ static int create_friend_list(void *data, int argc, char **argv, char **colName) { bctbx_list_t **list = (bctbx_list_t **)data; unsigned int storage_id = (unsigned int)atoi(argv[0]); LinphoneFriendList *lfl = linphone_core_create_friend_list(NULL); lfl->storage_id = storage_id; linphone_friend_list_set_display_name(lfl, argv[1]); linphone_friend_list_set_rls_uri(lfl, argv[2]); linphone_friend_list_set_uri(lfl, argv[3]); lfl->revision = atoi(argv[4]); *list = bctbx_list_append(*list, linphone_friend_list_ref(lfl)); linphone_friend_list_unref(lfl); return 0; } #if __clang__ || ((__GNUC__ == 4 && __GNUC_MINOR__ >= 6) || __GNUC__ > 4) #pragma GCC diagnostic push #endif #ifdef _MSC_VER #pragma warning(disable : 4996) #else #pragma GCC diagnostic ignored "-Wdeprecated-declarations" #endif /* DB layout: * | 0 | storage_id * | 1 | friend_list_id * | 2 | sip_uri * | 3 | subscribe_policy * | 4 | send_subscribe * | 5 | ref_key * | 6 | vCard * | 7 | vCard eTag * | 8 | vCard URL * | 9 | presence_received */ static int create_friend(void *data, int argc, char **argv, char **colName) { LinphoneVcardContext *context = (LinphoneVcardContext *)data; bctbx_list_t **list = (bctbx_list_t **)linphone_vcard_context_get_user_data(context); LinphoneFriend *lf = NULL; LinphoneVcard *vcard = NULL; unsigned int storage_id = (unsigned int)atoi(argv[0]); //sqlite3_exec return a Locale string and Belcard need to be in UTF8 for parsing if (argv[6]) { string localeBuffer(argv[6]); string utf8Buffer = LinphonePrivate::Utils::localeToUtf8(localeBuffer); vcard = linphone_vcard_context_get_vcard_from_buffer(context, utf8Buffer.c_str()); } if (vcard) { linphone_vcard_set_etag(vcard, argv[7]); linphone_vcard_set_url(vcard, argv[8]); lf = linphone_friend_new_from_vcard(vcard); linphone_vcard_unref(vcard); } if (!lf) { lf = linphone_friend_new(); if (argv[2] != NULL) { LinphoneAddress *addr = linphone_address_new(argv[2]); if (addr) { linphone_friend_set_address(lf, addr); linphone_address_unref(addr); } } } linphone_friend_set_inc_subscribe_policy(lf, static_cast(atoi(argv[3]))); linphone_friend_send_subscribe(lf, !!atoi(argv[4])); linphone_friend_set_ref_key(lf, ms_strdup(argv[5])); lf->presence_received = !!atoi(argv[9]); lf->storage_id = storage_id; *list = bctbx_list_append(*list, linphone_friend_ref(lf)); linphone_friend_unref(lf); return 0; } #if __clang__ || ((__GNUC__ == 4 && __GNUC_MINOR__ >= 6) || __GNUC__ > 4) #pragma GCC diagnostic pop #endif static int linphone_sql_request_friend(sqlite3* db, const char *stmt, LinphoneVcardContext *context) { char* errmsg = NULL; int ret; ret = sqlite3_exec(db, stmt, create_friend, context, &errmsg); if (ret != SQLITE_OK) { ms_error("linphone_sql_request: statement %s -> error sqlite3_exec(): %s.", stmt, errmsg); sqlite3_free(errmsg); } return ret; } static int linphone_sql_request_friends_list(sqlite3* db, const char *stmt, bctbx_list_t **list) { char* errmsg = NULL; int ret; ret = sqlite3_exec(db, stmt, create_friend_list, list, &errmsg); if (ret != SQLITE_OK) { ms_error("linphone_sql_request: statement %s -> error sqlite3_exec(): %s.", stmt, errmsg); sqlite3_free(errmsg); } return ret; } static int linphone_sql_request_generic(sqlite3* db, const char *stmt) { char* errmsg = NULL; int ret; ret = sqlite3_exec(db, stmt, NULL, NULL, &errmsg); if (ret != SQLITE_OK) { ms_error("linphone_sql_request: statement %s -> error sqlite3_exec(): %s.", stmt, errmsg); sqlite3_free(errmsg); } return ret; } void linphone_core_store_friend_in_db(LinphoneCore *lc, LinphoneFriend *lf) { if (lc && lc->friends_db) { char *buf; int store_friends = lp_config_get_int(lc->config, "misc", "store_friends", 1); LinphoneVcard *vcard = NULL; const LinphoneAddress *addr; char *addr_str = NULL; if (!store_friends) { return; } if (!lf || !lf->friend_list) { ms_warning("Either the friend or the friend list is null, skipping..."); return; } if (lf->friend_list->storage_id == 0) { ms_warning("Trying to add a friend in db, but friend list isn't, let's do that first"); linphone_core_store_friends_list_in_db(lc, lf->friend_list); } if (linphone_core_vcard_supported()) vcard = linphone_friend_get_vcard(lf); addr = linphone_friend_get_address(lf); if (addr != NULL) addr_str = linphone_address_as_string(addr); if (lf->storage_id > 0) { buf = sqlite3_mprintf("UPDATE friends SET friend_list_id=%u,sip_uri=%Q,subscribe_policy=%i,send_subscribe=%i,ref_key=%Q,vCard=%Q,vCard_etag=%Q,vCard_url=%Q,presence_received=%i WHERE (id = %u);", lf->friend_list->storage_id, addr_str, lf->pol, lf->subscribe, lf->refkey, vcard ? linphone_vcard_as_vcard4_string(vcard) : NULL, vcard ? linphone_vcard_get_etag(vcard) : NULL, vcard ? linphone_vcard_get_url(vcard): NULL, lf->presence_received, lf->storage_id ); } else { buf = sqlite3_mprintf("INSERT INTO friends VALUES(NULL,%u,%Q,%i,%i,%Q,%Q,%Q,%Q,%i);", lf->friend_list->storage_id, addr_str, lf->pol, lf->subscribe, lf->refkey, vcard ? linphone_vcard_as_vcard4_string(vcard) : NULL, vcard ? linphone_vcard_get_etag(vcard) : NULL, vcard ? linphone_vcard_get_url(vcard) : NULL, lf->presence_received ); } if (addr_str != NULL) ms_free(addr_str); linphone_sql_request_generic(lc->friends_db, buf); sqlite3_free(buf); if (lf->storage_id == 0) { lf->storage_id = (unsigned int)sqlite3_last_insert_rowid(lc->friends_db); } } } void linphone_core_store_friends_list_in_db(LinphoneCore *lc, LinphoneFriendList *list) { if (lc && lc->friends_db) { char *buf; int store_friends = lp_config_get_int(lc->config, "misc", "store_friends", 1); if (!store_friends) { return; } if (list->storage_id > 0) { buf = sqlite3_mprintf("UPDATE friends_lists SET display_name=%Q,rls_uri=%Q,uri=%Q,revision=%i WHERE (id = %u);", list->display_name, list->rls_uri, list->uri, list->revision, list->storage_id ); } else { buf = sqlite3_mprintf("INSERT INTO friends_lists VALUES(NULL,%Q,%Q,%Q,%i);", list->display_name, list->rls_uri, list->uri, list->revision ); } linphone_sql_request_generic(lc->friends_db, buf); sqlite3_free(buf); if (list->storage_id == 0) { list->storage_id = (unsigned int)sqlite3_last_insert_rowid(lc->friends_db); } } } void linphone_core_remove_friend_from_db(LinphoneCore *lc, LinphoneFriend *lf) { if (lc && lc->friends_db) { char *buf; if (lf->storage_id == 0) { ms_error("Friend doesn't have a storage_id !"); return; } buf = sqlite3_mprintf("DELETE FROM friends WHERE id = %u", lf->storage_id); linphone_sql_request_generic(lc->friends_db, buf); sqlite3_free(buf); lf->storage_id = 0; } } void linphone_core_remove_friends_list_from_db(LinphoneCore *lc, LinphoneFriendList *list) { if (lc && lc->friends_db) { char *buf; if (list->storage_id == 0) { ms_error("Friends list doesn't have a storage_id !"); return; } buf = sqlite3_mprintf("DELETE FROM friends_lists WHERE id = %u", list->storage_id); linphone_sql_request_generic(lc->friends_db, buf); sqlite3_free(buf); list->storage_id = 0; } } void linphone_friend_add_addresses_and_numbers_into_maps(LinphoneFriend *lf, LinphoneFriendList *list) { bctbx_list_t *iterator; bctbx_list_t *phone_numbers; const bctbx_list_t *addresses; if (lf->refkey) { bctbx_pair_t *pair = (bctbx_pair_t*) bctbx_pair_cchar_new(lf->refkey, linphone_friend_ref(lf)); bctbx_map_cchar_insert_and_delete(list->friends_map, pair); } phone_numbers = linphone_friend_get_phone_numbers(lf); iterator = phone_numbers; while (iterator) { const char *number = (const char *)bctbx_list_get_data(iterator); const char *uri = linphone_friend_phone_number_to_sip_uri(lf, number); if (uri) { add_friend_to_list_map_if_not_in_it_yet(lf, uri); } iterator = bctbx_list_next(iterator); } addresses = linphone_friend_get_addresses(lf); iterator = (bctbx_list_t *)addresses; while (iterator) { LinphoneAddress *lfaddr = (LinphoneAddress *)bctbx_list_get_data(iterator); char *uri = linphone_address_as_string_uri_only(lfaddr); if (uri) { add_friend_to_list_map_if_not_in_it_yet(lf, uri); ms_free(uri); } iterator = bctbx_list_next(iterator); } } bctbx_list_t* linphone_core_fetch_friends_from_db(LinphoneCore *lc, LinphoneFriendList *list) { char *buf; uint64_t begin,end; bctbx_list_t *result = NULL; bctbx_list_t *elem = NULL; if (!lc || lc->friends_db == NULL || list == NULL) { ms_warning("Either lc (or list) is NULL or friends database wasn't initialized with linphone_core_friends_storage_init() yet"); return NULL; } linphone_vcard_context_set_user_data(lc->vcard_context, &result); buf = sqlite3_mprintf("SELECT * FROM friends WHERE friend_list_id = %u ORDER BY id", list->storage_id); begin = ortp_get_cur_time_ms(); linphone_sql_request_friend(lc->friends_db, buf, lc->vcard_context); end = ortp_get_cur_time_ms(); ms_message("%s(): %u results fetched, completed in %i ms",__FUNCTION__, (unsigned int)bctbx_list_size(result), (int)(end-begin)); sqlite3_free(buf); for (elem = result; elem != NULL; elem = bctbx_list_next(elem)) { LinphoneFriend *lf = (LinphoneFriend *)bctbx_list_get_data(elem); lf->lc = lc; lf->friend_list = list; linphone_friend_add_addresses_and_numbers_into_maps(lf, list); } linphone_vcard_context_set_user_data(lc->vcard_context, NULL); return result; } bctbx_list_t* linphone_core_fetch_friends_lists_from_db(LinphoneCore *lc) { char *buf; uint64_t begin,end; bctbx_list_t *result = NULL; bctbx_list_t *elem = NULL; if (!lc || lc->friends_db == NULL) { ms_warning("Either lc is NULL or friends database wasn't initialized with linphone_core_friends_storage_init() yet"); return NULL; } buf = sqlite3_mprintf("SELECT * FROM friends_lists ORDER BY id"); begin = ortp_get_cur_time_ms(); linphone_sql_request_friends_list(lc->friends_db, buf, &result); end = ortp_get_cur_time_ms(); ms_message("%s(): %u results fetched, completed in %i ms",__FUNCTION__, (unsigned int)bctbx_list_size(result), (int)(end-begin)); sqlite3_free(buf); for(elem = result; elem != NULL; elem = bctbx_list_next(elem)) { LinphoneFriendList *lfl = (LinphoneFriendList *)bctbx_list_get_data(elem); lfl->lc = lc; lfl->friends = linphone_core_fetch_friends_from_db(lc, lfl); } return result; } void linphone_core_set_friends_database_path(LinphoneCore *lc, const char *path) { if (!linphone_core_conference_server_enabled(lc)) L_GET_PRIVATE(lc->cppPtr)->mainDb->import(LinphonePrivate::MainDb::Sqlite3, path); // TODO: Remove me later. if (lc->friends_db_file){ ms_free(lc->friends_db_file); lc->friends_db_file = NULL; } if (path) { lc->friends_db_file = ms_strdup(path); linphone_core_friends_storage_init(lc); } } const char* linphone_core_get_friends_database_path(LinphoneCore *lc) { return lc->friends_db_file; } LinphoneSubscriptionState linphone_friend_get_subscription_state(const LinphoneFriend *lf) { return lf->out_sub_state; } const char * linphone_friend_phone_number_to_sip_uri(LinphoneFriend *lf, const char *phone_number) { LinphoneFriendPhoneNumberSipUri * lfpnsu; char *normalized_number; char *full_uri; LinphoneProxyConfig *proxy_config; bctbx_list_t *iterator = lf->phone_number_sip_uri_map; while (iterator) { lfpnsu = (LinphoneFriendPhoneNumberSipUri *)bctbx_list_get_data(iterator); if (strcmp(lfpnsu->number, phone_number) == 0) { /*force sip uri computation because proxy config may have changed, specially, ccc could have been added since last computation*/ //free_phone_number_sip_uri(lfpnsu); if (lf->phone_number_sip_uri_map == iterator) { /*change list head if head is removed*/ iterator = lf->phone_number_sip_uri_map = bctbx_list_erase_link(lf->phone_number_sip_uri_map, iterator); } else { iterator = bctbx_list_erase_link(lf->phone_number_sip_uri_map, iterator); } } else { iterator = bctbx_list_next(iterator); } } proxy_config = linphone_core_get_default_proxy_config(linphone_friend_get_core(lf)); if (!proxy_config) return NULL; if (strstr(phone_number, "tel:") == phone_number) phone_number += 4; /* Remove the "tel:" prefix if it is present. */ normalized_number = linphone_proxy_config_normalize_phone_number(proxy_config, phone_number); if (!normalized_number) return NULL; full_uri = ms_strdup_printf("sip:%s@%s;user=phone", normalized_number, linphone_proxy_config_get_domain(proxy_config)); ms_free(normalized_number); lfpnsu = ms_new0(LinphoneFriendPhoneNumberSipUri, 1); lfpnsu->number = ms_strdup(phone_number); lfpnsu->uri = full_uri; lf->phone_number_sip_uri_map = bctbx_list_append(lf->phone_number_sip_uri_map, lfpnsu); return full_uri; } const char * linphone_friend_sip_uri_to_phone_number(LinphoneFriend *lf, const char *uri) { bctbx_list_t *iterator = lf->phone_number_sip_uri_map; while (iterator) { LinphoneFriendPhoneNumberSipUri *lfpnsu = (LinphoneFriendPhoneNumberSipUri *)bctbx_list_get_data(iterator); if (strcmp(lfpnsu->uri, uri) == 0) return lfpnsu->number; iterator = bctbx_list_next(iterator); } return NULL; } void linphone_friend_clear_presence_models(LinphoneFriend *lf) { lf->presence_models = bctbx_list_free_with_data(lf->presence_models, (bctbx_list_free_func)free_friend_presence); } int linphone_friend_get_capabilities(const LinphoneFriend *lf) { int capabilities = 0; const LinphonePresenceModel *presence = NULL; const bctbx_list_t* addrs = linphone_friend_get_addresses(lf); bctbx_list_t* phones = linphone_friend_get_phone_numbers(lf); bctbx_list_t *it; for (it = (bctbx_list_t *)addrs; it!= NULL; it = it->next) { LinphoneAddress *addr = (LinphoneAddress*)it->data; char *uri = linphone_address_as_string_uri_only(addr); presence = linphone_friend_get_presence_model_for_uri_or_tel(lf, uri); ms_free(uri); if (!presence) continue; capabilities |= linphone_presence_model_get_capabilities(presence); } for (it = phones; it!= NULL; it = it->next) { presence = linphone_friend_get_presence_model_for_uri_or_tel(lf, reinterpret_cast(it->data)); if (!presence) continue; capabilities |= linphone_presence_model_get_capabilities(presence); } bctbx_list_free(phones); return capabilities; } bool_t linphone_friend_has_capability(const LinphoneFriend *lf, const LinphoneFriendCapability capability) { return static_cast(linphone_friend_get_capabilities(lf) & capability); } bool_t linphone_friend_has_capability_with_version(const LinphoneFriend *lf, const LinphoneFriendCapability capability, float version) { const LinphonePresenceModel *presence = NULL; const bctbx_list_t* addrs = linphone_friend_get_addresses(lf); bctbx_list_t* phones = linphone_friend_get_phone_numbers(lf); bctbx_list_t *it; bool_t result = FALSE; for (it = (bctbx_list_t *)addrs; it!= NULL; it = it->next) { LinphoneAddress *addr = (LinphoneAddress*)it->data; char *uri = linphone_address_as_string_uri_only(addr); presence = linphone_friend_get_presence_model_for_uri_or_tel(lf, uri); ms_free(uri); if (!presence) continue; if(linphone_presence_model_has_capability_with_version(presence, capability, version)) result = TRUE; } for (it = phones; it!= NULL; it = it->next) { presence = linphone_friend_get_presence_model_for_uri_or_tel(lf, reinterpret_cast(it->data)); if (!presence) continue; if(linphone_presence_model_has_capability_with_version(presence, capability, version)) result = TRUE; } bctbx_list_free(phones); return result; } bool_t linphone_friend_has_capability_with_version_or_more(const LinphoneFriend *lf, const LinphoneFriendCapability capability, float version) { const LinphonePresenceModel *presence = NULL; const bctbx_list_t* addrs = linphone_friend_get_addresses(lf); bctbx_list_t* phones = linphone_friend_get_phone_numbers(lf); bctbx_list_t *it; bool_t result = FALSE; for (it = (bctbx_list_t *)addrs; it!= NULL; it = it->next) { LinphoneAddress *addr = (LinphoneAddress*)it->data; char *uri = linphone_address_as_string_uri_only(addr); presence = linphone_friend_get_presence_model_for_uri_or_tel(lf, uri); ms_free(uri); if (!presence) continue; if (linphone_presence_model_has_capability_with_version_or_more(presence, capability, version)) result = TRUE; } for (it = phones; it!= NULL; it = it->next) { presence = linphone_friend_get_presence_model_for_uri_or_tel(lf, reinterpret_cast(it->data)); if (!presence) continue; if (linphone_presence_model_has_capability_with_version_or_more(presence, capability, version)) result = TRUE; } bctbx_list_free(phones); return result; } float linphone_friend_get_capability_version(const LinphoneFriend *lf, const LinphoneFriendCapability capability) { const LinphonePresenceModel *presence = NULL; const bctbx_list_t* addrs = linphone_friend_get_addresses(lf); bctbx_list_t* phones = linphone_friend_get_phone_numbers(lf); bctbx_list_t *it; float version = -1.0; for (it = (bctbx_list_t *)addrs; it!= NULL; it = it->next) { LinphoneAddress *addr = (LinphoneAddress*)it->data; char *uri = linphone_address_as_string_uri_only(addr); presence = linphone_friend_get_presence_model_for_uri_or_tel(lf, uri); ms_free(uri); if (!presence) continue; float presence_version = linphone_presence_model_get_capability_version(presence, capability); if (presence_version > version) version = presence_version; } for (it = phones; it!= NULL; it = it->next) { presence = linphone_friend_get_presence_model_for_uri_or_tel(lf, reinterpret_cast(it->data)); if (!presence) continue; float presence_version = linphone_presence_model_get_capability_version(presence, capability); if (presence_version > version) version = presence_version; } bctbx_list_free(phones); return version; } liblinphone-4.4.21/coreapi/friendlist.c000066400000000000000000001417201376572216100200550ustar00rootroot00000000000000/* * Copyright (c) 2010-2019 Belledonne Communications SARL. * * This file is part of Liblinphone. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include "linphone/api/c-content.h" #include "linphone/core.h" #include "c-wrapper/c-wrapper.h" // TODO: From coreapi. Remove me later. #include "private.h" BELLE_SIP_DECLARE_NO_IMPLEMENTED_INTERFACES(LinphoneFriendListCbs); BELLE_SIP_INSTANCIATE_VPTR(LinphoneFriendListCbs, belle_sip_object_t, NULL, // destroy NULL, // clone NULL, // Marshall FALSE ); LinphoneFriendListCbs * linphone_friend_list_cbs_new(void) { return belle_sip_object_new(LinphoneFriendListCbs); } LinphoneFriendListCbs * linphone_friend_list_get_callbacks(const LinphoneFriendList *friend_list) { return friend_list->cbs; } void linphone_friend_list_add_callbacks(LinphoneFriendList *friend_list, LinphoneFriendListCbs *cbs) { friend_list->callbacks = bctbx_list_append(friend_list->callbacks, linphone_friend_list_cbs_ref(cbs)); } void linphone_friend_list_remove_callbacks(LinphoneFriendList *friend_list, LinphoneFriendListCbs *cbs) { friend_list->callbacks = bctbx_list_remove(friend_list->callbacks, cbs); linphone_friend_list_cbs_unref(cbs); } LinphoneFriendListCbs *linphone_friend_list_get_current_callbacks(const LinphoneFriendList *friend_list) { return friend_list->currentCbs; } void linphone_friend_list_set_current_callbacks(LinphoneFriendList *friend_list, LinphoneFriendListCbs *cbs) { friend_list->currentCbs = cbs; } const bctbx_list_t *linphone_friend_list_get_callbacks_list(const LinphoneFriendList *friend_list) { return friend_list->callbacks; } #define NOTIFY_IF_EXIST(cbName, functionName, ...) \ bctbx_list_t *callbacksCopy = bctbx_list_copy(linphone_friend_list_get_callbacks_list(list)); \ for (bctbx_list_t *it = callbacksCopy; it; it = bctbx_list_next(it)) { \ linphone_friend_list_set_current_callbacks(list, reinterpret_cast(bctbx_list_get_data(it))); \ LinphoneFriendListCbs ## cbName ## Cb cb = linphone_friend_list_cbs_get_ ## functionName (linphone_friend_list_get_current_callbacks(list)); \ if (cb) \ cb(__VA_ARGS__); \ } \ linphone_friend_list_set_current_callbacks(list, nullptr); \ bctbx_list_free(callbacksCopy); LinphoneFriendListCbs * linphone_friend_list_cbs_ref(LinphoneFriendListCbs *cbs) { belle_sip_object_ref(cbs); return cbs; } void linphone_friend_list_cbs_unref(LinphoneFriendListCbs *cbs) { belle_sip_object_unref(cbs); } void *linphone_friend_list_cbs_get_user_data(const LinphoneFriendListCbs *cbs) { return cbs->user_data; } void linphone_friend_list_cbs_set_user_data(LinphoneFriendListCbs *cbs, void *ud) { cbs->user_data = ud; } LinphoneFriendListCbsContactCreatedCb linphone_friend_list_cbs_get_contact_created(const LinphoneFriendListCbs *cbs) { return cbs->contact_created_cb; } void linphone_friend_list_cbs_set_contact_created(LinphoneFriendListCbs *cbs, LinphoneFriendListCbsContactCreatedCb cb) { cbs->contact_created_cb = cb; } LinphoneFriendListCbsContactDeletedCb linphone_friend_list_cbs_get_contact_deleted(const LinphoneFriendListCbs *cbs) { return cbs->contact_deleted_cb; } void linphone_friend_list_cbs_set_contact_deleted(LinphoneFriendListCbs *cbs, LinphoneFriendListCbsContactDeletedCb cb) { cbs->contact_deleted_cb = cb; } LinphoneFriendListCbsContactUpdatedCb linphone_friend_list_cbs_get_contact_updated(const LinphoneFriendListCbs *cbs) { return cbs->contact_updated_cb; } void linphone_friend_list_cbs_set_contact_updated(LinphoneFriendListCbs *cbs, LinphoneFriendListCbsContactUpdatedCb cb) { cbs->contact_updated_cb = cb; } LinphoneFriendListCbsSyncStateChangedCb linphone_friend_list_cbs_get_sync_status_changed(const LinphoneFriendListCbs *cbs) { return cbs->sync_state_changed_cb; } void linphone_friend_list_cbs_set_sync_status_changed(LinphoneFriendListCbs *cbs, LinphoneFriendListCbsSyncStateChangedCb cb) { cbs->sync_state_changed_cb = cb; } LinphoneFriendListCbsPresenceReceivedCb linphone_friend_list_cbs_get_presence_received(const LinphoneFriendListCbs *cbs) { return cbs->presence_received_cb; } void linphone_friend_list_cbs_set_presence_received(LinphoneFriendListCbs *cbs, LinphoneFriendListCbsPresenceReceivedCb cb) { cbs->presence_received_cb = cb; } static int add_uri_entry(xmlTextWriterPtr writer, int err, const char *uri) { if (err >= 0) { err = xmlTextWriterStartElement(writer, (const xmlChar *)"entry"); } if (err >= 0) { err = xmlTextWriterWriteAttribute(writer, (const xmlChar *)"uri", (const xmlChar *)uri); } if (err >= 0) { /* Close the "entry" element. */ err = xmlTextWriterEndElement(writer); } return err; } static char * create_resource_list_xml(const LinphoneFriendList *list) { char *xml_content = NULL; xmlBufferPtr buf; xmlTextWriterPtr writer; int err; if (list->friends_map_uri == NULL) return NULL; bctbx_iterator_t* it = bctbx_map_cchar_begin(list->friends_map_uri); bctbx_iterator_t* end = bctbx_map_cchar_end(list->friends_map_uri); if (bctbx_iterator_cchar_equals(it, end)) { ms_warning("%s: Empty list in subscription, ignored.", __FUNCTION__); return NULL; } buf = xmlBufferCreate(); if (buf == NULL) { ms_error("%s: Error creating the XML buffer", __FUNCTION__); return NULL; } writer = xmlNewTextWriterMemory(buf, 0); if (writer == NULL) { ms_error("%s: Error creating the XML writer", __FUNCTION__); return NULL; } xmlTextWriterSetIndent(writer,1); err = xmlTextWriterStartDocument(writer, "1.0", "UTF-8", NULL); if (err >= 0) { err = xmlTextWriterStartElementNS(writer, NULL, (const xmlChar *)"resource-lists", (const xmlChar *)"urn:ietf:params:xml:ns:resource-lists"); } if (err >= 0) { err = xmlTextWriterWriteAttributeNS(writer, (const xmlChar *)"xmlns", (const xmlChar *)"xsi", NULL, (const xmlChar *)"http://www.w3.org/2001/XMLSchema-instance"); } if (err>= 0) { err = xmlTextWriterStartElement(writer, (const xmlChar *)"list"); } const char *prev = NULL; while (!bctbx_iterator_cchar_equals(it, end)) { const bctbx_pair_t *pair = bctbx_iterator_cchar_get_pair(it); const char *uri = bctbx_pair_cchar_get_first(reinterpret_cast(pair)); if (prev == NULL || strcmp(prev, uri) != 0) { // Map is sorted, prevent duplicates err = add_uri_entry(writer, err, uri); } prev = uri; it = bctbx_iterator_cchar_get_next(it); } bctbx_iterator_cchar_delete(it); bctbx_iterator_cchar_delete(end); if (err >= 0) { /* Close the "list" element. */ err = xmlTextWriterEndElement(writer); } if (err >= 0) { /* Close the "resource-lists" element. */ err = xmlTextWriterEndElement(writer); } if (err >= 0) { err = xmlTextWriterEndDocument(writer); } if (err > 0) { /* xmlTextWriterEndDocument returns the size of the content. */ xml_content = ms_strdup((char *)buf->content); } xmlFreeTextWriter(writer); xmlBufferFree(buf); return xml_content; } static void linphone_friend_presence_received(LinphoneFriendList *list, LinphoneFriend *lf, const char *uri, LinphonePresenceModel *presence) { lf->presence_received = TRUE; const char *phone_number = linphone_friend_sip_uri_to_phone_number(lf, uri); if (phone_number) { char *presence_address = linphone_presence_model_get_contact(presence); bctbx_iterator_t *it = bctbx_map_cchar_find_key(list->friends_map_uri, presence_address); bctbx_iterator_t *end = bctbx_map_cchar_end(list->friends_map_uri); bool_t found_friend_with_phone = FALSE; // Map is sorted, check if next entry matches key otherwise stop while (!found_friend_with_phone && !bctbx_iterator_cchar_equals(it, end)) { bctbx_pair_t *pair = bctbx_iterator_cchar_get_pair(it); const char *key = bctbx_pair_cchar_get_first(reinterpret_cast(pair)); if (!key || strcmp(presence_address, key) != 0) break; LinphoneFriend *lf2 = (LinphoneFriend*) bctbx_pair_cchar_get_second(pair); if (lf2 == lf) { found_friend_with_phone = TRUE; } it = bctbx_iterator_cchar_get_next(it); } bctbx_iterator_cchar_delete(it); bctbx_iterator_cchar_delete(end); if (!found_friend_with_phone) { bctbx_pair_t *pair = (bctbx_pair_t*) bctbx_pair_cchar_new(presence_address, linphone_friend_ref(lf)); bctbx_map_cchar_insert_and_delete(list->friends_map_uri, pair); } linphone_friend_set_presence_model_for_uri_or_tel(lf, phone_number, presence); linphone_core_notify_notify_presence_received_for_uri_or_tel(list->lc, lf, phone_number, presence); } else { linphone_friend_set_presence_model_for_uri_or_tel(lf, uri, presence); linphone_core_notify_notify_presence_received_for_uri_or_tel(list->lc, lf, uri, presence); } // Deprecated linphone_core_notify_notify_presence_received(list->lc, lf); } static void linphone_friend_list_parse_multipart_related_body(LinphoneFriendList *list, const LinphoneContent *body, const char *first_part_body) { xmlparsing_context_t *xml_ctx = linphone_xmlparsing_context_new(); xmlSetGenericErrorFunc(xml_ctx, linphone_xmlparsing_genericxml_error); xml_ctx->doc = xmlReadDoc((const unsigned char*)first_part_body, 0, NULL, 0); if (xml_ctx->doc) { LinphoneFriend *lf; LinphoneContent *presence_part; xmlXPathObjectPtr resource_object; xmlXPathObjectPtr name_object; char *version_str = NULL; char *full_state_str = NULL; char *uri = NULL; bool_t full_state = FALSE; int version; int i; bctbx_list_t *list_friends_presence_received = NULL; LinphoneFriendListCbs *list_cbs = linphone_friend_list_get_callbacks(list); if (linphone_create_xml_xpath_context(xml_ctx) < 0) goto end; xmlXPathRegisterNs(xml_ctx->xpath_ctx, (const xmlChar *)"rlmi", (const xmlChar *)"urn:ietf:params:xml:ns:rlmi"); version_str = linphone_get_xml_attribute_text_content(xml_ctx, "/rlmi:list", "version"); if (!version_str) { ms_warning("rlmi+xml: No version attribute in list"); goto end; } version = atoi(version_str); linphone_free_xml_text_content(version_str); if (version < list->expected_notification_version) { /*no longuer an error as dialog may be silently restarting by the refresher*/ ms_warning("rlmi+xml: Received notification with version %d expected was %d, dialog may have been reseted", version, list->expected_notification_version); } full_state_str = linphone_get_xml_attribute_text_content(xml_ctx, "/rlmi:list", "fullState"); if (!full_state_str) { ms_warning("rlmi+xml: No fullState attribute in list"); goto end; } if ((strcmp(full_state_str, "true") == 0) || (strcmp(full_state_str, "1") == 0)) { bctbx_list_t *l = list->friends; for (; l != NULL; l = bctbx_list_next(l)) { lf = (LinphoneFriend *)bctbx_list_get_data(l); linphone_friend_clear_presence_models(lf); } full_state = TRUE; } linphone_free_xml_text_content(full_state_str); if ((list->expected_notification_version == 0) && !full_state) { ms_warning("rlmi+xml: Notification with version 0 is not full state, this is not valid"); goto end; } list->expected_notification_version = version + 1; name_object = linphone_get_xml_xpath_object_for_node_list(xml_ctx, "/rlmi:list/rlmi:resource/rlmi:name/.."); if (name_object && name_object->nodesetval) { for (i = 1; i <= name_object->nodesetval->nodeNr; i++) { char *name = NULL; LinphoneAddress* addr; linphone_xml_xpath_context_set_node(xml_ctx, xmlXPathNodeSetItem(name_object->nodesetval, i-1)); name = linphone_get_xml_text_content(xml_ctx, "./rlmi:name"); uri = linphone_get_xml_text_content(xml_ctx, "./@uri"); if (!uri) continue; addr = linphone_address_new(uri); if (!addr) continue; lf = linphone_friend_list_find_friend_by_address(list, addr); linphone_address_unref(addr); if (!lf && list->bodyless_subscription) { lf = linphone_core_create_friend_with_address(list->lc, uri); linphone_friend_list_add_friend(list, lf); linphone_friend_unref(lf); } if (name) { linphone_friend_set_name(lf, name); linphone_free_xml_text_content(name); } } } if (name_object) xmlXPathFreeObject(name_object); bctbx_list_t *parts = linphone_content_get_parts(body); resource_object = linphone_get_xml_xpath_object_for_node_list(xml_ctx, "/rlmi:list/rlmi:resource/rlmi:instance[@state=\"active\"]/.."); if (resource_object && resource_object->nodesetval) { for (i = 1; i <= resource_object->nodesetval->nodeNr; i++) { char *cid = NULL; linphone_xml_xpath_context_set_node(xml_ctx, xmlXPathNodeSetItem(resource_object->nodesetval, i-1)); cid = linphone_get_xml_text_content(xml_ctx, "./rlmi:instance/@cid"); if (cid) { presence_part = nullptr; bctbx_list_t *it = parts; while (it != nullptr) { LinphoneContent *content = (LinphoneContent *)it->data; const char *header = linphone_content_get_custom_header(content, "Content-Id"); if (header && strcmp(header, cid) == 0) { presence_part = linphone_content_ref(content); break; } it = bctbx_list_next(it); } if (!presence_part) { ms_warning("rlmi+xml: Cannot find part with Content-Id: %s", cid); } else { SalPresenceModel *presence = NULL; linphone_notify_parse_presence(linphone_content_get_type(presence_part), linphone_content_get_subtype(presence_part), linphone_content_get_string_buffer(presence_part), &presence); if (presence) { // Try to reduce CPU cost of linphone_address_new and find_friend_by_address by only doing it when we know for sure we have a presence to notify LinphoneAddress* addr; uri = linphone_get_xml_text_content(xml_ctx, "./@uri"); if (!uri) continue; addr = linphone_address_new(uri); if (!addr) continue; linphone_free_xml_text_content(uri); // Clean the URI if (linphone_address_has_uri_param(addr, "gr")) { linphone_address_remove_uri_param(addr, "gr"); } uri = linphone_address_as_string_uri_only(addr); linphone_address_unref(addr); bctbx_iterator_t *it = bctbx_map_cchar_find_key(list->friends_map_uri, uri); bctbx_iterator_t *end = bctbx_map_cchar_end(list->friends_map_uri); if (bctbx_iterator_cchar_equals(it, end)) { if (list->bodyless_subscription) { lf = linphone_core_create_friend_with_address(list->lc, uri); linphone_friend_list_add_friend(list, lf); linphone_friend_unref(lf); linphone_friend_presence_received(list, lf, uri, (LinphonePresenceModel *)presence); list_friends_presence_received = bctbx_list_prepend(list_friends_presence_received, lf); } } else { // Map is sorted, check if next entry matches key otherwise stop while (!bctbx_iterator_cchar_equals(it, end)) { bctbx_pair_t *pair = bctbx_iterator_cchar_get_pair(it); const char *key = bctbx_pair_cchar_get_first(reinterpret_cast(pair)); if (!key || strcmp(uri, key) != 0) break; lf = (LinphoneFriend*) bctbx_pair_cchar_get_second(pair); if (lf) { linphone_friend_presence_received(list, lf, uri, (LinphonePresenceModel *)presence); list_friends_presence_received = bctbx_list_prepend(list_friends_presence_received, lf); } it = bctbx_iterator_cchar_get_next(it); } } bctbx_iterator_cchar_delete(it); bctbx_iterator_cchar_delete(end); linphone_content_unref(presence_part); linphone_presence_model_unref((LinphonePresenceModel *)presence); } } linphone_free_xml_text_content(cid); } } // Notify list with all friends for which we received presence information if (bctbx_list_size(list_friends_presence_received) > 0) { if (list_cbs && linphone_friend_list_cbs_get_presence_received(list_cbs)) { linphone_friend_list_cbs_get_presence_received(list_cbs)(list, list_friends_presence_received); } NOTIFY_IF_EXIST(PresenceReceived, presence_received, list, list_friends_presence_received) } bctbx_list_free(list_friends_presence_received); } bctbx_list_free_with_data(parts, (void (*)(void *))linphone_content_unref); if (resource_object) xmlXPathFreeObject(resource_object); } else { ms_warning("Wrongly formatted rlmi+xml body: %s", xml_ctx->errorBuffer); } end: linphone_xmlparsing_context_destroy(xml_ctx); } static bool_t linphone_friend_list_has_subscribe_inactive(const LinphoneFriendList *list) { if (list->bodyless_subscription) return TRUE; bctbx_list_t *l = list->friends; bool_t has_subscribe_inactive = FALSE; for (; l != NULL; l = bctbx_list_next(l)) { LinphoneFriend *lf = (LinphoneFriend *)bctbx_list_get_data(l); if (lf->subscribe_active != TRUE) { has_subscribe_inactive = TRUE; break; } } return has_subscribe_inactive; } static LinphoneFriendList * linphone_friend_list_new(void) { LinphoneFriendList *list = belle_sip_object_new(LinphoneFriendList); list->cbs = linphone_friend_list_cbs_new(); list->enable_subscriptions = FALSE; list->friends_map = bctbx_mmap_cchar_new(); list->friends_map_uri = bctbx_mmap_cchar_new(); list->bodyless_subscription = FALSE; return list; } static void linphone_friend_list_destroy(LinphoneFriendList *list) { if (list->display_name != NULL) ms_free(list->display_name); if (list->rls_addr) linphone_address_unref(list->rls_addr); if (list->rls_uri != NULL) ms_free(list->rls_uri); if (list->content_digest != NULL) ms_free(list->content_digest); if (list->event != NULL) { linphone_event_terminate(list->event); linphone_event_unref(list->event); list->event = NULL; } if (list->uri != NULL) ms_free(list->uri); if (list->cbs) linphone_friend_list_cbs_unref(list->cbs); bctbx_list_free_with_data(list->callbacks, (bctbx_list_free_func)linphone_friend_list_cbs_unref); list->callbacks = nullptr; if (list->dirty_friends_to_update) list->dirty_friends_to_update = bctbx_list_free_with_data(list->dirty_friends_to_update, (void (*)(void *))linphone_friend_unref); if (list->friends) list->friends = bctbx_list_free_with_data(list->friends, (void (*)(void *))_linphone_friend_release); if (list->friends_map) bctbx_mmap_cchar_delete_with_data(list->friends_map, (void (*)(void *))linphone_friend_unref); if (list->friends_map_uri) bctbx_mmap_cchar_delete_with_data(list->friends_map_uri, (void (*)(void *))linphone_friend_unref); } BELLE_SIP_DECLARE_NO_IMPLEMENTED_INTERFACES(LinphoneFriendList); BELLE_SIP_INSTANCIATE_VPTR(LinphoneFriendList, belle_sip_object_t, (belle_sip_object_destroy_t)linphone_friend_list_destroy, NULL, // clone NULL, // marshal FALSE ); LinphoneFriendList * linphone_core_create_friend_list(LinphoneCore *lc) { LinphoneFriendList *list = linphone_friend_list_new(); list->lc = lc; if (lc) { // Will be NULL if created from database // We can't use linphone_core_is_friend_list_subscription_enabled because this will be called before the C++ core is initialized list->enable_subscriptions = !!lp_config_get_int(linphone_core_get_config(lc), "net", "friendlist_subscription_enabled", 1); } return list; } LinphoneFriendList * linphone_friend_list_ref(LinphoneFriendList *list) { belle_sip_object_ref(list); return list; } void _linphone_friend_list_release(LinphoneFriendList *list){ /*drops all references to core and unref*/ list->lc = NULL; if (list->event != NULL) { linphone_event_unref(list->event); list->event = NULL; } if (list->cbs) { linphone_friend_list_cbs_unref(list->cbs); list->cbs = NULL; } if (list->dirty_friends_to_update) { list->dirty_friends_to_update = bctbx_list_free_with_data(list->dirty_friends_to_update, (void (*)(void *))linphone_friend_unref); } if (list->friends) { list->friends = bctbx_list_free_with_data(list->friends, (void (*)(void *))_linphone_friend_release); } linphone_friend_list_unref(list); } void linphone_friend_list_unref(LinphoneFriendList *list) { belle_sip_object_unref(list); } void * linphone_friend_list_get_user_data(const LinphoneFriendList *list) { return list->user_data; } void linphone_friend_list_set_user_data(LinphoneFriendList *list, void *ud) { list->user_data = ud; } const char * linphone_friend_list_get_display_name(const LinphoneFriendList *list) { return list->display_name; } void linphone_friend_list_set_display_name(LinphoneFriendList *list, const char *display_name) { if (list->display_name != NULL) { ms_free(list->display_name); list->display_name = NULL; } if (display_name != NULL) { list->display_name = ms_strdup(display_name); linphone_core_store_friends_list_in_db(list->lc, list); } } const LinphoneAddress * linphone_friend_list_get_rls_address(const LinphoneFriendList *list){ return list->rls_addr; } const LinphoneAddress * _linphone_friend_list_get_rls_address(const LinphoneFriendList *list) { if (list->rls_addr) return list->rls_addr; else if (list->lc) { const char* rls_uri = lp_config_get_string(list->lc->config, "sip", "rls_uri", NULL); if (list->lc->default_rls_addr) linphone_address_unref(list->lc->default_rls_addr); list->lc->default_rls_addr=NULL; if (rls_uri) { /*to make sure changes in config are used if any*/ list->lc->default_rls_addr = linphone_address_new(rls_uri); } return list->lc->default_rls_addr; } else return NULL; } void linphone_friend_list_set_rls_address(LinphoneFriendList *list, const LinphoneAddress *rls_addr){ LinphoneAddress *new_rls_addr = rls_addr ? linphone_address_clone(rls_addr) : NULL; if (list->rls_addr){ linphone_address_unref(list->rls_addr); } list->rls_addr = new_rls_addr; if (list->rls_uri != NULL){ ms_free(list->rls_uri); list->rls_uri = NULL; } if (list->rls_addr){ list->rls_uri = linphone_address_as_string(list->rls_addr); linphone_core_store_friends_list_in_db(list->lc, list); } } const char * linphone_friend_list_get_rls_uri(const LinphoneFriendList *list) { return list->rls_uri; } void linphone_friend_list_set_rls_uri(LinphoneFriendList *list, const char *rls_uri) { LinphoneAddress *addr = rls_uri ? linphone_core_create_address(list->lc, rls_uri) : NULL; linphone_friend_list_set_rls_address(list, addr); if (addr) linphone_address_unref(addr); } static LinphoneFriendListStatus _linphone_friend_list_add_friend(LinphoneFriendList *list, LinphoneFriend *lf, bool_t synchronize) { LinphoneFriendListStatus status = LinphoneFriendListInvalidFriend; const LinphoneAddress *addr; if (!list || lf->friend_list) { if (!list) ms_error("linphone_friend_list_add_friend(): invalid list, null"); if (lf->friend_list) ms_error("linphone_friend_list_add_friend(): invalid friend, already in list"); return status; } addr = linphone_friend_get_address(lf); if (addr == NULL && linphone_friend_get_vcard(lf) == NULL && linphone_friend_get_phone_numbers(lf) == NULL) { ms_error("linphone_friend_list_add_friend(): invalid friend, no vCard, SIP URI or phone number"); return status; } bool_t present = FALSE; if (lf->refkey) { present = linphone_friend_list_find_friend_by_ref_key(list, lf->refkey) != NULL; } else { present = bctbx_list_find(list->friends, lf) != NULL; } if (present) { char *tmp = NULL; if (addr) tmp = linphone_address_as_string(addr); ms_warning("Friend %s already in list [%s], ignored.", tmp ? tmp : "unknown", list->display_name); if (tmp) ms_free(tmp); } else { status = linphone_friend_list_import_friend(list, lf, synchronize); linphone_friend_save(lf, lf->lc); } if (!list->rls_uri) // Mimic the behaviour of linphone_core_add_friend() when a resource list server is not in use linphone_friend_apply(lf, lf->lc); return status; } LinphoneFriendListStatus linphone_friend_list_add_friend(LinphoneFriendList *list, LinphoneFriend *lf) { return _linphone_friend_list_add_friend(list, lf, TRUE); } LinphoneFriendListStatus linphone_friend_list_add_local_friend(LinphoneFriendList *list, LinphoneFriend *lf) { return _linphone_friend_list_add_friend(list, lf, FALSE); } void linphone_friend_list_invalidate_friends_maps(LinphoneFriendList *list) { if (list->friends_map) bctbx_mmap_cchar_delete_with_data(list->friends_map, (void (*)(void *))linphone_friend_unref); list->friends_map = bctbx_mmap_cchar_new(); if (list->friends_map_uri) bctbx_mmap_cchar_delete_with_data(list->friends_map_uri, (void (*)(void *))linphone_friend_unref); list->friends_map_uri = bctbx_mmap_cchar_new(); const bctbx_list_t *elem; for (elem = list->friends; elem != NULL; elem = bctbx_list_next(elem)) { LinphoneFriend *lf = (LinphoneFriend *)bctbx_list_get_data(elem); linphone_friend_add_addresses_and_numbers_into_maps(lf, list); } } LinphoneFriendListStatus linphone_friend_list_import_friend(LinphoneFriendList *list, LinphoneFriend *lf, bool_t synchronize) { if (lf->friend_list) { ms_error("linphone_friend_list_add_friend(): invalid friend, already in list"); return LinphoneFriendListInvalidFriend; } lf->friend_list = list; lf->lc = list->lc; list->friends = bctbx_list_prepend(list->friends, linphone_friend_ref(lf)); linphone_friend_add_addresses_and_numbers_into_maps(lf, list); if (synchronize) { list->dirty_friends_to_update = bctbx_list_prepend(list->dirty_friends_to_update, linphone_friend_ref(lf)); } return LinphoneFriendListOK; } static void carddav_done(LinphoneCardDavContext *cdc, bool_t success, const char *msg) { LinphoneFriendList *list = cdc->friend_list; if (cdc && cdc->friend_list->cbs && cdc->friend_list->cbs->sync_state_changed_cb) { cdc->friend_list->cbs->sync_state_changed_cb(cdc->friend_list, success ? LinphoneFriendListSyncSuccessful : LinphoneFriendListSyncFailure, msg); } NOTIFY_IF_EXIST(SyncStateChanged, sync_status_changed, list, success ? LinphoneFriendListSyncSuccessful : LinphoneFriendListSyncFailure, msg) linphone_carddav_context_destroy(cdc); } static LinphoneFriendListStatus _linphone_friend_list_remove_friend(LinphoneFriendList *list, LinphoneFriend *lf, bool_t remove_from_server) { bctbx_list_t *iterator; bctbx_list_t *phone_numbers; const bctbx_list_t *addresses; bctbx_list_t *elem = bctbx_list_find(list->friends, lf); if (elem == NULL) return LinphoneFriendListNonExistentFriend; if (lf && lf->lc && lf->lc->friends_db) { linphone_core_remove_friend_from_db(lf->lc, lf); } if (remove_from_server) { LinphoneVcard *lvc = linphone_friend_get_vcard(lf); if (lvc && linphone_vcard_get_uid(lvc)) { LinphoneCardDavContext *cdc = linphone_carddav_context_new(list); if (cdc) { cdc->sync_done_cb = carddav_done; if (cdc->friend_list->cbs->sync_state_changed_cb) { cdc->friend_list->cbs->sync_state_changed_cb(cdc->friend_list, LinphoneFriendListSyncStarted, NULL); } NOTIFY_IF_EXIST(SyncStateChanged, sync_status_changed, list, LinphoneFriendListSyncStarted, NULL) linphone_carddav_delete_vcard(cdc, lf); } } } list->friends = bctbx_list_erase_link(list->friends, elem); if (lf->refkey) { bctbx_iterator_t * it = bctbx_map_cchar_find_key(list->friends_map, lf->refkey); bctbx_iterator_t * end = bctbx_map_cchar_end(list->friends_map); if (!bctbx_iterator_cchar_equals(it, end)){ linphone_friend_unref((LinphoneFriend*)bctbx_pair_cchar_get_second(bctbx_iterator_cchar_get_pair(it))); bctbx_map_cchar_erase(list->friends_map, it); } if (it) bctbx_iterator_cchar_delete(it); if (end) bctbx_iterator_cchar_delete(end); } phone_numbers = linphone_friend_get_phone_numbers(lf); iterator = phone_numbers; while (iterator) { const char *number = (const char *)bctbx_list_get_data(iterator); const char *uri = linphone_friend_phone_number_to_sip_uri(lf, number); if (uri) { bctbx_iterator_t * it = bctbx_map_cchar_find_key(list->friends_map_uri, uri); bctbx_iterator_t * end = bctbx_map_cchar_end(list->friends_map_uri); if (!bctbx_iterator_cchar_equals(it, end)){ linphone_friend_unref((LinphoneFriend*)bctbx_pair_cchar_get_second(bctbx_iterator_cchar_get_pair(it))); bctbx_map_cchar_erase(list->friends_map_uri, it); } if (it) bctbx_iterator_cchar_delete(it); if (end) bctbx_iterator_cchar_delete(end); } iterator = bctbx_list_next(iterator); } if (phone_numbers) bctbx_list_free(phone_numbers); addresses = linphone_friend_get_addresses(lf); iterator = (bctbx_list_t *)addresses; while (iterator) { LinphoneAddress *lfaddr = (LinphoneAddress *)bctbx_list_get_data(iterator); char *uri = linphone_address_as_string_uri_only(lfaddr); if (uri) { bctbx_iterator_t * it = bctbx_map_cchar_find_key(list->friends_map_uri, uri); bctbx_iterator_t * end = bctbx_map_cchar_end(list->friends_map_uri); if (!bctbx_iterator_cchar_equals(it, end)){ linphone_friend_unref((LinphoneFriend*)bctbx_pair_cchar_get_second(bctbx_iterator_cchar_get_pair(it))); bctbx_map_cchar_erase(list->friends_map_uri, it); } if (it) bctbx_iterator_cchar_delete(it); if (end) bctbx_iterator_cchar_delete(end); ms_free(uri); } iterator = bctbx_list_next(iterator); } lf->friend_list = NULL; linphone_friend_unref(lf); return LinphoneFriendListOK; } LinphoneFriendListStatus linphone_friend_list_remove_friend(LinphoneFriendList *list, LinphoneFriend *lf) { return _linphone_friend_list_remove_friend(list, lf, TRUE); } const bctbx_list_t * linphone_friend_list_get_friends(const LinphoneFriendList *list) { return list->friends; } void linphone_friend_list_update_dirty_friends(LinphoneFriendList *list) { bctbx_list_t *dirty_friends = list->dirty_friends_to_update; while (dirty_friends) { LinphoneCardDavContext *cdc = linphone_carddav_context_new(list); if (cdc) { LinphoneFriend *lf = (LinphoneFriend *)bctbx_list_get_data(dirty_friends); cdc->sync_done_cb = carddav_done; if (lf) { if (cdc->friend_list->cbs->sync_state_changed_cb) { cdc->friend_list->cbs->sync_state_changed_cb(cdc->friend_list, LinphoneFriendListSyncStarted, NULL); } NOTIFY_IF_EXIST(SyncStateChanged, sync_status_changed, list, LinphoneFriendListSyncStarted, NULL) linphone_carddav_put_vcard(cdc, lf); } } dirty_friends = bctbx_list_next(dirty_friends); } list->dirty_friends_to_update = bctbx_list_free_with_data(list->dirty_friends_to_update, (void (*)(void *))linphone_friend_unref); } static void carddav_created(LinphoneCardDavContext *cdc, LinphoneFriend *lf) { if (cdc) { LinphoneFriendList *list = cdc->friend_list; linphone_friend_list_import_friend(list, lf, FALSE); if (cdc->friend_list->cbs->contact_created_cb) { cdc->friend_list->cbs->contact_created_cb(list, lf); } NOTIFY_IF_EXIST(ContactCreated, contact_created, list, lf) } } static void carddav_removed(LinphoneCardDavContext *cdc, LinphoneFriend *lf) { if (cdc) { LinphoneFriendList *list = cdc->friend_list; _linphone_friend_list_remove_friend(list, lf, FALSE); if (cdc->friend_list->cbs->contact_deleted_cb) { cdc->friend_list->cbs->contact_deleted_cb(list, lf); } NOTIFY_IF_EXIST(ContactDeleted, contact_deleted, list, lf) } } static void carddav_updated(LinphoneCardDavContext *cdc, LinphoneFriend *lf_new, LinphoneFriend *lf_old) { if (cdc) { LinphoneFriendList *list = cdc->friend_list; bctbx_list_t *elem = bctbx_list_find(list->friends, lf_old); if (elem) { elem->data = linphone_friend_ref(lf_new); } linphone_core_store_friend_in_db(lf_new->lc, lf_new); if (cdc->friend_list->cbs->contact_updated_cb) { cdc->friend_list->cbs->contact_updated_cb(list, lf_new, lf_old); } NOTIFY_IF_EXIST(ContactUpdated, contact_updated, list, lf_new, lf_old) linphone_friend_unref(lf_old); } } void linphone_friend_list_synchronize_friends_from_server(LinphoneFriendList *list) { LinphoneCardDavContext *cdc = NULL; if (!list || !list->uri || !list->lc) { ms_error("FATAL"); return; } cdc = linphone_carddav_context_new(list); if (cdc) { cdc->contact_created_cb = carddav_created; cdc->contact_removed_cb = carddav_removed; cdc->contact_updated_cb = carddav_updated; cdc->sync_done_cb = carddav_done; if (cdc && cdc->friend_list->cbs->sync_state_changed_cb) { cdc->friend_list->cbs->sync_state_changed_cb(cdc->friend_list, LinphoneFriendListSyncStarted, NULL); } NOTIFY_IF_EXIST(SyncStateChanged, sync_status_changed, list, LinphoneFriendListSyncStarted, NULL) linphone_carddav_synchronize(cdc); } } LinphoneFriend * linphone_friend_list_find_friend_by_address(const LinphoneFriendList *list, const LinphoneAddress *address) { LinphoneAddress *clean_addr = linphone_address_clone(address); LinphoneFriend *lf; if (linphone_address_has_uri_param(clean_addr, "gr")) { linphone_address_remove_uri_param(clean_addr, "gr"); } char *uri = linphone_address_as_string_uri_only(clean_addr); lf = linphone_friend_list_find_friend_by_uri(list, uri); bctbx_free(uri); linphone_address_unref(clean_addr); return lf; } bctbx_list_t * linphone_friend_list_find_friends_by_address(const LinphoneFriendList *list, const LinphoneAddress *address) { LinphoneAddress *clean_addr = linphone_address_clone(address); bctbx_list_t *result = NULL; if (linphone_address_has_uri_param(clean_addr, "gr")) { linphone_address_remove_uri_param(clean_addr, "gr"); } char *uri = linphone_address_as_string_uri_only(clean_addr); result = linphone_friend_list_find_friends_by_uri(list, uri); bctbx_free(uri); linphone_address_unref(clean_addr); return result; } LinphoneFriend * linphone_friend_list_find_friend_by_uri(const LinphoneFriendList *list, const char *uri) { LinphoneFriend *result = NULL; bctbx_iterator_t *it = bctbx_map_cchar_find_key(list->friends_map_uri, uri); bctbx_iterator_t *end = bctbx_map_cchar_end(list->friends_map_uri); if (!bctbx_iterator_cchar_equals(it, end)) { bctbx_pair_t *pair = bctbx_iterator_cchar_get_pair(it); result = (LinphoneFriend *)bctbx_pair_cchar_get_second(pair); } bctbx_iterator_cchar_delete(end); bctbx_iterator_cchar_delete(it); return result; } bctbx_list_t * linphone_friend_list_find_friends_by_uri(const LinphoneFriendList *list, const char *uri) { bctbx_list_t *result = NULL; bctbx_iterator_t *it = bctbx_map_cchar_find_key(list->friends_map_uri, uri); bctbx_iterator_t *end = bctbx_map_cchar_end(list->friends_map_uri); while (!bctbx_iterator_cchar_equals(it, end)) { const bctbx_pair_t *pair = bctbx_iterator_cchar_get_pair(it); const char *friend_uri = bctbx_pair_cchar_get_first(reinterpret_cast(pair)); if (uri && friend_uri && strcmp(friend_uri, uri) == 0) { LinphoneFriend *lf = (LinphoneFriend *)bctbx_pair_cchar_get_second(pair); result = bctbx_list_prepend(result, linphone_friend_ref(lf)); } else { break; } it = bctbx_iterator_cchar_get_next(it); } bctbx_iterator_cchar_delete(end); bctbx_iterator_cchar_delete(it); return result; } LinphoneFriend *linphone_friend_list_find_friend_by_ref_key (const LinphoneFriendList *list, const char *ref_key) { LinphoneFriend *result = NULL; if (list) { bctbx_iterator_t *it = bctbx_map_cchar_find_key(list->friends_map, ref_key); bctbx_iterator_t *end = bctbx_map_cchar_end(list->friends_map); if (!bctbx_iterator_cchar_equals(it, end)) { bctbx_pair_t *pair = bctbx_iterator_cchar_get_pair(it); result = (LinphoneFriend *)bctbx_pair_cchar_get_second(pair); } bctbx_iterator_cchar_delete(end); bctbx_iterator_cchar_delete(it); } return result; } LinphoneFriend * linphone_friend_list_find_friend_by_inc_subscribe ( const LinphoneFriendList *list, LinphonePrivate::SalOp *op ) { const bctbx_list_t *elem; for (elem = list->friends; elem != NULL; elem = bctbx_list_next(elem)) { LinphoneFriend *lf = (LinphoneFriend *)bctbx_list_get_data(elem); if (bctbx_list_find(lf->insubs, op)) return lf; } return NULL; } LinphoneFriend * linphone_friend_list_find_friend_by_out_subscribe ( const LinphoneFriendList *list, LinphonePrivate::SalOp *op ) { const bctbx_list_t *elem; for (elem = list->friends; elem != NULL; elem = bctbx_list_next(elem)) { LinphoneFriend *lf = (LinphoneFriend *)bctbx_list_get_data(elem); if (lf->outsub && ((lf->outsub == op) || lf->outsub->isForkedOf(op))) return lf; } return NULL; } static void linphone_friend_list_close_subscriptions(LinphoneFriendList *list) { /* FIXME we should wait until subscription to complete. */ if (list->event) { linphone_event_terminate(list->event); linphone_event_unref(list->event); list->event = NULL; } bctbx_list_for_each(list->friends, (void (*)(void *))linphone_friend_close_subscriptions); } static void _linphone_friend_list_send_list_subscription_with_body(LinphoneFriendList *list, const LinphoneAddress *address) { char *xml_content = create_resource_list_xml(list); if (!xml_content) return; unsigned char digest[16]; bctbx_md5((unsigned char *)xml_content, strlen(xml_content), digest); if (list->event && list->content_digest && (memcmp(list->content_digest, digest, sizeof(digest)) == 0)) { /* The content has not changed, only refresh the event. */ linphone_event_refresh_subscribe(list->event); } else { LinphoneContent *content; bctbx_list_t * elem = NULL; int expires = lp_config_get_int(list->lc->config, "sip", "rls_presence_expires", 3600); list->expected_notification_version = 0; if (list->content_digest) ms_free(list->content_digest); list->content_digest = reinterpret_cast(ms_malloc(sizeof(digest))); memcpy(list->content_digest, digest, sizeof(digest)); if (list->event) { linphone_event_terminate(list->event); linphone_event_unref(list->event); } list->event = linphone_core_create_subscribe(list->lc, address, "presence", expires); linphone_event_ref(list->event); linphone_event_set_internal(list->event, TRUE); linphone_event_add_custom_header(list->event, "Require", "recipient-list-subscribe"); linphone_event_add_custom_header(list->event, "Supported", "eventlist"); linphone_event_add_custom_header(list->event, "Accept", "multipart/related, application/pidf+xml, application/rlmi+xml"); linphone_event_add_custom_header(list->event, "Content-Disposition", "recipient-list"); content = linphone_core_create_content(list->lc); linphone_content_set_type(content, "application"); linphone_content_set_subtype(content, "resource-lists+xml"); linphone_content_set_string_buffer(content, xml_content); if (linphone_core_content_encoding_supported(list->lc, "deflate")) { linphone_content_set_encoding(content, "deflate"); linphone_event_add_custom_header(list->event, "Accept-Encoding", "deflate"); } for (elem = list->friends; elem != NULL; elem = bctbx_list_next(elem)) { LinphoneFriend *lf = (LinphoneFriend *)elem->data; lf->subscribe_active = TRUE; } linphone_event_send_subscribe(list->event, content); linphone_content_unref(content); linphone_event_set_user_data(list->event, list); } ms_free(xml_content); } static void _linphone_friend_list_send_list_subscription_without_body(LinphoneFriendList *list, const LinphoneAddress *address) { bctbx_list_t *elem = NULL; int expires = lp_config_get_int(list->lc->config, "sip", "rls_presence_expires", 3600); list->expected_notification_version = 0; if (list->content_digest) ms_free(list->content_digest); if (list->event) { linphone_event_terminate(list->event); linphone_event_unref(list->event); } list->event = linphone_core_create_subscribe(list->lc, address, "presence", expires); linphone_event_ref(list->event); linphone_event_set_internal(list->event, TRUE); linphone_event_add_custom_header(list->event, "Supported", "eventlist"); linphone_event_add_custom_header(list->event, "Accept", "multipart/related, application/pidf+xml, application/rlmi+xml"); if (linphone_core_content_encoding_supported(list->lc, "deflate")) linphone_event_add_custom_header(list->event, "Accept-Encoding", "deflate"); for (elem = list->friends; elem != NULL; elem = bctbx_list_next(elem)) { LinphoneFriend *lf = (LinphoneFriend *)elem->data; lf->subscribe_active = TRUE; } linphone_event_send_subscribe(list->event, NULL); linphone_event_set_user_data(list->event, list); } static void linphone_friend_list_send_list_subscription(LinphoneFriendList *list) { const LinphoneAddress *address = _linphone_friend_list_get_rls_address(list); if (!address) return; if (!linphone_friend_list_has_subscribe_inactive(list)) return; if (list->bodyless_subscription) _linphone_friend_list_send_list_subscription_without_body(list, address); else _linphone_friend_list_send_list_subscription_with_body(list, address); } void linphone_friend_list_update_subscriptions(LinphoneFriendList *list) { LinphoneProxyConfig *cfg = NULL; const LinphoneAddress *address = _linphone_friend_list_get_rls_address(list); bool_t only_when_registered = FALSE; bool_t should_send_list_subscribe = FALSE; if (list->lc){ if (address) cfg = linphone_core_lookup_known_proxy(list->lc, address); only_when_registered = linphone_core_should_subscribe_friends_only_when_registered(list->lc); //in case of only_when_registered, proxy config is mandatory to send subscribes. Otherwise, unexpected subscribtion can be issued using default contact address even if no account is configured yet. should_send_list_subscribe = (!only_when_registered || (cfg && cfg->state == LinphoneRegistrationOk)); } if (address != NULL) { if (list->enable_subscriptions) { if (should_send_list_subscribe){ linphone_friend_list_send_list_subscription(list); } else { if (list->event){ linphone_event_terminate(list->event); linphone_event_unref(list->event); list->event = NULL; ms_message("Friends list [%p] subscription terminated because proxy config lost connection", list); }else{ ms_message("Friends list [%p] subscription update skipped since dependant proxy config is not yet registered", list); } } } else { ms_message("Friends list [%p] subscription update skipped since subscriptions not enabled yet", list); } } else if (list->enable_subscriptions) { const bctbx_list_t *elem; for (elem = list->friends; elem != NULL; elem = bctbx_list_next(elem)) { LinphoneFriend *lf = (LinphoneFriend *)bctbx_list_get_data(elem); linphone_friend_update_subscribes(lf, only_when_registered); } } } void linphone_friend_list_invalidate_subscriptions(LinphoneFriendList *list) { const bctbx_list_t *elem; // Terminate subscription event if (list->event) { linphone_event_terminate(list->event); linphone_event_unref(list->event); list->event = NULL; } for (elem = list->friends; elem != NULL; elem = bctbx_list_next(elem)) { LinphoneFriend *lf = (LinphoneFriend *)bctbx_list_get_data(elem); linphone_friend_invalidate_subscription(lf); } } void linphone_friend_list_notify_presence(LinphoneFriendList *list, LinphonePresenceModel *presence) { const bctbx_list_t *elem; for(elem = list->friends; elem != NULL; elem = bctbx_list_next(elem)) { LinphoneFriend *lf = (LinphoneFriend *)bctbx_list_get_data(elem); linphone_friend_notify(lf, presence); } } void linphone_friend_list_notify_presence_received(LinphoneFriendList *list, LinphoneEvent *lev, const LinphoneContent *body) { if (!linphone_content_is_multipart(body)) return; LinphoneContent *first_part; const char *type = linphone_content_get_type(body); const char *subtype = linphone_content_get_subtype(body); if ((strcmp(type, "multipart") != 0) || (strcmp(subtype, "related") != 0)) { ms_warning("multipart presence notified but it is not 'multipart/related', instead is '%s/%s'", type, subtype); return; } first_part = linphone_content_get_part(body, 0); if (first_part == NULL) { ms_warning("'multipart/related' presence notified but it doesn't contain any part"); return; } type = linphone_content_get_type(first_part); subtype = linphone_content_get_subtype(first_part); if ((strcmp(type, "application") != 0) || (strcmp(subtype, "rlmi+xml") != 0)) { ms_warning("multipart presence notified but first part is not 'application/rlmi+xml'"); linphone_content_unref(first_part); return; } linphone_friend_list_parse_multipart_related_body(list, body, linphone_content_get_string_buffer(first_part)); linphone_content_unref(first_part); } const char * linphone_friend_list_get_uri(const LinphoneFriendList *list) { return list->uri; } void linphone_friend_list_set_uri(LinphoneFriendList *list, const char *uri) { if (list->uri != NULL) { ms_free(list->uri); list->uri = NULL; } if (uri != NULL) { list->uri = ms_strdup(uri); linphone_core_store_friends_list_in_db(list->lc, list); } } bool_t linphone_friend_list_is_subscription_bodyless(LinphoneFriendList *list) { return list->bodyless_subscription; } void linphone_friend_list_set_subscription_bodyless(LinphoneFriendList *list, bool_t bodyless) { list->bodyless_subscription = bodyless; } void linphone_friend_list_update_revision(LinphoneFriendList *list, int rev) { list->revision = rev; linphone_core_store_friends_list_in_db(list->lc, list); } void linphone_friend_list_subscription_state_changed(LinphoneCore *lc, LinphoneEvent *lev, LinphoneSubscriptionState state) { LinphoneFriendList *list = (LinphoneFriendList *)linphone_event_get_user_data(lev); if (!list) { ms_warning("core [%p] Receiving unexpected state [%s] for event [%p], no associated friend list",lc , linphone_subscription_state_to_string(state) , lev); } else { ms_message("Receiving new state [%s] for event [%p] for friend list [%p]" , linphone_subscription_state_to_string(state) , lev , list); if (state == LinphoneSubscriptionOutgoingProgress && linphone_event_get_reason(lev) == LinphoneReasonNoMatch) { ms_message("Reseting version count for friend list [%p]",list); list->expected_notification_version = 0; } } } LinphoneCore* linphone_friend_list_get_core(const LinphoneFriendList *list) { return list->lc; } static LinphoneStatus linphone_friend_list_import_friends_from_vcard4(LinphoneFriendList *list, bctbx_list_t *vcards) { bctbx_list_t *vcards_iterator = NULL; int count = 0; if (!linphone_core_vcard_supported()) { ms_error("vCard support wasn't enabled at compilation time"); return -1; } if (!list) { ms_error("Can't import into a NULL list"); return -1; } vcards_iterator = vcards; while (vcards_iterator != NULL && bctbx_list_get_data(vcards_iterator) != NULL) { LinphoneVcard *vcard = (LinphoneVcard *)bctbx_list_get_data(vcards_iterator); LinphoneFriend *lf = linphone_friend_new_from_vcard(vcard); linphone_vcard_unref(vcard); if (lf) { if (LinphoneFriendListOK == linphone_friend_list_import_friend(list, lf, TRUE)) { linphone_friend_save(lf, lf->lc); count++; } linphone_friend_unref(lf); } vcards_iterator = bctbx_list_next(vcards_iterator); } bctbx_list_free(vcards); linphone_core_store_friends_list_in_db(list->lc, list); return count; } LinphoneStatus linphone_friend_list_import_friends_from_vcard4_file(LinphoneFriendList *list, const char *vcard_file) { bctbx_list_t *vcards = NULL; if (!linphone_core_vcard_supported()) { ms_error("vCard support wasn't enabled at compilation time"); return -1; } if (!list) { ms_error("Can't import into a NULL list"); return -1; } vcards = linphone_vcard_context_get_vcard_list_from_file(list->lc->vcard_context, vcard_file); if (!vcards) { ms_error("Failed to parse the file %s", vcard_file); return -1; } return linphone_friend_list_import_friends_from_vcard4(list,vcards); } LinphoneStatus linphone_friend_list_import_friends_from_vcard4_buffer(LinphoneFriendList *list, const char *vcard_buffer) { bctbx_list_t *vcards = NULL; if (!linphone_core_vcard_supported()) { ms_error("vCard support wasn't enabled at compilation time"); return -1; } if (!list) { ms_error("Can't import into a NULL list"); return -1; } vcards = linphone_vcard_context_get_vcard_list_from_buffer(list->lc->vcard_context, vcard_buffer); if (!vcards) { ms_error("Failed to parse the buffer"); return -1; } return linphone_friend_list_import_friends_from_vcard4(list,vcards);} void linphone_friend_list_export_friends_as_vcard4_file(LinphoneFriendList *list, const char *vcard_file) { FILE *file = NULL; const bctbx_list_t *friends; if (!linphone_core_vcard_supported()) { ms_error("vCard support wasn't enabled at compilation time"); return; } file = fopen(vcard_file, "wb"); if (file == NULL) { ms_warning("Could not write %s ! Maybe it is read-only. Contacts will not be saved.", vcard_file); return; } friends = linphone_friend_list_get_friends(list); while (friends != NULL && bctbx_list_get_data(friends) != NULL) { LinphoneFriend *lf = (LinphoneFriend *)bctbx_list_get_data(friends); LinphoneVcard *vcard = linphone_friend_get_vcard(lf); if (vcard) { const char *vcard_text = linphone_vcard_as_vcard4_string(vcard); fprintf(file, "%s", vcard_text); } friends = bctbx_list_next(friends); } fclose(file); } void linphone_friend_list_enable_subscriptions(LinphoneFriendList *list, bool_t enabled) { if (list->enable_subscriptions != enabled) { list->enable_subscriptions = enabled; if (enabled) { linphone_friend_list_update_subscriptions(list); } else { linphone_friend_list_close_subscriptions(list); } } } bool_t linphone_friend_list_subscriptions_enabled(LinphoneFriendList *list) { return list->enable_subscriptions; } liblinphone-4.4.21/coreapi/help/000077500000000000000000000000001376572216100164715ustar00rootroot00000000000000liblinphone-4.4.21/coreapi/help/CMakeLists.txt000066400000000000000000000020661376572216100212350ustar00rootroot00000000000000############################################################################ # CMakeLists.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. # ############################################################################ add_subdirectory(examples/C) add_subdirectory(doc) liblinphone-4.4.21/coreapi/help/Makefile.am000066400000000000000000000000251376572216100205220ustar00rootroot00000000000000SUBDIRS=doc examples liblinphone-4.4.21/coreapi/help/doc/000077500000000000000000000000001376572216100172365ustar00rootroot00000000000000liblinphone-4.4.21/coreapi/help/doc/CMakeLists.txt000066400000000000000000000035341376572216100220030ustar00rootroot00000000000000############################################################################ # CMakeLists.txt # 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. # ############################################################################ add_subdirectory(doxygen) add_subdirectory(sphinx) if(ENABLE_JAVADOC) find_package(Java REQUIRED) set(JAVADOC_PACKAGES "org.linphone.core org.linphone.mediastream") set(JAVADOC_CLASSPATHS "${PROJECT_SOURCE_DIR}/java/common" "${PROJECT_SOURCE_DIR}/java/j2se" "${PROJECT_SOURCE_DIR}/mediastreamer2/java/src" ) string(REPLACE ";" ":" JAVADOC_CLASSPATHS "${JAVADOC_CLASSPATHS}") set(JAVADOC_TITLE "Linphone SDK ${PROJECT_VERSION} reference documentation") set(JAVADOC_JAVA_REFERENCE "http://docs.oracle.com/javase/8/docs/api/") set(JAVADOC_OUTPUT_DIR "${CMAKE_CURRENT_BINARY_DIR}/doc/java") set(JAVADOC_LOGFILE "${CMAKE_CURRENT_BINARY_DIR}/javadoc.log") configure_file("generate_javadoc.sh.in" "generate_javadoc.sh" @ONLY) add_custom_target(javadoc ALL COMMAND "${CMAKE_CURRENT_BINARY_DIR}/generate_javadoc.sh" ) endif() liblinphone-4.4.21/coreapi/help/doc/Makefile.am000066400000000000000000000000201376572216100212620ustar00rootroot00000000000000SUBDIRS=doxygen liblinphone-4.4.21/coreapi/help/doc/doxygen/000077500000000000000000000000001376572216100207135ustar00rootroot00000000000000liblinphone-4.4.21/coreapi/help/doc/doxygen/CMakeLists.txt000066400000000000000000000042371376572216100234610ustar00rootroot00000000000000################################################################################ # CMakeLists.txt # 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. # ################################################################################ if(ENABLE_DOC OR ENABLE_CXX_WRAPPER OR ENABLE_CSHARP_WRAPPER OR ENABLE_JAVA_WRAPPER OR ENABLE_SWIFT_WRAPPER) find_package(Doxygen REQUIRED) if(DOXYGEN_FOUND) set(top_srcdir "${PROJECT_SOURCE_DIR}") set(DOXYGEN_INPUT "") foreach (HEADER_FILE ${LINPHONE_HEADER_FILES}) string(CONCAT DOXYGEN_INPUT ${DOXYGEN_INPUT} " \"${HEADER_FILE}\"") endforeach () string(CONCAT DOXYGEN_INPUT ${DOXYGEN_INPUT} " \"${CMAKE_CURRENT_SOURCE_DIR}\"") string(CONCAT DOXYGEN_INPUT ${DOXYGEN_INPUT} " \"${PROJECT_SOURCE_DIR}/coreapi/help/examples/C\"") configure_file(${CMAKE_CURRENT_SOURCE_DIR}/Doxyfile.in ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile) set(DOC_INPUT_FILES ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile ${CMAKE_CURRENT_SOURCE_DIR}/doxygen.dox ${LINPHONE_HEADER_FILES} ) set(XML_DIR "${CMAKE_CURRENT_BINARY_DIR}/xml") set(LINPHONE_DOXYGEN_XML_DIR ${XML_DIR} PARENT_SCOPE) add_custom_command(OUTPUT "${XML_DIR}/index.xml" COMMAND ${CMAKE_COMMAND} -E remove -f xml/* COMMAND ${DOXYGEN_EXECUTABLE} ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile DEPENDS ${DOC_INPUT_FILES} ) add_custom_target(linphone-doc ALL DEPENDS "${XML_DIR}/index.xml") endif() endif() liblinphone-4.4.21/coreapi/help/doc/doxygen/Doxyfile.in000066400000000000000000003167131376572216100230410ustar00rootroot00000000000000# Doxyfile 1.8.11 # This file describes the settings to be used by the documentation system # doxygen (www.doxygen.org) for a project. # # All text after a double hash (##) is considered a comment and is placed in # front of the TAG it is preceding. # # All text after a single hash (#) is considered a comment and will be ignored. # The format is: # TAG = value [value, ...] # For lists, items can also be appended using: # TAG += value [value, ...] # Values that contain spaces should be placed between quotes (\" \"). #--------------------------------------------------------------------------- # Project related configuration options #--------------------------------------------------------------------------- # This tag specifies the encoding used for all characters in the config file # that follow. The default is UTF-8 which is also the encoding used for all text # before the first occurrence of this tag. Doxygen uses libiconv (or the iconv # built into libc) for the transcoding. See http://www.gnu.org/software/libiconv # for the list of possible encodings. # The default value is: UTF-8. DOXYFILE_ENCODING = UTF-8 # The PROJECT_NAME tag is a single word (or a sequence of words surrounded by # double-quotes, unless you are using Doxywizard) that should identify the # project for which the documentation is generated. This name is used in the # title of most generated pages and in a few other places. # The default value is: My Project. PROJECT_NAME = Liblinphone # The PROJECT_NUMBER tag can be used to enter a project or revision number. This # could be handy for archiving the generated documentation or if some version # control system is used. PROJECT_NUMBER = @LINPHONE_VERSION@ # Using the PROJECT_BRIEF tag one can provide an optional one line description # for a project that appears at the top of each page and should give viewer a # quick idea about the purpose of the project. Keep the description short. PROJECT_BRIEF = # With the PROJECT_LOGO tag one can specify a logo or an icon that is included # in the documentation. The maximum height of the logo should not exceed 55 # pixels and the maximum width should not exceed 200 pixels. Doxygen will copy # the logo to the output directory. PROJECT_LOGO = # The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path # into which the generated documentation will be written. If a relative path is # entered, it will be relative to the location where doxygen was started. If # left blank the current directory will be used. OUTPUT_DIRECTORY = . # If the CREATE_SUBDIRS tag is set to YES then doxygen will create 4096 sub- # directories (in 2 levels) under the output directory of each output format and # will distribute the generated files over these directories. Enabling this # option can be useful when feeding doxygen a huge amount of source files, where # putting all generated files in the same directory would otherwise causes # performance problems for the file system. # The default value is: NO. CREATE_SUBDIRS = NO # If the ALLOW_UNICODE_NAMES tag is set to YES, doxygen will allow non-ASCII # characters to appear in the names of generated files. If set to NO, non-ASCII # characters will be escaped, for example _xE3_x81_x84 will be used for Unicode # U+3044. # The default value is: NO. ALLOW_UNICODE_NAMES = NO # The OUTPUT_LANGUAGE tag is used to specify the language in which all # documentation generated by doxygen is written. Doxygen will use this # information to generate all constant output in the proper language. # Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Catalan, Chinese, # Chinese-Traditional, Croatian, Czech, Danish, Dutch, English (United States), # Esperanto, Farsi (Persian), Finnish, French, German, Greek, Hungarian, # Indonesian, Italian, Japanese, Japanese-en (Japanese with English messages), # Korean, Korean-en (Korean with English messages), Latvian, Lithuanian, # Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese, Romanian, Russian, # Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish, Swedish, Turkish, # Ukrainian and Vietnamese. # The default value is: English. OUTPUT_LANGUAGE = English # If the BRIEF_MEMBER_DESC tag is set to YES, doxygen will include brief member # descriptions after the members that are listed in the file and class # documentation (similar to Javadoc). Set to NO to disable this. # The default value is: YES. BRIEF_MEMBER_DESC = YES # If the REPEAT_BRIEF tag is set to YES, doxygen will prepend the brief # description of a member or function before the detailed description # # Note: If both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the # brief descriptions will be completely suppressed. # The default value is: YES. REPEAT_BRIEF = YES # This tag implements a quasi-intelligent brief description abbreviator that is # used to form the text in various listings. Each string in this list, if found # as the leading text of the brief description, will be stripped from the text # and the result, after processing the whole list, is used as the annotated # text. Otherwise, the brief description is used as-is. If left blank, the # following values are used ($name is automatically replaced with the name of # the entity):The $name class, The $name widget, The $name file, is, provides, # specifies, contains, represents, a, an and the. ABBREVIATE_BRIEF = # If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then # doxygen will generate a detailed section even if there is only a brief # description. # The default value is: NO. ALWAYS_DETAILED_SEC = NO # If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all # inherited members of a class in the documentation of that class as if those # members were ordinary class members. Constructors, destructors and assignment # operators of the base classes will not be shown. # The default value is: NO. INLINE_INHERITED_MEMB = NO # If the FULL_PATH_NAMES tag is set to YES, doxygen will prepend the full path # before files name in the file list and in the header files. If set to NO the # shortest path that makes the file name unique will be used # The default value is: YES. FULL_PATH_NAMES = NO # The STRIP_FROM_PATH tag can be used to strip a user-defined part of the path. # Stripping is only done if one of the specified strings matches the left-hand # part of the path. The tag can be used to show relative paths in the file list. # If left blank the directory from which doxygen is run is used as the path to # strip. # # Note that you can specify absolute paths here, but also relative paths, which # will be relative from the directory where doxygen is started. # This tag requires that the tag FULL_PATH_NAMES is set to YES. STRIP_FROM_PATH = # The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the # path mentioned in the documentation of a class, which tells the reader which # header file to include in order to use a class. If left blank only the name of # the header file containing the class definition is used. Otherwise one should # specify the list of include paths that are normally passed to the compiler # using the -I flag. STRIP_FROM_INC_PATH = # If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter (but # less readable) file names. This can be useful is your file systems doesn't # support long names like on DOS, Mac, or CD-ROM. # The default value is: NO. SHORT_NAMES = NO # If the JAVADOC_AUTOBRIEF tag is set to YES then doxygen will interpret the # first line (until the first dot) of a Javadoc-style comment as the brief # description. If set to NO, the Javadoc-style will behave just like regular Qt- # style comments (thus requiring an explicit @brief command for a brief # description.) # The default value is: NO. JAVADOC_AUTOBRIEF = YES # If the QT_AUTOBRIEF tag is set to YES then doxygen will interpret the first # line (until the first dot) of a Qt-style comment as the brief description. If # set to NO, the Qt-style will behave just like regular Qt-style comments (thus # requiring an explicit \brief command for a brief description.) # The default value is: NO. QT_AUTOBRIEF = NO # The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make doxygen treat a # multi-line C++ special comment block (i.e. a block of //! or /// comments) as # a brief description. This used to be the default behavior. The new default is # to treat a multi-line C++ comment block as a detailed description. Set this # tag to YES if you prefer the old behavior instead. # # Note that setting this tag to YES also means that rational rose comments are # not recognized any more. # The default value is: NO. MULTILINE_CPP_IS_BRIEF = NO # If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the # documentation from any documented member that it re-implements. # The default value is: YES. INHERIT_DOCS = YES # If the SEPARATE_MEMBER_PAGES tag is set to YES then doxygen will produce a new # page for each member. If set to NO, the documentation of a member will be part # of the file/class/namespace that contains it. # The default value is: NO. SEPARATE_MEMBER_PAGES = NO # The TAB_SIZE tag can be used to set the number of spaces in a tab. Doxygen # uses this value to replace tabs by spaces in code fragments. # Minimum value: 1, maximum value: 16, default value: 4. TAB_SIZE = 8 # This tag can be used to specify a number of aliases that act as commands in # the documentation. An alias has the form: # name=value # For example adding # "sideeffect=@par Side Effects:\n" # will allow you to put the command \sideeffect (or @sideeffect) in the # documentation, which will result in a user-defined paragraph with heading # "Side Effects:". You can put \n's in the value part of an alias to insert # newlines. ALIASES = bctbx_list{1}="A list of \ref \1 objects. \xmlonly \1 \endxmlonly" ALIASES += onTheFlyList="The objects inside the list are freshly allocated with a reference counter equal to one, so they need to be freed on list destruction with bctbx_list_free_with_data() for instance. \xmlonly \endxmlonly" ALIASES += donotwrap="\xmlonly \endxmlonly" ALIASES += maybenil="\xmlonly \endxmlonly" # This tag can be used to specify a number of word-keyword mappings (TCL only). # A mapping has the form "name=value". For example adding "class=itcl::class" # will allow you to use the command class in the itcl::class meaning. TCL_SUBST = # Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources # only. Doxygen will then generate output that is more tailored for C. For # instance, some of the names that are used will be different. The list of all # members will be omitted, etc. # The default value is: NO. OPTIMIZE_OUTPUT_FOR_C = YES # Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java or # Python sources only. Doxygen will then generate output that is more tailored # for that language. For instance, namespaces will be presented as packages, # qualified scopes will look different, etc. # The default value is: NO. OPTIMIZE_OUTPUT_JAVA = NO # Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran # sources. Doxygen will then generate output that is tailored for Fortran. # The default value is: NO. OPTIMIZE_FOR_FORTRAN = NO # Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL # sources. Doxygen will then generate output that is tailored for VHDL. # The default value is: NO. OPTIMIZE_OUTPUT_VHDL = NO # Doxygen selects the parser to use depending on the extension of the files it # parses. With this tag you can assign which parser to use for a given # extension. Doxygen has a built-in mapping, but you can override or extend it # using this tag. The format is ext=language, where ext is a file extension, and # language is one of the parsers supported by doxygen: IDL, Java, Javascript, # C#, C, C++, D, PHP, Objective-C, Python, Fortran (fixed format Fortran: # FortranFixed, free formatted Fortran: FortranFree, unknown formatted Fortran: # Fortran. In the later case the parser tries to guess whether the code is fixed # or free formatted code, this is the default for Fortran type files), VHDL. For # instance to make doxygen treat .inc files as Fortran files (default is PHP), # and .f files as C (default is Fortran), use: inc=Fortran f=C. # # Note: For files without extension you can use no_extension as a placeholder. # # Note that for custom extensions you also need to set FILE_PATTERNS otherwise # the files are not read by doxygen. EXTENSION_MAPPING = # If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments # according to the Markdown format, which allows for more readable # documentation. See http://daringfireball.net/projects/markdown/ for details. # The output of markdown processing is further processed by doxygen, so you can # mix doxygen, HTML, and XML commands with Markdown formatting. Disable only in # case of backward compatibilities issues. # The default value is: YES. MARKDOWN_SUPPORT = YES # When enabled doxygen tries to link words that correspond to documented # classes, or namespaces to their corresponding documentation. Such a link can # be prevented in individual cases by putting a % sign in front of the word or # globally by setting AUTOLINK_SUPPORT to NO. # The default value is: YES. AUTOLINK_SUPPORT = YES # If you use STL classes (i.e. std::string, std::vector, etc.) but do not want # to include (a tag file for) the STL sources as input, then you should set this # tag to YES in order to let doxygen match functions declarations and # definitions whose arguments contain STL classes (e.g. func(std::string); # versus func(std::string) {}). This also make the inheritance and collaboration # diagrams that involve STL classes more complete and accurate. # The default value is: NO. BUILTIN_STL_SUPPORT = NO # If you use Microsoft's C++/CLI language, you should set this option to YES to # enable parsing support. # The default value is: NO. CPP_CLI_SUPPORT = NO # Set the SIP_SUPPORT tag to YES if your project consists of sip (see: # http://www.riverbankcomputing.co.uk/software/sip/intro) sources only. Doxygen # will parse them like normal C++ but will assume all classes use public instead # of private inheritance when no explicit protection keyword is present. # The default value is: NO. SIP_SUPPORT = NO # For Microsoft's IDL there are propget and propput attributes to indicate # getter and setter methods for a property. Setting this option to YES will make # doxygen to replace the get and set methods by a property in the documentation. # This will only work if the methods are indeed getting or setting a simple # type. If this is not the case, or you want to show the methods anyway, you # should set this option to NO. # The default value is: YES. IDL_PROPERTY_SUPPORT = YES # If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC # tag is set to YES then doxygen will reuse the documentation of the first # member in the group (if any) for the other members of the group. By default # all members of a group must be documented explicitly. # The default value is: NO. DISTRIBUTE_GROUP_DOC = NO # If one adds a struct or class to a group and this option is enabled, then also # any nested class or struct is added to the same group. By default this option # is disabled and one has to add nested compounds explicitly via \ingroup. # The default value is: NO. GROUP_NESTED_COMPOUNDS = NO # Set the SUBGROUPING tag to YES to allow class member groups of the same type # (for instance a group of public functions) to be put as a subgroup of that # type (e.g. under the Public Functions section). Set it to NO to prevent # subgrouping. Alternatively, this can be done per class using the # \nosubgrouping command. # The default value is: YES. SUBGROUPING = YES # When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and unions # are shown inside the group in which they are included (e.g. using \ingroup) # instead of on a separate page (for HTML and Man pages) or section (for LaTeX # and RTF). # # Note that this feature does not work in combination with # SEPARATE_MEMBER_PAGES. # The default value is: NO. INLINE_GROUPED_CLASSES = NO # When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and unions # with only public data fields or simple typedef fields will be shown inline in # the documentation of the scope in which they are defined (i.e. file, # namespace, or group documentation), provided this scope is documented. If set # to NO, structs, classes, and unions are shown on a separate page (for HTML and # Man pages) or section (for LaTeX and RTF). # The default value is: NO. INLINE_SIMPLE_STRUCTS = NO # When TYPEDEF_HIDES_STRUCT tag is enabled, a typedef of a struct, union, or # enum is documented as struct, union, or enum with the name of the typedef. So # typedef struct TypeS {} TypeT, will appear in the documentation as a struct # with name TypeT. When disabled the typedef will appear as a member of a file, # namespace, or class. And the struct will be named TypeS. This can typically be # useful for C code in case the coding convention dictates that all compound # types are typedef'ed and only the typedef is referenced, never the tag name. # The default value is: NO. TYPEDEF_HIDES_STRUCT = NO # The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This # cache is used to resolve symbols given their name and scope. Since this can be # an expensive process and often the same symbol appears multiple times in the # code, doxygen keeps a cache of pre-resolved symbols. If the cache is too small # doxygen will become slower. If the cache is too large, memory is wasted. The # cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range # is 0..9, the default is 0, corresponding to a cache size of 2^16=65536 # symbols. At the end of a run doxygen will report the cache usage and suggest # the optimal cache size from a speed point of view. # Minimum value: 0, maximum value: 9, default value: 0. LOOKUP_CACHE_SIZE = 0 #--------------------------------------------------------------------------- # Build related configuration options #--------------------------------------------------------------------------- # If the EXTRACT_ALL tag is set to YES, doxygen will assume all entities in # documentation are documented, even if no documentation was available. Private # class members and static file members will be hidden unless the # EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES. # Note: This will also disable the warnings about undocumented members that are # normally produced when WARNINGS is set to YES. # The default value is: NO. EXTRACT_ALL = NO # If the EXTRACT_PRIVATE tag is set to YES, all private members of a class will # be included in the documentation. # The default value is: NO. EXTRACT_PRIVATE = NO # If the EXTRACT_PACKAGE tag is set to YES, all members with package or internal # scope will be included in the documentation. # The default value is: NO. EXTRACT_PACKAGE = NO # If the EXTRACT_STATIC tag is set to YES, all static members of a file will be # included in the documentation. # The default value is: NO. EXTRACT_STATIC = NO # If the EXTRACT_LOCAL_CLASSES tag is set to YES, classes (and structs) defined # locally in source files will be included in the documentation. If set to NO, # only classes defined in header files are included. Does not have any effect # for Java sources. # The default value is: YES. EXTRACT_LOCAL_CLASSES = YES # This flag is only useful for Objective-C code. If set to YES, local methods, # which are defined in the implementation section but not in the interface are # included in the documentation. If set to NO, only methods in the interface are # included. # The default value is: NO. EXTRACT_LOCAL_METHODS = NO # If this flag is set to YES, the members of anonymous namespaces will be # extracted and appear in the documentation as a namespace called # 'anonymous_namespace{file}', where file will be replaced with the base name of # the file that contains the anonymous namespace. By default anonymous namespace # are hidden. # The default value is: NO. EXTRACT_ANON_NSPACES = NO # If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all # undocumented members inside documented classes or files. If set to NO these # members will be included in the various overviews, but no documentation # section is generated. This option has no effect if EXTRACT_ALL is enabled. # The default value is: NO. HIDE_UNDOC_MEMBERS = NO # If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all # undocumented classes that are normally visible in the class hierarchy. If set # to NO, these classes will be included in the various overviews. This option # has no effect if EXTRACT_ALL is enabled. # The default value is: NO. HIDE_UNDOC_CLASSES = NO # If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend # (class|struct|union) declarations. If set to NO, these declarations will be # included in the documentation. # The default value is: NO. HIDE_FRIEND_COMPOUNDS = NO # If the HIDE_IN_BODY_DOCS tag is set to YES, doxygen will hide any # documentation blocks found inside the body of a function. If set to NO, these # blocks will be appended to the function's detailed documentation block. # The default value is: NO. HIDE_IN_BODY_DOCS = NO # The INTERNAL_DOCS tag determines if documentation that is typed after a # \internal command is included. If the tag is set to NO then the documentation # will be excluded. Set it to YES to include the internal documentation. # The default value is: NO. INTERNAL_DOCS = YES # If the CASE_SENSE_NAMES tag is set to NO then doxygen will only generate file # names in lower-case letters. If set to YES, upper-case letters are also # allowed. This is useful if you have classes or files whose names only differ # in case and if your file system supports case sensitive file names. Windows # and Mac users are advised to set this option to NO. # The default value is: system dependent. CASE_SENSE_NAMES = YES # If the HIDE_SCOPE_NAMES tag is set to NO then doxygen will show members with # their full class and namespace scopes in the documentation. If set to YES, the # scope will be hidden. # The default value is: NO. HIDE_SCOPE_NAMES = NO # If the HIDE_COMPOUND_REFERENCE tag is set to NO (default) then doxygen will # append additional text to a page's title, such as Class Reference. If set to # YES the compound reference will be hidden. # The default value is: NO. HIDE_COMPOUND_REFERENCE= NO # If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of # the files that are included by a file in the documentation of that file. # The default value is: YES. SHOW_INCLUDE_FILES = YES # If the SHOW_GROUPED_MEMB_INC tag is set to YES then Doxygen will add for each # grouped member an include statement to the documentation, telling the reader # which file to include in order to use the member. # The default value is: NO. SHOW_GROUPED_MEMB_INC = NO # If the FORCE_LOCAL_INCLUDES tag is set to YES then doxygen will list include # files with double quotes in the documentation rather than with sharp brackets. # The default value is: NO. FORCE_LOCAL_INCLUDES = NO # If the INLINE_INFO tag is set to YES then a tag [inline] is inserted in the # documentation for inline members. # The default value is: YES. INLINE_INFO = YES # If the SORT_MEMBER_DOCS tag is set to YES then doxygen will sort the # (detailed) documentation of file and class members alphabetically by member # name. If set to NO, the members will appear in declaration order. # The default value is: YES. SORT_MEMBER_DOCS = YES # If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the brief # descriptions of file, namespace and class members alphabetically by member # name. If set to NO, the members will appear in declaration order. Note that # this will also influence the order of the classes in the class list. # The default value is: NO. SORT_BRIEF_DOCS = NO # If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the # (brief and detailed) documentation of class members so that constructors and # destructors are listed first. If set to NO the constructors will appear in the # respective orders defined by SORT_BRIEF_DOCS and SORT_MEMBER_DOCS. # Note: If SORT_BRIEF_DOCS is set to NO this option is ignored for sorting brief # member documentation. # Note: If SORT_MEMBER_DOCS is set to NO this option is ignored for sorting # detailed member documentation. # The default value is: NO. SORT_MEMBERS_CTORS_1ST = NO # If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the hierarchy # of group names into alphabetical order. If set to NO the group names will # appear in their defined order. # The default value is: NO. SORT_GROUP_NAMES = NO # If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be sorted by # fully-qualified names, including namespaces. If set to NO, the class list will # be sorted only by class name, not including the namespace part. # Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. # Note: This option applies only to the class list, not to the alphabetical # list. # The default value is: NO. SORT_BY_SCOPE_NAME = NO # If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to do proper # type resolution of all parameters of a function it will reject a match between # the prototype and the implementation of a member function even if there is # only one candidate or it is obvious which candidate to choose by doing a # simple string match. By disabling STRICT_PROTO_MATCHING doxygen will still # accept a match between prototype and implementation in such cases. # The default value is: NO. STRICT_PROTO_MATCHING = NO # The GENERATE_TODOLIST tag can be used to enable (YES) or disable (NO) the todo # list. This list is created by putting \todo commands in the documentation. # The default value is: YES. GENERATE_TODOLIST = YES # The GENERATE_TESTLIST tag can be used to enable (YES) or disable (NO) the test # list. This list is created by putting \test commands in the documentation. # The default value is: YES. GENERATE_TESTLIST = YES # The GENERATE_BUGLIST tag can be used to enable (YES) or disable (NO) the bug # list. This list is created by putting \bug commands in the documentation. # The default value is: YES. GENERATE_BUGLIST = YES # The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or disable (NO) # the deprecated list. This list is created by putting \deprecated commands in # the documentation. # The default value is: YES. GENERATE_DEPRECATEDLIST= YES # The ENABLED_SECTIONS tag can be used to enable conditional documentation # sections, marked by \if ... \endif and \cond # ... \endcond blocks. ENABLED_SECTIONS = # The MAX_INITIALIZER_LINES tag determines the maximum number of lines that the # initial value of a variable or macro / define can have for it to appear in the # documentation. If the initializer consists of more lines than specified here # it will be hidden. Use a value of 0 to hide initializers completely. The # appearance of the value of individual variables and macros / defines can be # controlled using \showinitializer or \hideinitializer command in the # documentation regardless of this setting. # Minimum value: 0, maximum value: 10000, default value: 30. MAX_INITIALIZER_LINES = 30 # Set the SHOW_USED_FILES tag to NO to disable the list of files generated at # the bottom of the documentation of classes and structs. If set to YES, the # list will mention the files that were used to generate the documentation. # The default value is: YES. SHOW_USED_FILES = YES # Set the SHOW_FILES tag to NO to disable the generation of the Files page. This # will remove the Files entry from the Quick Index and from the Folder Tree View # (if specified). # The default value is: YES. SHOW_FILES = YES # Set the SHOW_NAMESPACES tag to NO to disable the generation of the Namespaces # page. This will remove the Namespaces entry from the Quick Index and from the # Folder Tree View (if specified). # The default value is: YES. SHOW_NAMESPACES = YES # The FILE_VERSION_FILTER tag can be used to specify a program or script that # doxygen should invoke to get the current version for each file (typically from # the version control system). Doxygen will invoke the program by executing (via # popen()) the command command input-file, where command is the value of the # FILE_VERSION_FILTER tag, and input-file is the name of an input file provided # by doxygen. Whatever the program writes to standard output is used as the file # version. For an example see the documentation. FILE_VERSION_FILTER = # The LAYOUT_FILE tag can be used to specify a layout file which will be parsed # by doxygen. The layout file controls the global structure of the generated # output files in an output format independent way. To create the layout file # that represents doxygen's defaults, run doxygen with the -l option. You can # optionally specify a file name after the option, if omitted DoxygenLayout.xml # will be used as the name of the layout file. # # Note that if you run doxygen from a directory containing a file called # DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE # tag is left empty. LAYOUT_FILE = # The CITE_BIB_FILES tag can be used to specify one or more bib files containing # the reference definitions. This must be a list of .bib files. The .bib # extension is automatically appended if omitted. This requires the bibtex tool # to be installed. See also http://en.wikipedia.org/wiki/BibTeX for more info. # For LaTeX the style of the bibliography can be controlled using # LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the # search path. See also \cite for info how to create references. CITE_BIB_FILES = #--------------------------------------------------------------------------- # Configuration options related to warning and progress messages #--------------------------------------------------------------------------- # The QUIET tag can be used to turn on/off the messages that are generated to # standard output by doxygen. If QUIET is set to YES this implies that the # messages are off. # The default value is: NO. QUIET = YES # The WARNINGS tag can be used to turn on/off the warning messages that are # generated to standard error (stderr) by doxygen. If WARNINGS is set to YES # this implies that the warnings are on. # # Tip: Turn warnings on while writing the documentation. # The default value is: YES. WARNINGS = YES # If the WARN_IF_UNDOCUMENTED tag is set to YES then doxygen will generate # warnings for undocumented members. If EXTRACT_ALL is set to YES then this flag # will automatically be disabled. # The default value is: YES. WARN_IF_UNDOCUMENTED = NO # If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for # potential errors in the documentation, such as not documenting some parameters # in a documented function, or documenting parameters that don't exist or using # markup commands wrongly. # The default value is: YES. WARN_IF_DOC_ERROR = YES # This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that # are documented, but have no documentation for their parameters or return # value. If set to NO, doxygen will only warn about wrong or incomplete # parameter documentation, but not about the absence of documentation. # The default value is: NO. WARN_NO_PARAMDOC = NO # If the WARN_AS_ERROR tag is set to YES then doxygen will immediately stop when # a warning is encountered. # The default value is: NO. WARN_AS_ERROR = NO # The WARN_FORMAT tag determines the format of the warning messages that doxygen # can produce. The string should contain the $file, $line, and $text tags, which # will be replaced by the file and line number from which the warning originated # and the warning text. Optionally the format may contain $version, which will # be replaced by the version of the file (if it could be obtained via # FILE_VERSION_FILTER) # The default value is: $file:$line: $text. WARN_FORMAT = "$file:$line: $text" # The WARN_LOGFILE tag can be used to specify a file to which warning and error # messages should be written. If left blank the output is written to standard # error (stderr). WARN_LOGFILE = #--------------------------------------------------------------------------- # Configuration options related to the input files #--------------------------------------------------------------------------- # The INPUT tag is used to specify the files and/or directories that contain # documented source files. You may enter file names like myfile.cpp or # directories like /usr/src/myproject. Separate the files or directories with # spaces. See also FILE_PATTERNS and EXTENSION_MAPPING # Note: If this tag is empty the current directory is searched. INPUT = @DOXYGEN_INPUT@ # This tag can be used to specify the character encoding of the source files # that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses # libiconv (or the iconv built into libc) for the transcoding. See the libiconv # documentation (see: http://www.gnu.org/software/libiconv) for the list of # possible encodings. # The default value is: UTF-8. INPUT_ENCODING = UTF-8 # If the value of the INPUT tag contains directories, you can use the # FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and # *.h) to filter out the source-files in the directories. # # Note that for custom extensions or not directly supported extensions you also # need to set EXTENSION_MAPPING for the extension otherwise the files are not # read by doxygen. # # If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cpp, # *.c++, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h, # *.hh, *.hxx, *.hpp, *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc, # *.m, *.markdown, *.md, *.mm, *.dox, *.py, *.f90, *.f, *.for, *.tcl, *.vhd, # *.vhdl, *.ucf, *.qsf, *.as and *.js. FILE_PATTERNS = *.c \ *.h \ *.dox # The RECURSIVE tag can be used to specify whether or not subdirectories should # be searched for input files as well. # The default value is: NO. RECURSIVE = NO # The EXCLUDE tag can be used to specify files and/or directories that should be # excluded from the INPUT source files. This way you can easily exclude a # subdirectory from a directory tree whose root is specified with the INPUT tag. # # Note that relative paths are relative to the directory from which doxygen is # run. EXCLUDE = # The EXCLUDE_SYMLINKS tag can be used to select whether or not files or # directories that are symbolic links (a Unix file system feature) are excluded # from the input. # The default value is: NO. EXCLUDE_SYMLINKS = NO # If the value of the INPUT tag contains directories, you can use the # EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude # certain files from those directories. # # Note that the wildcards are matched against the file with absolute path, so to # exclude all test directories for example use the pattern */test/* EXCLUDE_PATTERNS = # The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names # (namespaces, classes, functions, etc.) that should be excluded from the # output. The symbol name can be a fully qualified name, a word, or if the # wildcard * is used, a substring. Examples: ANamespace, AClass, # AClass::ANamespace, ANamespace::*Test # # Note that the wildcards are matched against the file with absolute path, so to # exclude all test directories use the pattern */test/* EXCLUDE_SYMBOLS = # The EXAMPLE_PATH tag can be used to specify one or more files or directories # that contain example code fragments that are included (see the \include # command). EXAMPLE_PATH = @top_srcdir@ \ @top_srcdir@/coreapi/help/examples/C # If the value of the EXAMPLE_PATH tag contains directories, you can use the # EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and # *.h) to filter out the source-files in the directories. If left blank all # files are included. EXAMPLE_PATTERNS = # If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be # searched for input files to be used with the \include or \dontinclude commands # irrespective of the value of the RECURSIVE tag. # The default value is: NO. EXAMPLE_RECURSIVE = NO # The IMAGE_PATH tag can be used to specify one or more files or directories # that contain images that are to be included in the documentation (see the # \image command). IMAGE_PATH = # The INPUT_FILTER tag can be used to specify a program that doxygen should # invoke to filter for each input file. Doxygen will invoke the filter program # by executing (via popen()) the command: # # # # where is the value of the INPUT_FILTER tag, and is the # name of an input file. Doxygen will then use the output that the filter # program writes to standard output. If FILTER_PATTERNS is specified, this tag # will be ignored. # # Note that the filter must not add or remove lines; it is applied before the # code is scanned, but not when the output code is generated. If lines are added # or removed, the anchors will not be placed correctly. # # Note that for custom extensions or not directly supported extensions you also # need to set EXTENSION_MAPPING for the extension otherwise the files are not # properly processed by doxygen. INPUT_FILTER = # The FILTER_PATTERNS tag can be used to specify filters on a per file pattern # basis. Doxygen will compare the file name with each pattern and apply the # filter if there is a match. The filters are a list of the form: pattern=filter # (like *.cpp=my_cpp_filter). See INPUT_FILTER for further information on how # filters are used. If the FILTER_PATTERNS tag is empty or if none of the # patterns match the file name, INPUT_FILTER is applied. # # Note that for custom extensions or not directly supported extensions you also # need to set EXTENSION_MAPPING for the extension otherwise the files are not # properly processed by doxygen. FILTER_PATTERNS = # If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using # INPUT_FILTER) will also be used to filter the input files that are used for # producing the source files to browse (i.e. when SOURCE_BROWSER is set to YES). # The default value is: NO. FILTER_SOURCE_FILES = NO # The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file # pattern. A pattern will override the setting for FILTER_PATTERN (if any) and # it is also possible to disable source filtering for a specific pattern using # *.ext= (so without naming a filter). # This tag requires that the tag FILTER_SOURCE_FILES is set to YES. FILTER_SOURCE_PATTERNS = # If the USE_MDFILE_AS_MAINPAGE tag refers to the name of a markdown file that # is part of the input, its contents will be placed on the main page # (index.html). This can be useful if you have a project on for instance GitHub # and want to reuse the introduction page also for the doxygen output. USE_MDFILE_AS_MAINPAGE = #--------------------------------------------------------------------------- # Configuration options related to source browsing #--------------------------------------------------------------------------- # If the SOURCE_BROWSER tag is set to YES then a list of source files will be # generated. Documented entities will be cross-referenced with these sources. # # Note: To get rid of all source code in the generated output, make sure that # also VERBATIM_HEADERS is set to NO. # The default value is: NO. SOURCE_BROWSER = NO # Setting the INLINE_SOURCES tag to YES will include the body of functions, # classes and enums directly into the documentation. # The default value is: NO. INLINE_SOURCES = NO # Setting the STRIP_CODE_COMMENTS tag to YES will instruct doxygen to hide any # special comment blocks from generated source code fragments. Normal C, C++ and # Fortran comments will always remain visible. # The default value is: YES. STRIP_CODE_COMMENTS = YES # If the REFERENCED_BY_RELATION tag is set to YES then for each documented # function all documented functions referencing it will be listed. # The default value is: NO. REFERENCED_BY_RELATION = NO # If the REFERENCES_RELATION tag is set to YES then for each documented function # all documented entities called/used by that function will be listed. # The default value is: NO. REFERENCES_RELATION = NO # If the REFERENCES_LINK_SOURCE tag is set to YES and SOURCE_BROWSER tag is set # to YES then the hyperlinks from functions in REFERENCES_RELATION and # REFERENCED_BY_RELATION lists will link to the source code. Otherwise they will # link to the documentation. # The default value is: YES. REFERENCES_LINK_SOURCE = YES # If SOURCE_TOOLTIPS is enabled (the default) then hovering a hyperlink in the # source code will show a tooltip with additional information such as prototype, # brief description and links to the definition and documentation. Since this # will make the HTML file larger and loading of large files a bit slower, you # can opt to disable this feature. # The default value is: YES. # This tag requires that the tag SOURCE_BROWSER is set to YES. SOURCE_TOOLTIPS = YES # If the USE_HTAGS tag is set to YES then the references to source code will # point to the HTML generated by the htags(1) tool instead of doxygen built-in # source browser. The htags tool is part of GNU's global source tagging system # (see http://www.gnu.org/software/global/global.html). You will need version # 4.8.6 or higher. # # To use it do the following: # - Install the latest version of global # - Enable SOURCE_BROWSER and USE_HTAGS in the config file # - Make sure the INPUT points to the root of the source tree # - Run doxygen as normal # # Doxygen will invoke htags (and that will in turn invoke gtags), so these # tools must be available from the command line (i.e. in the search path). # # The result: instead of the source browser generated by doxygen, the links to # source code will now point to the output of htags. # The default value is: NO. # This tag requires that the tag SOURCE_BROWSER is set to YES. USE_HTAGS = NO # If the VERBATIM_HEADERS tag is set the YES then doxygen will generate a # verbatim copy of the header file for each class for which an include is # specified. Set to NO to disable this. # See also: Section \class. # The default value is: YES. VERBATIM_HEADERS = NO #--------------------------------------------------------------------------- # Configuration options related to the alphabetical class index #--------------------------------------------------------------------------- # If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index of all # compounds will be generated. Enable this if the project contains a lot of # classes, structs, unions or interfaces. # The default value is: YES. ALPHABETICAL_INDEX = NO # The COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns in # which the alphabetical index list will be split. # Minimum value: 1, maximum value: 20, default value: 5. # This tag requires that the tag ALPHABETICAL_INDEX is set to YES. COLS_IN_ALPHA_INDEX = 5 # In case all classes in a project start with a common prefix, all classes will # be put under the same header in the alphabetical index. The IGNORE_PREFIX tag # can be used to specify a prefix (or a list of prefixes) that should be ignored # while generating the index headers. # This tag requires that the tag ALPHABETICAL_INDEX is set to YES. IGNORE_PREFIX = #--------------------------------------------------------------------------- # Configuration options related to the HTML output #--------------------------------------------------------------------------- # If the GENERATE_HTML tag is set to YES, doxygen will generate HTML output # The default value is: YES. GENERATE_HTML = NO # The HTML_OUTPUT tag is used to specify where the HTML docs will be put. If a # relative path is entered the value of OUTPUT_DIRECTORY will be put in front of # it. # The default directory is: html. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_OUTPUT = html # The HTML_FILE_EXTENSION tag can be used to specify the file extension for each # generated HTML page (for example: .htm, .php, .asp). # The default value is: .html. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_FILE_EXTENSION = .html # The HTML_HEADER tag can be used to specify a user-defined HTML header file for # each generated HTML page. If the tag is left blank doxygen will generate a # standard header. # # To get valid HTML the header file that includes any scripts and style sheets # that doxygen needs, which is dependent on the configuration options used (e.g. # the setting GENERATE_TREEVIEW). It is highly recommended to start with a # default header using # doxygen -w html new_header.html new_footer.html new_stylesheet.css # YourConfigFile # and then modify the file new_header.html. See also section "Doxygen usage" # for information on how to generate the default header that doxygen normally # uses. # Note: The header is subject to change so you typically have to regenerate the # default header when upgrading to a newer version of doxygen. For a description # of the possible markers and block names see the documentation. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_HEADER = # The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each # generated HTML page. If the tag is left blank doxygen will generate a standard # footer. See HTML_HEADER for more information on how to generate a default # footer and what special commands can be used inside the footer. See also # section "Doxygen usage" for information on how to generate the default footer # that doxygen normally uses. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_FOOTER = # The HTML_STYLESHEET tag can be used to specify a user-defined cascading style # sheet that is used by each HTML page. It can be used to fine-tune the look of # the HTML output. If left blank doxygen will generate a default style sheet. # See also section "Doxygen usage" for information on how to generate the style # sheet that doxygen normally uses. # Note: It is recommended to use HTML_EXTRA_STYLESHEET instead of this tag, as # it is more robust and this tag (HTML_STYLESHEET) will in the future become # obsolete. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_STYLESHEET = # The HTML_EXTRA_STYLESHEET tag can be used to specify additional user-defined # cascading style sheets that are included after the standard style sheets # created by doxygen. Using this option one can overrule certain style aspects. # This is preferred over using HTML_STYLESHEET since it does not replace the # standard style sheet and is therefore more robust against future updates. # Doxygen will copy the style sheet files to the output directory. # Note: The order of the extra style sheet files is of importance (e.g. the last # style sheet in the list overrules the setting of the previous ones in the # list). For an example see the documentation. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_EXTRA_STYLESHEET = # The HTML_EXTRA_FILES tag can be used to specify one or more extra images or # other source files which should be copied to the HTML output directory. Note # that these files will be copied to the base HTML output directory. Use the # $relpath^ marker in the HTML_HEADER and/or HTML_FOOTER files to load these # files. In the HTML_STYLESHEET file, use the file name only. Also note that the # files will be copied as-is; there are no commands or markers available. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_EXTRA_FILES = # The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen # will adjust the colors in the style sheet and background images according to # this color. Hue is specified as an angle on a colorwheel, see # http://en.wikipedia.org/wiki/Hue for more information. For instance the value # 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300 # purple, and 360 is red again. # Minimum value: 0, maximum value: 359, default value: 220. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_COLORSTYLE_HUE = 220 # The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors # in the HTML output. For a value of 0 the output will use grayscales only. A # value of 255 will produce the most vivid colors. # Minimum value: 0, maximum value: 255, default value: 100. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_COLORSTYLE_SAT = 100 # The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to the # luminance component of the colors in the HTML output. Values below 100 # gradually make the output lighter, whereas values above 100 make the output # darker. The value divided by 100 is the actual gamma applied, so 80 represents # a gamma of 0.8, The value 220 represents a gamma of 2.2, and 100 does not # change the gamma. # Minimum value: 40, maximum value: 240, default value: 80. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_COLORSTYLE_GAMMA = 80 # If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML # page will contain the date and time when the page was generated. Setting this # to YES can help to show when doxygen was last run and thus if the # documentation is up to date. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_TIMESTAMP = NO # If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML # documentation will contain sections that can be hidden and shown after the # page has loaded. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_DYNAMIC_SECTIONS = NO # With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries # shown in the various tree structured indices initially; the user can expand # and collapse entries dynamically later on. Doxygen will expand the tree to # such a level that at most the specified number of entries are visible (unless # a fully collapsed tree already exceeds this amount). So setting the number of # entries 1 will produce a full collapsed tree by default. 0 is a special value # representing an infinite number of entries and will result in a full expanded # tree by default. # Minimum value: 0, maximum value: 9999, default value: 100. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_INDEX_NUM_ENTRIES = 100 # If the GENERATE_DOCSET tag is set to YES, additional index files will be # generated that can be used as input for Apple's Xcode 3 integrated development # environment (see: http://developer.apple.com/tools/xcode/), introduced with # OSX 10.5 (Leopard). To create a documentation set, doxygen will generate a # Makefile in the HTML output directory. Running make will produce the docset in # that directory and running make install will install the docset in # ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at # startup. See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html # for more information. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. GENERATE_DOCSET = NO # This tag determines the name of the docset feed. A documentation feed provides # an umbrella under which multiple documentation sets from a single provider # (such as a company or product suite) can be grouped. # The default value is: Doxygen generated docs. # This tag requires that the tag GENERATE_DOCSET is set to YES. DOCSET_FEEDNAME = "Doxygen generated docs" # This tag specifies a string that should uniquely identify the documentation # set bundle. This should be a reverse domain-name style string, e.g. # com.mycompany.MyDocSet. Doxygen will append .docset to the name. # The default value is: org.doxygen.Project. # This tag requires that the tag GENERATE_DOCSET is set to YES. DOCSET_BUNDLE_ID = org.doxygen.Project # The DOCSET_PUBLISHER_ID tag specifies a string that should uniquely identify # the documentation publisher. This should be a reverse domain-name style # string, e.g. com.mycompany.MyDocSet.documentation. # The default value is: org.doxygen.Publisher. # This tag requires that the tag GENERATE_DOCSET is set to YES. DOCSET_PUBLISHER_ID = org.doxygen.Publisher # The DOCSET_PUBLISHER_NAME tag identifies the documentation publisher. # The default value is: Publisher. # This tag requires that the tag GENERATE_DOCSET is set to YES. DOCSET_PUBLISHER_NAME = Publisher # If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three # additional HTML index files: index.hhp, index.hhc, and index.hhk. The # index.hhp is a project file that can be read by Microsoft's HTML Help Workshop # (see: http://www.microsoft.com/en-us/download/details.aspx?id=21138) on # Windows. # # The HTML Help Workshop contains a compiler that can convert all HTML output # generated by doxygen into a single compiled HTML file (.chm). Compiled HTML # files are now used as the Windows 98 help format, and will replace the old # Windows help format (.hlp) on all Windows platforms in the future. Compressed # HTML files also contain an index, a table of contents, and you can search for # words in the documentation. The HTML workshop also contains a viewer for # compressed HTML files. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. GENERATE_HTMLHELP = NO # The CHM_FILE tag can be used to specify the file name of the resulting .chm # file. You can add a path in front of the file if the result should not be # written to the html output directory. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. CHM_FILE = # The HHC_LOCATION tag can be used to specify the location (absolute path # including file name) of the HTML help compiler (hhc.exe). If non-empty, # doxygen will try to run the HTML help compiler on the generated index.hhp. # The file has to be specified with full path. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. HHC_LOCATION = # The GENERATE_CHI flag controls if a separate .chi index file is generated # (YES) or that it should be included in the master .chm file (NO). # The default value is: NO. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. GENERATE_CHI = NO # The CHM_INDEX_ENCODING is used to encode HtmlHelp index (hhk), content (hhc) # and project file content. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. CHM_INDEX_ENCODING = # The BINARY_TOC flag controls whether a binary table of contents is generated # (YES) or a normal table of contents (NO) in the .chm file. Furthermore it # enables the Previous and Next buttons. # The default value is: NO. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. BINARY_TOC = NO # The TOC_EXPAND flag can be set to YES to add extra items for group members to # the table of contents of the HTML help documentation and to the tree view. # The default value is: NO. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. TOC_EXPAND = NO # If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and # QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that # can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help # (.qch) of the generated HTML documentation. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. GENERATE_QHP = NO # If the QHG_LOCATION tag is specified, the QCH_FILE tag can be used to specify # the file name of the resulting .qch file. The path specified is relative to # the HTML output folder. # This tag requires that the tag GENERATE_QHP is set to YES. QCH_FILE = # The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help # Project output. For more information please see Qt Help Project / Namespace # (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#namespace). # The default value is: org.doxygen.Project. # This tag requires that the tag GENERATE_QHP is set to YES. QHP_NAMESPACE = org.doxygen.Project # The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt # Help Project output. For more information please see Qt Help Project / Virtual # Folders (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#virtual- # folders). # The default value is: doc. # This tag requires that the tag GENERATE_QHP is set to YES. QHP_VIRTUAL_FOLDER = doc # If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom # filter to add. For more information please see Qt Help Project / Custom # Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom- # filters). # This tag requires that the tag GENERATE_QHP is set to YES. QHP_CUST_FILTER_NAME = # The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the # custom filter to add. For more information please see Qt Help Project / Custom # Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom- # filters). # This tag requires that the tag GENERATE_QHP is set to YES. QHP_CUST_FILTER_ATTRS = # The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this # project's filter section matches. Qt Help Project / Filter Attributes (see: # http://qt-project.org/doc/qt-4.8/qthelpproject.html#filter-attributes). # This tag requires that the tag GENERATE_QHP is set to YES. QHP_SECT_FILTER_ATTRS = # The QHG_LOCATION tag can be used to specify the location of Qt's # qhelpgenerator. If non-empty doxygen will try to run qhelpgenerator on the # generated .qhp file. # This tag requires that the tag GENERATE_QHP is set to YES. QHG_LOCATION = # If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files will be # generated, together with the HTML files, they form an Eclipse help plugin. To # install this plugin and make it available under the help contents menu in # Eclipse, the contents of the directory containing the HTML and XML files needs # to be copied into the plugins directory of eclipse. The name of the directory # within the plugins directory should be the same as the ECLIPSE_DOC_ID value. # After copying Eclipse needs to be restarted before the help appears. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. GENERATE_ECLIPSEHELP = NO # A unique identifier for the Eclipse help plugin. When installing the plugin # the directory name containing the HTML and XML files should also have this # name. Each documentation set should have its own identifier. # The default value is: org.doxygen.Project. # This tag requires that the tag GENERATE_ECLIPSEHELP is set to YES. ECLIPSE_DOC_ID = org.doxygen.Project # If you want full control over the layout of the generated HTML pages it might # be necessary to disable the index and replace it with your own. The # DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) at top # of each HTML page. A value of NO enables the index and the value YES disables # it. Since the tabs in the index contain the same information as the navigation # tree, you can set this option to YES if you also set GENERATE_TREEVIEW to YES. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. DISABLE_INDEX = NO # The GENERATE_TREEVIEW tag is used to specify whether a tree-like index # structure should be generated to display hierarchical information. If the tag # value is set to YES, a side panel will be generated containing a tree-like # index structure (just like the one that is generated for HTML Help). For this # to work a browser that supports JavaScript, DHTML, CSS and frames is required # (i.e. any modern browser). Windows users are probably better off using the # HTML help feature. Via custom style sheets (see HTML_EXTRA_STYLESHEET) one can # further fine-tune the look of the index. As an example, the default style # sheet generated by doxygen has an example that shows how to put an image at # the root of the tree instead of the PROJECT_NAME. Since the tree basically has # the same information as the tab index, you could consider setting # DISABLE_INDEX to YES when enabling this option. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. GENERATE_TREEVIEW = NO # The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that # doxygen will group on one line in the generated HTML documentation. # # Note that a value of 0 will completely suppress the enum values from appearing # in the overview section. # Minimum value: 0, maximum value: 20, default value: 4. # This tag requires that the tag GENERATE_HTML is set to YES. ENUM_VALUES_PER_LINE = 1 # If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be used # to set the initial width (in pixels) of the frame in which the tree is shown. # Minimum value: 0, maximum value: 1500, default value: 250. # This tag requires that the tag GENERATE_HTML is set to YES. TREEVIEW_WIDTH = 250 # If the EXT_LINKS_IN_WINDOW option is set to YES, doxygen will open links to # external symbols imported via tag files in a separate window. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. EXT_LINKS_IN_WINDOW = NO # Use this tag to change the font size of LaTeX formulas included as images in # the HTML documentation. When you change the font size after a successful # doxygen run you need to manually remove any form_*.png images from the HTML # output directory to force them to be regenerated. # Minimum value: 8, maximum value: 50, default value: 10. # This tag requires that the tag GENERATE_HTML is set to YES. FORMULA_FONTSIZE = 10 # Use the FORMULA_TRANPARENT tag to determine whether or not the images # generated for formulas are transparent PNGs. Transparent PNGs are not # supported properly for IE 6.0, but are supported on all modern browsers. # # Note that when changing this option you need to delete any form_*.png files in # the HTML output directory before the changes have effect. # The default value is: YES. # This tag requires that the tag GENERATE_HTML is set to YES. FORMULA_TRANSPARENT = YES # Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see # http://www.mathjax.org) which uses client side Javascript for the rendering # instead of using pre-rendered bitmaps. Use this if you do not have LaTeX # installed or if you want to formulas look prettier in the HTML output. When # enabled you may also need to install MathJax separately and configure the path # to it using the MATHJAX_RELPATH option. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. USE_MATHJAX = NO # When MathJax is enabled you can set the default output format to be used for # the MathJax output. See the MathJax site (see: # http://docs.mathjax.org/en/latest/output.html) for more details. # Possible values are: HTML-CSS (which is slower, but has the best # compatibility), NativeMML (i.e. MathML) and SVG. # The default value is: HTML-CSS. # This tag requires that the tag USE_MATHJAX is set to YES. MATHJAX_FORMAT = HTML-CSS # When MathJax is enabled you need to specify the location relative to the HTML # output directory using the MATHJAX_RELPATH option. The destination directory # should contain the MathJax.js script. For instance, if the mathjax directory # is located at the same level as the HTML output directory, then # MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax # Content Delivery Network so you can quickly see the result without installing # MathJax. However, it is strongly recommended to install a local copy of # MathJax from http://www.mathjax.org before deployment. # The default value is: http://cdn.mathjax.org/mathjax/latest. # This tag requires that the tag USE_MATHJAX is set to YES. MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest # The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax # extension names that should be enabled during MathJax rendering. For example # MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols # This tag requires that the tag USE_MATHJAX is set to YES. MATHJAX_EXTENSIONS = # The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces # of code that will be used on startup of the MathJax code. See the MathJax site # (see: http://docs.mathjax.org/en/latest/output.html) for more details. For an # example see the documentation. # This tag requires that the tag USE_MATHJAX is set to YES. MATHJAX_CODEFILE = # When the SEARCHENGINE tag is enabled doxygen will generate a search box for # the HTML output. The underlying search engine uses javascript and DHTML and # should work on any modern browser. Note that when using HTML help # (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET) # there is already a search function so this one should typically be disabled. # For large projects the javascript based search engine can be slow, then # enabling SERVER_BASED_SEARCH may provide a better solution. It is possible to # search using the keyboard; to jump to the search box use + S # (what the is depends on the OS and browser, but it is typically # , /