diff --git a/.cproject b/.cproject
index 8f37d4ae1..f4c0aff9b 100644
--- a/.cproject
+++ b/.cproject
@@ -39,6 +39,7 @@
+
diff --git a/.gitignore b/.gitignore
index 36ee91daf..b262a4641 100644
--- a/.gitignore
+++ b/.gitignore
@@ -50,22 +50,31 @@ coreapi/help/chatroom
coreapi/help/doc/
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
+share/certdata.txt
+coreapi/test_numbers
specs.c
*.orig
*.rej
*.kdev4
-*.lo
-*.la
*.swp
.deps
.libs
-coreapi/test_numbers
+tools/test_ecc
+tools/test_lsd
+tools/test_numbers
coreapi/help/notify
share/fresh-rootca.pem
-share/certdata.txt
tester/liblinphone_tester
tools/lp-gen-wrappers
tools/lpc2xml_test
@@ -85,10 +94,14 @@ tester/linphone_log.gz.txt
tools/auto_answer
tools/lp-autoanswer
build/macos/pkg-distribution.xml
-tester/record_for_lc_*.wav
+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
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 80f393877..ee02b53b1 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -21,30 +21,35 @@
############################################################################
cmake_minimum_required(VERSION 3.0)
-project(LINPHONE C CXX)
+project(linphone VERSION 3.10.2 LANGUAGES C CXX)
-set(LINPHONE_MAJOR_VERSION "3")
-set(LINPHONE_MINOR_VERSION "8")
-set(LINPHONE_MICRO_VERSION "5")
-set(LINPHONE_VERSION "${LINPHONE_MAJOR_VERSION}.${LINPHONE_MINOR_VERSION}.${LINPHONE_MICRO_VERSION}")
-set(LINPHONE_SO_VERSION "7")
+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 "9")
-file(GLOB LINPHONE_PO_FILES RELATIVE "${CMAKE_SOURCE_DIR}/po" "${CMAKE_SOURCE_DIR}/po/*.po")
+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)
-option(ENABLE_STATIC "Build static library (default is shared library)." NO)
+option(ENABLE_SHARED "Build shared library." YES)
+option(ENABLE_STATIC "Build static library." YES)
option(ENABLE_CONSOLE_UI "Turn on or off compilation of console interface." YES)
option(ENABLE_DATE "Use build date in internal version number." NO)
+cmake_dependent_option(ENABLE_DAEMON "Enable the linphone daemon interface." YES "NOT WIN32" NO)
option(ENABLE_DOC "Enable documentation generation with Doxygen." YES)
+option(ENABLE_JAVADOC "Add a target to generate documentation for Java API" NO)
option(ENABLE_GTK_UI "Turn on or off compilation of gtk interface." YES)
option(ENABLE_LDAP "Enable LDAP support." NO)
-option(ENABLE_MSG_STORAGE "Turn on compilation of message storage." YES)
-cmake_dependent_option(ENABLE_NOTIFY "Enable libnotify support." YES "ENABLE_GTK_UI" NO)
+option(ENABLE_LIME "Enable Instant Messaging Encryption." YES)
+option(ENABLE_SQLITE_STORAGE "Turn on compilation sqlite storage, for messages, contacts, history" YES)
+cmake_dependent_option(ENABLE_NOTIFY "Enable libnotify support." YES "ENABLE_GTK_UI;NOT APPLE" NO)
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)
@@ -54,63 +59,73 @@ option(ENABLE_VIDEO "Build with video support." YES)
cmake_dependent_option(ENABLE_ASSISTANT "Turn on assistant compiling." YES "ENABLE_GTK_UI" NO)
option(ENABLE_DEBUG_LOGS "Turn on or off debug level logs." NO)
option(ENABLE_NLS "Build with internationalisation support" YES)
+option(ENABLE_VCARD "Turn on compilation of vcard4 support." YES)
+macro(apply_compile_flags SOURCE_FILES)
+ if(${SOURCE_FILES})
+ set(options "")
+ foreach(a ${ARGV})
+ if(STRICT_OPTIONS_${a})
+ string(REPLACE ";" " " options_${a} "${STRICT_OPTIONS_${a}}")
+ set(options "${options} ${options_${a}}")
+ endif()
+ endforeach()
+ if(options)
+ set_source_files_properties(${${SOURCE_FILES}} PROPERTIES COMPILE_FLAGS "${options}")
+ endif()
+ endif()
+endmacro()
+
+if(ENABLE_STATIC)
+ set(LINPHONE_LIBS_FOR_TOOLS linphone-static)
+else()
+ set(LINPHONE_LIBS_FOR_TOOLS linphone)
+endif()
+
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake")
include(CheckSymbolExists)
include(CMakePushCheckState)
+include(GNUInstallDirs)
+
+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}")
-
- if(ENABLE_GTK_UI)
- if(NOT EXISTS "${CMAKE_CURRENT_BINARY_DIR}/intltool_win32.zip")
- message(STATUS "Installing intltool")
- file(DOWNLOAD http://ftp.acc.umu.se/pub/GNOME/binaries/win32/intltool/0.40/intltool_0.40.4-1_win32.zip "${CMAKE_CURRENT_BINARY_DIR}/intltool_win32.zip" SHOW_PROGRESS)
- execute_process(
- COMMAND "${CMAKE_COMMAND}" "-E" "tar" "x" "${CMAKE_CURRENT_BINARY_DIR}/intltool_win32.zip"
- WORKING_DIRECTORY "${CMAKE_INSTALL_PREFIX}"
- )
- endif()
- if(NOT EXISTS "${CMAKE_CURRENT_BINARY_DIR}/gtk+-bundle_win32.zip")
- message(STATUS "Installing GTK")
- file(DOWNLOAD http://ftp.gnome.org/pub/gnome/binaries/win32/gtk+/2.24/gtk+-bundle_2.24.10-20120208_win32.zip "${CMAKE_CURRENT_BINARY_DIR}/gtk+-bundle_win32.zip" SHOW_PROGRESS)
- execute_process(
- COMMAND "${CMAKE_COMMAND}" "-E" "tar" "x" "${CMAKE_CURRENT_BINARY_DIR}/gtk+-bundle_win32.zip"
- WORKING_DIRECTORY "${CMAKE_INSTALL_PREFIX}"
- )
- endif()
- endif()
endif()
-find_package(BelleSIP REQUIRED)
-find_package(Mediastreamer2 REQUIRED)
+# find_package should be invoked here to check for libraries - however do NOT
+# call include_directories here (see below)
+
+if(LINPHONE_BUILDER_GROUP_EXTERNAL_SOURCE_PATH_BUILDERS)
+ include("${EP_bellesip_CONFIG_DIR}/BelleSIPConfig.cmake")
+ include("${EP_ms2_CONFIG_DIR}/Mediastreamer2Config.cmake")
+ set(BcToolbox_FIND_COMPONENTS tester)
+ include("${EP_bctoolbox_CONFIG_DIR}/BcToolboxConfig.cmake")
+else()
+ find_package(BelleSIP REQUIRED)
+ find_package(Mediastreamer2 REQUIRED)
+ find_package(BcToolbox 0.0.3 REQUIRED OPTIONAL_COMPONENTS tester)
+endif()
find_package(XML2 REQUIRED)
find_package(Zlib)
-if(ENABLE_UNIT_TESTS)
- find_package(CUnit)
- if(CUNIT_FOUND)
- cmake_push_check_state(RESET)
- list(APPEND CMAKE_REQUIRED_INCLUDES ${CUNIT_INCLUDE_DIRS})
- list(APPEND CMAKE_REQUIRED_LIBRARIES ${CUNIT_LIBRARIES})
- check_symbol_exists("CU_get_suite" "CUnit/CUnit.h" HAVE_CU_GET_SUITE)
- check_symbol_exists("CU_curses_run_tests" "CUnit/CUnit.h" HAVE_CU_CURSES)
- cmake_pop_check_state()
- else()
- message(WARNING "Could not find the cunit library!")
- set(ENABLE_UNIT_TESTS OFF CACHE BOOL "Enable compilation of unit tests." FORCE)
- endif()
-endif()
if(ENABLE_TUNNEL)
- find_package(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_MSG_STORAGE)
+if(ENABLE_SQLITE_STORAGE)
find_package(Sqlite3 REQUIRED)
endif()
if(ENABLE_NOTIFY)
@@ -131,6 +146,15 @@ if(ENABLE_GTK_UI)
message(WARNING "You need at least GTK 2.22 to enable the assistant")
set(ENABLE_ASSISTANT OFF CACHE BOOL "Turn on assistant compiling." FORCE)
endif()
+ if(APPLE)
+ find_package(GtkMacIntegration)
+ if(GTKMACINTEGRATION_FOUND)
+ set(HAVE_GTK_OSX 1)
+ add_definitions("${GTKMACINTEGRATION_CPPFLAGS}")
+ else()
+ message(WARNING "gtk-mac-integration not found. Please install gtk-osx-application package.")
+ endif()
+ endif()
endif()
if(ENABLE_ASSISTANT)
set(BUILD_WIZARD 1)
@@ -138,7 +162,22 @@ endif()
if(ENABLE_NLS)
find_package(Gettext REQUIRED)
find_package(Intl REQUIRED)
- include_directories(${INTL_INCLUDE_DIRS})
+endif()
+if(ENABLE_LIME)
+ set(HAVE_LIME 1)
+endif()
+if (ENABLE_VCARD)
+ if(LINPHONE_BUILDER_GROUP_EXTERNAL_SOURCE_PATH_BUILDERS)
+ include("${EP_belcard_CONFIG_DIR}/BelcardConfig.cmake")
+ else()
+ find_package(Belcard)
+ endif()
+ 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()
if(UNIX AND NOT APPLE)
@@ -146,6 +185,13 @@ if(UNIX AND NOT APPLE)
check_include_files(libudev.h HAVE_LIBUDEV_H)
endif()
+set(LINPHONE_LDFLAGS "${BELLESIP_LDFLAGS} ${MEDIASTREAMER2_LDFLAGS}")
+if(BELCARD_FOUND AND APPLE)
+ set(LINPHONE_LDFLAGS "${LINPHONE_LDFLAGS} -stdlib=libc++")
+endif()
+
+# 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(
include/
coreapi/
@@ -153,49 +199,77 @@ include_directories(
${CMAKE_CURRENT_BINARY_DIR}/coreapi/
${BELLESIP_INCLUDE_DIRS}
${MEDIASTREAMER2_INCLUDE_DIRS}
- ${XML2_INCLUDE_DIRS}
+ ${BCTOOLBOX_CORE_INCLUDE_DIRS}
)
+if(ENABLE_TUNNEL)
+ include_directories(${TUNNEL_INCLUDE_DIRS})
+endif()
+if (ENABLE_VCARD)
+ include_directories(${BELCARD_INCLUDE_DIRS})
+endif()
+
+include_directories(${XML2_INCLUDE_DIRS})
+
if(ZLIB_FOUND)
include_directories(${ZLIB_INCLUDE_DIRS})
set(HAVE_ZLIB 1)
endif()
if(SQLITE3_FOUND)
include_directories(${SQLITE3_INCLUDE_DIRS})
- add_definitions("-DMSG_STORAGE_ENABLED")
+ if(ENABLE_SQLITE_STORAGE)
+ add_definitions("-DSQLITE_STORAGE_ENABLED")
+ endif()
endif()
-if(ENABLE_TUNNEL)
- include_directories(${TUNNEL_INCLUDE_DIRS})
-endif()
-if(ENABLE_DEBUG_LOGS)
- add_definitions("-DDEBUG")
+if(INTL_FOUND)
+ set(HAVE_INTL 1)
+ include_directories(${INTL_INCLUDE_DIRS})
endif()
if(MSVC)
include_directories(${MSVC_INCLUDE_DIR})
endif()
-if(INTL_FOUND)
- set(HAVE_INTL 1)
- include_directories(${INTL_INCLUDE_DIRECTORIES})
-endif()
add_definitions("-DIN_LINPHONE")
+if(ENABLE_DEBUG_LOGS)
+ add_definitions("-DDEBUG")
+endif()
+if(ANDROID)
+ add_definitions("-DANDROID")
+endif()
-
+set(STRICT_OPTIONS_CPP )
+set(STRICT_OPTIONS_C )
+set(STRICT_OPTIONS_CXX )
+set(STRICT_OPTIONS_OBJC )
if(MSVC)
- set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /W3")
- set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W3")
-else()
- set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wuninitialized -Wdeclaration-after-statement -fno-strict-aliasing -Werror")
- set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wuninitialized -Werror")
- if(CMAKE_C_COMPILER_ID STREQUAL "Clang")
- set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Qunused-arguments -Wno-array-bounds")
+ list(APPEND STRICT_OPTIONS_CPP "/wd4995") # Disable "name was marked as #pragma deprecated" warnings
+ if(ENABLE_STRICT)
+ list(APPEND STRICT_OPTIONS_CPP "/WX")
endif()
- if(CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
- set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Qunused-arguments -Wno-array-bounds")
+else()
+ list(APPEND STRICT_OPTIONS_CPP "-Wall" "-Wuninitialized" "-Wno-error=deprecated-declarations")
+ list(APPEND STRICT_OPTIONS_C "-Wdeclaration-after-statement" "-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 STREQUAL "Clang")
+ list(APPEND STRICT_OPTIONS_CPP "-Qunused-arguments" "-Wno-array-bounds")
endif()
if(APPLE)
- set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-error=unknown-warning-option -Wno-tautological-compare -Wno-unused-function")
- set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-error=unknown-warning-option -Wno-tautological-compare -Wno-unused-function")
+ 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(CMAKE_SYSTEM_NAME STREQUAL "WindowsStore")
+ list(APPEND STRICT_OPTIONS_CPP "/wd4996")
+endif()
+if(STRICT_OPTIONS_CPP)
+ list(REMOVE_DUPLICATES STRICT_OPTIONS_CPP)
+endif()
+if(STRICT_OPTIONS_C)
+ list(REMOVE_DUPLICATES STRICT_OPTIONS_C)
endif()
@@ -205,13 +279,13 @@ if(ENABLE_RELATIVE_PREFIX)
else()
set(LINPHONE_DATA_DIR "${CMAKE_INSTALL_PREFIX}")
endif()
-set(LINPHONE_PLUGINS_DIR "${LINPHONE_DATA_DIR}/lib/liblinphone/plugins")
+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}/share/locale")
-set(PACKAGE_DATA_DIR "${LINPHONE_DATA_DIR}/share")
-set(PACKAGE_SOUND_DIR "${LINPHONE_DATA_DIR}/share/sounds/linphone")
+set(PACKAGE_LOCALE_DIR "${LINPHONE_DATA_DIR}/${CMAKE_INSTALL_DATADIR}/locale")
+set(PACKAGE_DATA_DIR "${LINPHONE_DATA_DIR}/${CMAKE_INSTALL_DATADIR}")
+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)
@@ -224,11 +298,22 @@ if(ENABLE_VIDEO)
endif()
+if(LINPHONE_BUILDER_GROUP_EXTERNAL_SOURCE_PATH_BUILDERS)
+ set(EXPORT_TARGETS_NAME "LinphoneBuilder")
+else()
+ set(EXPORT_TARGETS_NAME "Linphone")
+endif()
+
+
+add_subdirectory(java)
add_subdirectory(coreapi)
add_subdirectory(share)
if(ENABLE_CONSOLE_UI)
add_subdirectory(console)
endif()
+if(ENABLE_DAEMON)
+ add_subdirectory(daemon)
+endif()
if(ENABLE_GTK_UI)
add_subdirectory(gtk)
add_subdirectory(pixmaps)
@@ -237,7 +322,7 @@ endif()
if(ENABLE_TOOLS)
add_subdirectory(tools)
endif()
-if(ENABLE_UNIT_TESTS)
+if(ENABLE_UNIT_TESTS AND BCTOOLBOX_TESTER_FOUND)
add_subdirectory(tester)
endif()
@@ -248,23 +333,21 @@ write_basic_package_version_file(
VERSION ${LINPHONE_VERSION}
COMPATIBILITY AnyNewerVersion
)
-export(EXPORT LinphoneTargets
+export(EXPORT ${EXPORT_TARGETS_NAME}Targets
FILE "${CMAKE_CURRENT_BINARY_DIR}/LinphoneTargets.cmake"
- NAMESPACE BelledonneCommunications::
)
configure_file(cmake/LinphoneConfig.cmake.in
"${CMAKE_CURRENT_BINARY_DIR}/LinphoneConfig.cmake"
@ONLY
)
-set(ConfigPackageLocation lib/cmake/Linphone)
-install(EXPORT LinphoneTargets
+set(CONFIG_PACKAGE_LOCATION "${CMAKE_INSTALL_DATADIR}/Linphone/cmake")
+install(EXPORT ${EXPORT_TARGETS_NAME}Targets
FILE LinphoneTargets.cmake
- NAMESPACE BelledonneCommunications::
- DESTINATION ${ConfigPackageLocation}
+ DESTINATION ${CONFIG_PACKAGE_LOCATION}
)
install(FILES
"${CMAKE_CURRENT_BINARY_DIR}/LinphoneConfig.cmake"
"${CMAKE_CURRENT_BINARY_DIR}/LinphoneConfigVersion.cmake"
- DESTINATION ${ConfigPackageLocation}
+ DESTINATION ${CONFIG_PACKAGE_LOCATION}
)
diff --git a/COPYING b/COPYING
index 365b8c279..ce1d99769 100644
--- a/COPYING
+++ b/COPYING
@@ -349,7 +349,7 @@ About The Cisco-Provided Binary of OpenH264 Video Codec
Cisco provides this program under the terms of the BSD license.
-Additionally, this binary is licensed under Cisco’s AVC/H.264 Patent Portfolio
+Additionally, this binary is licensed under Cisco's AVC/H.264 Patent Portfolio
License from MPEG LA, at no cost to you, provided that the requirements and
conditions shown below in the AVC/H.264 Patent Portfolio sections are met.
@@ -367,7 +367,7 @@ BSD terms, which can be found at http://www.openh264.org
BSD License
-----------
-Copyright © 2014 Cisco Systems, Inc.
+Copyright (C) 2014 Cisco Systems, Inc.
All rights reserved.
diff --git a/Makefile.am b/Makefile.am
index 007dc5dbf..7960711f0 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -4,8 +4,7 @@
ACLOCAL_AMFLAGS = -I m4 $(ACLOCAL_MACOS_FLAGS)
SUBDIRS = build m4 pixmaps po @ORTP_DIR@ @MS2_DIR@ \
- coreapi console gtk share scripts tools tester include
-
+ coreapi console gtk share scripts tools daemon tester include
GITVERSION=`cd $(top_srcdir) && git describe --always || echo $(VERSION)`
@@ -62,6 +61,30 @@ EXTRA_DIST = BUGS \
$(LINPHONEDEPS_FILELIST) \
$(ISS_SCRIPT).in
+EXTRA_DIST += CMakeLists.txt \
+cmake/FindGtkMacIntegration.cmake \
+cmake/FindIconv.cmake \
+cmake/FindIntl.cmake \
+cmake/FindNotify.cmake \
+cmake/FindSqlite3.cmake \
+cmake/FindXML2.cmake \
+cmake/FindZlib.cmake \
+cmake/LinphoneConfig.cmake.in \
+config.h.cmake \
+console/CMakeLists.txt \
+coreapi/CMakeLists.txt \
+coreapi/gitversion.cmake \
+coreapi/help/CMakeLists.txt \
+gtk/CMakeLists.txt \
+java/CMakeLists.txt \
+pixmaps/CMakeLists.txt \
+po/CMakeLists.txt \
+share/CMakeLists.txt \
+share/rings/CMakeLists.txt \
+share/rootca.cmake \
+tester/CMakeLists.txt \
+tools/CMakeLists.txt
+
DISTCLEANFILES= $(ISS_SCRIPT) $(PACKAGE_WIN32_FILELIST)
CLEANFILES=Portfile Portfile-devel
@@ -226,11 +249,7 @@ Linphone.app:
MS2_PLUGINS_INSTALL_PREFIX=$(prefix) \
LINPHONE_ADDITIONAL_DEPENDENCIES_PREFIX=$(LINPHONE_ADDITIONAL_DEPENDENCIES_PREFIX) \
gtk-mac-bundler $(PACKAGE_BUNDLE_FILE)
- printf "[Pango]\nModuleFiles=./etc/pango/pango.modules\n" \
- > $(BUNDLEDIR)/Contents/Resources/etc/pango/pangorc
- cp -f $(BUNDLEDIR)/Contents/Resources/etc/pango/pango.modules $(BUNDLEDIR)/Contents/Resources/etc/pango/pango.modules.orig
- sed -e 's:@executable_path.*/::g' $(BUNDLEDIR)/Contents/Resources/etc/pango/pango.modules.orig > $(BUNDLEDIR)/Contents/Resources/etc/pango/pango.modules
- patch -R ${BUNDLEDIR}/Contents/Resources/share/themes/Quartz/gtk-2.0/gtkrc ${srcdir}/build/macos/quartz-theme-gtkrc.patch
+ patch ${BUNDLEDIR}/Contents/Resources/share/themes/Quartz/gtk-2.0/gtkrc ${srcdir}/build/macos/quartz-theme-gtkrc.patch
rm -f ${BUNDLEDIR}/Contents/Resources/lib/libopenh264*
bundle: $(MACAPPNAME)
@@ -248,7 +267,7 @@ pkg: $(MACAPPNAME)
cp ${srcdir}/pixmaps/linphone.png ./packaging
pkgbuild --install-location /Applications --scripts ${srcdir}/build/macos/pkg-scripts --component $(MACAPPNAME) ./packaging/linphone.pkg
productbuild --resources . --distribution ${srcdir}/build/macos/pkg-distribution.xml --package-path ./packaging $(MACAPPPKG)
-
+
signed-pkg: pkg
mv $(MACAPPPKG) $(MACAPPPKG).tmp
productsign --sign "$(BUNDLE_SIGNING_ID)" $(MACAPPPKG).tmp $(MACAPPPKG)
@@ -263,7 +282,7 @@ clean-local:
discovery:
touch specs.c
$(CC) --include $(top_builddir)/config.h \
- $(TUNNEL_CFLAGS) $(CFLAGS) $(MEDIASTREAMER2_CFLAGS) $(ORTP_CFLAGS) $(SIPSTACK_CFLAGS) $(CUNIT_CFLAGS) -E -P -v -dD specs.c
+ $(TUNNEL_CFLAGS) $(CFLAGS) $(MEDIASTREAMER2_CFLAGS) $(ORTP_CFLAGS) $(SIPSTACK_CFLAGS) $(BCTOOLBOXTESTER_CFLAGS) -E -P -v -dD specs.c
.PHONY: $(MACAPPNAME) pkg
diff --git a/NEWS b/NEWS
index 385b7831a..0d5eb29eb 100644
--- a/NEWS
+++ b/NEWS
@@ -1,3 +1,36 @@
+linphone-3.10.2 -- August 30th, 2016
+ * Fixing linphone python version compilation - fixing vcards
+
+inphone-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
@@ -19,7 +52,7 @@ linphone-3.8.2 -- May 7th, 2015
* 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
@@ -67,14 +100,14 @@ linphone-3.7.0 -- February 20th, 2014
* 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 provisionning http URI for configuration
+ * Possibility to specify a remote provisioning http URI for configuration
* LDAP search integration for Linux and MacOSX
- * is-composing notification in chat area
+ * 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
+ * fully asynchronous behavior: no more lengthly DNS or connections
* +sip.instance parameter (RFC5626)
* alias parameter (RFC5923)
* better management of network disconnections
@@ -92,7 +125,7 @@ linphone-3.7.0 -- February 20th, 2014
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
@@ -157,9 +190,9 @@ linphone-3.4.1 -- February 17th, 2011
Requires mediastreamer-2.7.1
linphone-3.4.0 -- February 7th, 2011
- * implement multiple calls feature:
+ * implement multiple calls feature:
- call hold (with possibility to play a music file)
- - call resume
+ - call resume
- acceptance of 2nd call while putting the others on hold
- creation of another outgoing call while already in call
- blind call transfer
@@ -330,7 +363,7 @@ linphone-1.4.1 -- September 18, 2006
* do not change mixer settings at startup
linphone-1.4.0 -- September 11, 2006
- * no more glib dependency at all
+ * no more glib dependency at all
* new mediastreamer2 framework for audio/video streaming
* stable video support with H.263-1998
* echo cancelation
diff --git a/README b/README
index 7e0b97524..cde6c760e 100644
--- a/README
+++ b/README
@@ -1,7 +1,12 @@
This is Linphone, a free (GPL) video softphone based on the SIP protocol.
+# Warning
-******************Building linphone ***********************************
+Unless you exactly know what you are doing, you should take at look at [linphone-desktop](https://github.com/BelledonneCommunications/linphone-desktop).
+
+# Otherwise…
+
+## Building Linphone
- Install build time dependencies
- libtool
@@ -11,6 +16,7 @@ This is Linphone, a free (GPL) video softphone based on the SIP protocol.
- belle-sip>=1.3.0
- speex>=1.2.0 (including libspeexdsp part)
- libxml2
+ - bctoolbox
+ if you want the gtk/glade interface:
- libgtk >=2.16.0
@@ -41,7 +47,7 @@ libglew1.6-dev libv4l-dev libxml2-dev
libsqlite3-dev libupnp4-dev libsrtp-dev
+ Install zrtp (optional), for unbreakable call encryption
- $ git clone git://git.linphone.org:bzrtp
+ $ git clone git://git.linphone.org/bzrtp.git
$ cd bzrtp && ./autogen.sh && ./configure && make
$ sudo make install
@@ -58,7 +64,7 @@ For windows compilation see README.mingw.
For macOS X, see README.macos
-******************************** Notes for developers *****************************
+## Notes for developers
Here is a short description of the content of the source tree.
diff --git a/README.macos.md b/README.macos.md
index 5e34f88ad..b655ba141 100644
--- a/README.macos.md
+++ b/README.macos.md
@@ -23,7 +23,7 @@ In order to enable generation of bundle for older MacOS version, it is recommend
##### Linphone library (liblinphone)
- sudo port install automake autoconf libtool pkgconfig intltool wget cunit \
+ sudo port install automake autoconf libtool pkgconfig intltool wget bcunit \
antlr3 speex readline sqlite3 openldap libupnp \
ffmpeg-devel -gpl2
@@ -32,7 +32,7 @@ In order to enable generation of bundle for older MacOS version, it is recommend
Install `GTK`. It is recommended to use the `quartz` backend for better integration.
sudo port install gtk2 +quartz +no_x11
- sudo port install gtk-osx-application +no_python
+ sudo port install gtk-osx-application-gtk2 +no_python
sudo port install hicolor-icon-theme
#### Using HomeBrew
@@ -78,24 +78,24 @@ The next pieces need to be compiled manually.
* Install libvpx (Must be manualy build because the macport recipe does not support 'macosx_deployment_target')
- git clone https://chromium.googlesource.com/webm/libvpx -b v1.3.0
- cd libvpx
- ./configure --prefix=/opt/local \
- --target=x86_64-darwin10-gcc \
- --enable-error-concealment \
- --enable-multithread \
- --enable-realtime-only \
- --enable-spatial-resampling \
- --enable-vp8 \
- --disable-vp9 \
- --enable-libs \
- --disable-install-docs \
- --disable-debug-libs \
- --disable-examples \
- --disable-unit-tests \
- --as=yasm
- make
- sudo make install
+ git clone https://chromium.googlesource.com/webm/libvpx -b v1.4.0
+ cd libvpx
+ ./configure --prefix=/opt/local \
+ --target=x86_64-darwin10-gcc \
+ --enable-error-concealment \
+ --enable-multithread \
+ --enable-realtime-only \
+ --enable-spatial-resampling \
+ --enable-vp8 \
+ --disable-vp9 \
+ --enable-libs \
+ --disable-install-docs \
+ --disable-debug-libs \
+ --disable-examples \
+ --disable-unit-tests \
+ --as=yasm
+ make
+ sudo make install
* Install belle-sip (sip stack)
@@ -143,15 +143,12 @@ The next pieces need to be compiled manually.
### Generate portable bundle
-If you want to generate a portable bundle, then install `gtk-mac-bundler`:
+If you want to generate a portable bundle, then install `gtk-mac-bundler` linphone fork:
- git clone https://github.com/jralls/gtk-mac-bundler.git
- cd gtk-mac-bundler
- git checkout 6e2ed855aaeae43c29436c342ae83568573b5636
+ git clone git://git.linphone.org/gtk-mac-bundler.git
+ cd gtk-mac-bundler
make install
- export PATH=$PATH:~/.local/bin
- # make this dummy charset.alias file for the bundler to be happy:
- sudo touch /opt/local/lib/charset.alias
+ export PATH=$PATH:~/.local/bin
# set writing right for owner on the libssl and libcrypto libraries in order gtk-mac-bundler
# be able to rewrite their rpath
sudo chmod u+w /opt/local/lib/libssl.1.0.0.dylib /opt/local/lib/libcrypto.1.0.0.dylib
@@ -180,11 +177,11 @@ The resulting bundle is located in Linphone build directory, together with a zip
* For a better appearance, you can install `gtk-quartz-engine` (a GTK theme) that makes GTK application more similar to other Mac applications (but not perfect).
sudo port install gnome-common
- git clone https://github.com/jralls/gtk-quartz-engine.git
- cd gtk-quartz-engine
- ./autogen.sh
- ./configure --prefix=/opt/local CFLAGS="$CFLAGS -Wno-error" && make
- sudo make install
+ git clone https://github.com/jralls/gtk-quartz-engine.git
+ cd gtk-quartz-engine
+ ./autogen.sh
+ ./configure --prefix=/opt/local CFLAGS="$CFLAGS -Wno-error" && make
+ sudo make install
Generate a new bundle to have it included.
diff --git a/build/Makefile.am b/build/Makefile.am
index 84ac3e909..8145474f1 100644
--- a/build/Makefile.am
+++ b/build/Makefile.am
@@ -1,2 +1,4 @@
SUBDIRS=macos
+EXTRA_DIST = openembedded
+
diff --git a/build/android/Android.mk b/build/android/Android.mk
index 9dfd049fa..705ddc2ab 100755
--- a/build/android/Android.mk
+++ b/build/android/Android.mk
@@ -45,13 +45,16 @@ LOCAL_SRC_FILES := \
callbacks.c \
call_log.c \
call_params.c \
+ carddav.c \
chat.c \
- conference.c \
+ chat_file_transfer.c \
+ conference.cc \
content.c \
ec-calibrator.c \
enum.c \
event.c \
friend.c \
+ friendlist.c \
info.c \
linphonecall.c \
linphonecore.c \
@@ -75,13 +78,15 @@ LOCAL_SRC_FILES := \
xml2lpc.c \
xml.c \
xmlrpc.c \
- vtables.c
+ vtables.c \
+ ringtoneplayer.c
ifndef LIBLINPHONE_VERSION
LIBLINPHONE_VERSION = "Devel"
endif
LOCAL_CFLAGS += \
+ -Wno-error=deprecated-declarations \
-D_BYTE_ORDER=_LITTLE_ENDIAN \
-DORTP_INET6 \
-DINET6 \
@@ -119,11 +124,12 @@ LOCAL_C_INCLUDES += \
$(LOCAL_PATH)/../oRTP/include \
$(LOCAL_PATH)/../mediastreamer2/include \
$(LOCAL_PATH)/../mediastreamer2/src/audiofilters/ \
+ $(LOCAL_PATH)/../../bctoolbox/include \
$(LOCAL_PATH)/../../belle-sip/include \
$(LOCAL_PATH)/../../../gen \
$(LOCAL_PATH)/../../externals/libxml2/include \
$(LOCAL_PATH)/../../externals/build/libxml2 \
- $(LOCAL_PATH)/../../externals/polarssl/include
+ $(LOCAL_PATH)/../../externals/polarssl/include \
LOCAL_LDLIBS += -llog -ldl -lz
@@ -132,6 +138,7 @@ LOCAL_STATIC_LIBRARIES := \
libmediastreamer2 \
libortp \
libbellesip \
+ libbctoolbox \
libgsm \
liblpxml2
@@ -178,33 +185,52 @@ LOCAL_CFLAGS += -DHAVE_CODEC2
LOCAL_STATIC_LIBRARIES += libcodec2 libmscodec2
endif
-ifneq ($(BUILD_WEBRTC_AECM)$(BUILD_WEBRTC_ISAC),00)
+ifneq ($(BUILD_WEBRTC_AECM)$(BUILD_WEBRTC_ISAC)$(BUILD_ILBC),000)
LOCAL_CFLAGS += -DHAVE_WEBRTC
LOCAL_STATIC_LIBRARIES += libmswebrtc
endif
+
ifneq ($(BUILD_WEBRTC_AECM),0)
LOCAL_STATIC_LIBRARIES += \
- libwebrtc_aecm \
+ libwebrtc_aecm
+ifeq ($(TARGET_ARCH_ABI), armeabi-v7a)
+LOCAL_STATIC_LIBRARIES += \
+ libwebrtc_aecm_neon
+endif
+endif
+
+
+ifneq ($(BUILD_WEBRTC_ISAC),0)
+LOCAL_STATIC_LIBRARIES += \
+ libwebrtc_isacfix
+ifeq ($(TARGET_ARCH_ABI), armeabi-v7a)
+LOCAL_STATIC_LIBRARIES += \
+ libwebrtc_isacfix_neon
+endif
+endif
+
+ifneq ($(BUILD_ILBC),0)
+LOCAL_STATIC_LIBRARIES += \
+ libwebrtc_ilbc
+endif
+
+
+ifneq ($(BUILD_WEBRTC_AECM)$(BUILD_WEBRTC_ISAC)$(BUILD_ILBC),000)
+
+LOCAL_STATIC_LIBRARIES += \
+ libwebrtc_apm_utility \
+ libwebrtc_system_wrappers \
libwebrtc_apm_utility \
libwebrtc_spl \
libwebrtc_system_wrappers
ifeq ($(TARGET_ARCH_ABI), armeabi-v7a)
LOCAL_STATIC_LIBRARIES += \
- libwebrtc_aecm_neon \
- libwebrtc_spl_neon
-endif
-endif
-ifneq ($(BUILD_WEBRTC_ISAC),0)
-LOCAL_STATIC_LIBRARIES += \
- libwebrtc_isacfix \
- libwebrtc_spl
-ifeq ($(TARGET_ARCH_ABI), armeabi-v7a)
-LOCAL_STATIC_LIBRARIES += \
- libwebrtc_isacfix_neon \
libwebrtc_spl_neon
endif
+
endif
+
ifeq ($(BUILD_G729),1)
LOCAL_CFLAGS += -DHAVE_G729
LOCAL_STATIC_LIBRARIES += libbcg729 libmsbcg729
@@ -236,6 +262,10 @@ ifeq ($(BUILD_SRTP), 1)
LOCAL_C_INCLUDES += $(SRTP_C_INCLUDE)
endif
+ifeq ($(BUILD_VCARD),1)
+ LOCAL_C_INCLUDES += $(VCARD_C_INCLUDE)
+endif
+
ifeq ($(BUILD_ILBC), 1)
ifneq ($(TARGET_ARCH_ABI),armeabi)
LOCAL_CFLAGS += -DHAVE_ILBC=1
@@ -257,8 +287,16 @@ ifeq ($(BUILD_SRTP),1)
LOCAL_STATIC_LIBRARIES += libsrtp
endif
+ifeq ($(BUILD_VCARD),1)
+ LOCAL_CFLAGS += -DVCARD_ENABLED
+ LOCAL_SRC_FILES += vcard.cc
+ LOCAL_STATIC_LIBRARIES += libbelr libbelcard
+else
+ LOCAL_SRC_FILES += vcard_stubs.c
+endif
+
ifeq ($(BUILD_SQLITE),1)
-LOCAL_CFLAGS += -DMSG_STORAGE_ENABLED
+LOCAL_CFLAGS += -DMSG_STORAGE_ENABLED -DCALL_LOGS_STORAGE_ENABLED -DFRIENDS_SQL_STORAGE_ENABLED
LOCAL_STATIC_LIBRARIES += liblinsqlite
LOCAL_C_INCLUDES += \
$(LOCAL_PATH)/../../externals/sqlite3/
@@ -280,8 +318,9 @@ LOCAL_MODULE_FILENAME := liblinphone-$(TARGET_ARCH_ABI)
include $(BUILD_SHARED_LIBRARY)
-LOCAL_CPPFLAGS=$(LOCAL_CFLAGS)
+LOCAL_CPPFLAGS += $(LOCAL_CFLAGS)
LOCAL_CFLAGS += -Wdeclaration-after-statement
+LOCAL_LDFLAGS := -Wl,-soname,$(LOCAL_MODULE_FILENAME).so
$(call import-module,android/cpufeatures)
diff --git a/build/android/liblinphone_tester.mk b/build/android/liblinphone_tester.mk
index 8395df144..597e412fa 100644
--- a/build/android/liblinphone_tester.mk
+++ b/build/android/liblinphone_tester.mk
@@ -1,7 +1,6 @@
LOCAL_PATH := $(call my-dir)/../../tester
common_SRC_FILES := \
- common/bc_tester_utils.c \
accountmanager.c \
call_tester.c \
dtmf_tester.c \
@@ -24,14 +23,15 @@ common_SRC_FILES := \
tunnel_tester.c \
upnp_tester.c \
multicast_call_tester.c \
+ vcard_tester.c \
+ complex_sip_call_tester.c \
common_C_INCLUDES += \
$(LOCAL_PATH) \
$(LOCAL_PATH)/../include \
$(LOCAL_PATH)/../coreapi \
$(LOCAL_PATH)/../oRTP/include \
- $(LOCAL_PATH)/../mediastreamer2/include \
- $(LOCAL_PATH)/common
+ $(LOCAL_PATH)/../mediastreamer2/include
include $(CLEAR_VARS)
@@ -47,7 +47,9 @@ ifeq ($(BUILD_MATROSKA), 1)
LOCAL_CFLAGS += -DHAVE_MATROSKA -DHAVE_ZLIB
endif
-LOCAL_SHARED_LIBRARIES := cunit liblinphone
+LOCAL_STATIC_LIBRARIES := bctoolbox_tester
+
+LOCAL_SHARED_LIBRARIES := bcunit liblinphone
include $(BUILD_SHARED_LIBRARY)
#end
diff --git a/build/macos/environment.sh b/build/macos/environment.sh
index fae264e0e..879db36dc 100644
--- a/build/macos/environment.sh
+++ b/build/macos/environment.sh
@@ -3,6 +3,7 @@ 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)
@@ -23,4 +24,4 @@ esac
export LANG
-echo "LANG is $LANG"
\ No newline at end of file
+echo "LANG is $LANG"
diff --git a/build/macos/linphone.bundle b/build/macos/linphone.bundle
index 8109ff6cb..19793fe78 100644
--- a/build/macos/linphone.bundle
+++ b/build/macos/linphone.bundle
@@ -17,6 +17,7 @@
${env:MS2_PLUGINS_INSTALL_PREFIX}
${env:LINPHONE_ADDITIONAL_DEPENDENCIES_PREFIX}
+
-
- ${prefix}/lib/gio/modules/libgiognutls.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:linphone}/share/locale
-
-
- ${prefix}/share/locale
-
-
- ${prefix}/share/locale
-
${prefix}/share/locale
+
+ ${prefix:linphone}/share/locale
+
+
+
+ ${prefix}/share/locale
+
+
+
+ ${prefix}/share/locale
+
@@ -151,7 +175,6 @@
${project}/../../pixmaps/linphone.icns
-
${project}/environment.sh
@@ -164,14 +187,18 @@
${project}/../../gtk/gtkrc.mac
-
- ${prefix:linphone}/share/sounds/linphone/rings/oldphone.wav
+
+ ${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
diff --git a/build/macos/quartz-theme-gtkrc.patch b/build/macos/quartz-theme-gtkrc.patch
index 9099f2cac..ac7a48a98 100644
--- a/build/macos/quartz-theme-gtkrc.patch
+++ b/build/macos/quartz-theme-gtkrc.patch
@@ -1,4 +1,20 @@
-85c85
-< buttontype = "textured"
----
-> buttontype = "aqua"
+--- /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"
+ }
+ }
+
diff --git a/build/openembedded/README b/build/openembedded/README
new file mode 100644
index 000000000..ec505a9c3
--- /dev/null
+++ b/build/openembedded/README
@@ -0,0 +1,57 @@
+Recipes for open embedded: http://www.openembedded.org
+
+Documentations:
+http://docs.openembedded.org/usermanual/
+http://bitbake.berlios.de/manual/
+
+
+
+Instructions for compilation from sources: (requires 10 Go of free space)
+- Choose a distribution and build it.
+ For example, to build Angstrom follow the guide at http://www.angstrom-distribution.org/building-angstrom
+ For IGEPv2 use environment variable MACHINE=igep0020
+ It is possible to use MACHINE=qemuarm to build an image that can be run on a computer with qemu.
+
+- Add linphone recipes to the pool with an higher priority:
+ Edit conf/bblayers.conf to set EXTRALAYERS to point to the source repository of linphone. Search the EXTRALAYERS definition in conf/bblayers.conf
+ and modify it like this:
+
+ # Add your overlay location to EXTRALAYERS
+ # Make sure to have a conf/layers.conf in there
+ EXTRALAYERS ?= "/home/smorlat/sources/git/linphone-daemon/build/openembedded"
+
+ This additional layer gives access to the various linphone recipes but also to a recipe to build an entire image containing linphone.
+ To build this image based on the generic console image you will need to use:
+ bitbake console-linphone-image
+
+
+- Prepare compilation
+ Source appropriate environment with "~/.oe/enviro*"
+ Change directory to where you launched Angstrom install script.
+
+- Compile linphone
+ bitbake -c clean linphone
+ bitbake linphone
+
+- If you want additional codecs (e.g. iLBC or AMR) compile linphone-plugins
+ bitbake -c clean linphone-plugins
+ bitbake linphone-plugins
+
+- Find the generated packages "*.ipk"
+ Example: /Data/work/angstrom/angstrom-setup-scripts/build/tmp-angstrom_2008_1/deploy/glibc/ipk/armv7a/
+
+
+
+
+Installation
+- check network connectivity
+ * ping linphone.org
+ * see "route -n"
+ * see "/etc/resolv.conf"
+- update package list
+ * opkg update
+- copy ipk files to install to /tmp
+- eventually remove previously installed packages
+- install with "opkg install libortp*.ipk libmediastreamer*.ipk liblinphone*.ipk linphonec*.ipk"
+
+
diff --git a/build/openembedded/antlr3/antlr3c.inc b/build/openembedded/antlr3/antlr3c.inc
new file mode 100644
index 000000000..e13b73088
--- /dev/null
+++ b/build/openembedded/antlr3/antlr3c.inc
@@ -0,0 +1,15 @@
+DESCRIPTION = "Linphone version of antlr3"
+LICENSE = "GPL"
+
+PROVIDES = "antlr3c antlr3c-dev"
+ALLOW_EMPTY_${PN} = "1"
+
+S = "${WORKDIR}/git/runtime/C"
+
+inherit autotools pkgconfig lib_package
+
+do_fetch_append() {
+ import os
+ os.system("autogen.sh")
+}
+
diff --git a/build/openembedded/antlr3/antlr3c_linphone.bb b/build/openembedded/antlr3/antlr3c_linphone.bb
new file mode 100644
index 000000000..1c090ffab
--- /dev/null
+++ b/build/openembedded/antlr3/antlr3c_linphone.bb
@@ -0,0 +1,7 @@
+require antlr3c.inc
+
+SRCREV="f0dbcbbcd22a7fd9a479ff68d4daa9225fb2f3b1"
+PR="R3"
+SRC_URI = "git://git.linphone.org/antlr3.git"
+
+LIC_FILES_CHKSUM= "file://COPYING;md5=13c502aaa9b2ca91d01a3aae44d899b4"
diff --git a/build/openembedded/belle-sip/belle-sip.inc b/build/openembedded/belle-sip/belle-sip.inc
new file mode 100644
index 000000000..a57e4e41a
--- /dev/null
+++ b/build/openembedded/belle-sip/belle-sip.inc
@@ -0,0 +1,15 @@
+DESCRIPTION = "SIP stack from Belledonne Communications"
+LICENSE = "GPL"
+
+DEPENDS_${PN} = "polarssl-dev antlr3c-dev"
+DEPENDS = "polarssl-dev antlr3c-dev"
+RDEPENDS_${PN} = "polarssl-dev antlr3c-dev"
+
+EXTRA_OECONF += "--disable-strict --with-antlr=${STAGING_DIR_HOST}${layout_exec_prefix}/usr --with-polarssl=${STAGING_DIR_HOST}${layout_exec_prefix}/usr"
+INSANE_SKIP_belle-sip += "dev-deps"
+
+inherit autotools pkgconfig
+
+do_autoreconf () {
+ ./autogen.sh
+}
diff --git a/build/openembedded/belle-sip/belle-sip_master.bb b/build/openembedded/belle-sip/belle-sip_master.bb
new file mode 100644
index 000000000..f0f843543
--- /dev/null
+++ b/build/openembedded/belle-sip/belle-sip_master.bb
@@ -0,0 +1,10 @@
+require belle-sip.inc
+
+SRCREV="af93922ac91cf3cbf5ceed0328bf43d08d37714e"
+S = "${WORKDIR}/git"
+
+PR="R1"
+
+
+SRC_URI = "git://git.linphone.org/belle-sip.git;commit=${SRCREV}"
+LIC_FILES_CHKSUM = "file://COPYING;md5=9f9938e31db89d55a796e86808c96848"
diff --git a/build/openembedded/conf/layer.conf b/build/openembedded/conf/layer.conf
new file mode 100644
index 000000000..1b29844a8
--- /dev/null
+++ b/build/openembedded/conf/layer.conf
@@ -0,0 +1,9 @@
+# We have a conf and classes directory, append to BBPATH
+BBPATH .= ":${LAYERDIR}"
+
+# We have a recipes directory, add to BBFILES
+BBFILES += "${LAYERDIR}/*.bb ${LAYERDIR}/*/*.bb"
+
+BBFILE_COLLECTIONS += "linphone-layer"
+BBFILE_PATTERN_linphone-layer := "^${LAYERDIR}/"
+BBFILE_PRIORITY_linphone-layer = "50"
diff --git a/build/openembedded/files/igep0020/alsa_8khz.patch b/build/openembedded/files/igep0020/alsa_8khz.patch
new file mode 100644
index 000000000..17774d4e6
--- /dev/null
+++ b/build/openembedded/files/igep0020/alsa_8khz.patch
@@ -0,0 +1,13 @@
+--- linphone/mediastreamer2/src/alsa.c_orig 2011-05-24 12:39:33.824600109 +0200
++++ linphone/mediastreamer2/src/alsa.c 2011-05-24 12:40:04.760407404 +0200
+@@ -32,8 +32,8 @@
+ /*in case of troubles with a particular driver, try incrementing ALSA_PERIOD_SIZE
+ to 512, 1024, 2048, 4096...
+ then try incrementing the number of periods*/
+-#define ALSA_PERIODS 8
+-#define ALSA_PERIOD_SIZE 256
++#define ALSA_PERIODS 4
++#define ALSA_PERIOD_SIZE 512
+
+ /*uncomment the following line if you have problems with an alsa driver
+ having sound quality trouble:*/
diff --git a/build/openembedded/libgsm/libgsm-1.0.13/01_makefile.patch b/build/openembedded/libgsm/libgsm-1.0.13/01_makefile.patch
new file mode 100644
index 000000000..947db37bf
--- /dev/null
+++ b/build/openembedded/libgsm/libgsm-1.0.13/01_makefile.patch
@@ -0,0 +1,71 @@
+diff -urNad libgsm-1.0.12~/Makefile libgsm-1.0.12/Makefile
+--- libgsm-1.0.12~/Makefile 2007-11-01 15:37:52.000000000 +0100
++++ libgsm-1.0.12/Makefile 2007-11-01 15:43:06.000000000 +0100
+@@ -96,7 +96,7 @@
+ # Other tools
+
+ SHELL = /bin/sh
+-LN = ln
++LN = ln -s
+ BASENAME = basename
+ AR = ar
+ ARFLAGS = cr
+@@ -140,6 +140,7 @@
+ # Targets
+
+ LIBGSM = $(LIB)/libgsm.a
++LIBGSMSO= $(LIB)/libgsm.so
+
+ TOAST = $(BIN)/toast
+ UNTOAST = $(BIN)/untoast
+@@ -279,7 +280,7 @@
+
+ # Target rules
+
+-all: $(LIBGSM) $(TOAST) $(TCAT) $(UNTOAST)
++all: $(LIBGSM) $(LIBGSMSO) $(TOAST) $(TCAT) $(UNTOAST)
+ @-echo $(ROOT): Done.
+
+ tst: $(TST)/lin2cod $(TST)/cod2lin $(TOAST) $(TST)/test-result
+@@ -299,6 +300,11 @@
+
+ # The basic API: libgsm
+
++$(LIBGSMSO): $(LIB) $(GSM_OBJECTS)
++ $(LD) -o $@.1.0.12 -shared -Xlinker -soname -Xlinker libgsm.so.1 $(GSM_OBJECTS) -lc $(LDFLAGS)
++ ln -fs libgsm.so.1.0.12 lib/libgsm.so.1
++ ln -fs libgsm.so.1.0.12 lib/libgsm.so
++
+ $(LIBGSM): $(LIB) $(GSM_OBJECTS)
+ -rm $(RMFLAGS) $(LIBGSM)
+ $(AR) $(ARFLAGS) $(LIBGSM) $(GSM_OBJECTS)
+@@ -308,15 +314,15 @@
+ # Toast, Untoast and Tcat -- the compress-like frontends to gsm.
+
+ $(TOAST): $(BIN) $(TOAST_OBJECTS) $(LIBGSM)
+- $(LD) $(LFLAGS) -o $(TOAST) $(TOAST_OBJECTS) $(LIBGSM) $(LDLIB)
++ $(LD) $(LFLAGS) -o $(TOAST) $(TOAST_OBJECTS) $(LIBGSMSO) $(LDLIB)
+
+ $(UNTOAST): $(BIN) $(TOAST)
+ -rm $(RMFLAGS) $(UNTOAST)
+- $(LN) $(TOAST) $(UNTOAST)
++ $(LN) toast $(UNTOAST)
+
+ $(TCAT): $(BIN) $(TOAST)
+ -rm $(RMFLAGS) $(TCAT)
+- $(LN) $(TOAST) $(TCAT)
++ $(LN) toast $(TCAT)
+
+
+ # The local bin and lib directories
+@@ -426,7 +432,9 @@
+
+ clean: semi-clean
+ -rm $(RMFLAGS) $(LIBGSM) $(ADDTST)/add \
+- $(TOAST) $(TCAT) $(UNTOAST) \
++ $(LIBGSMSO) $(LIB)/libgsm.so.1.0.12 \
++ $(LIB)libgsm.so.1 \
++ $(TOAST) $(TCAT) $(UNTOAST) \
+ $(ROOT)/gsm-1.0.tar.Z
+
+
diff --git a/build/openembedded/libgsm/libgsm-1.0.13/02_cplusplus.patch b/build/openembedded/libgsm/libgsm-1.0.13/02_cplusplus.patch
new file mode 100644
index 000000000..a4bbb4067
--- /dev/null
+++ b/build/openembedded/libgsm/libgsm-1.0.13/02_cplusplus.patch
@@ -0,0 +1,25 @@
+diff -urNad libgsm-1.0.10~/inc/gsm.h libgsm-1.0.10/inc/gsm.h
+--- libgsm-1.0.10~/inc/gsm.h 2007-11-01 15:37:52.000000000 +0100
++++ libgsm-1.0.10/inc/gsm.h 2007-11-01 15:44:52.000000000 +0100
+@@ -54,6 +54,10 @@
+ #define GSM_OPT_FRAME_INDEX 5
+ #define GSM_OPT_FRAME_CHAIN 6
+
++#ifdef __cplusplus
++extern "C" {
++#endif
++
+ extern gsm gsm_create GSM_P((void));
+ extern void gsm_destroy GSM_P((gsm));
+
+@@ -66,6 +70,10 @@
+ extern int gsm_explode GSM_P((gsm, gsm_byte *, gsm_signal *));
+ extern void gsm_implode GSM_P((gsm, gsm_signal *, gsm_byte *));
+
++#ifdef __cplusplus
++}
++#endif
++
+ #undef GSM_P
+
+ #endif /* GSM_H */
diff --git a/build/openembedded/libgsm/libgsm-1.0.13/03_config.patch b/build/openembedded/libgsm/libgsm-1.0.13/03_config.patch
new file mode 100644
index 000000000..dad241e2b
--- /dev/null
+++ b/build/openembedded/libgsm/libgsm-1.0.13/03_config.patch
@@ -0,0 +1,154 @@
+diff -urNad libgsm-1.0.10~/Makefile libgsm-1.0.10/Makefile
+--- libgsm-1.0.10~/Makefile 2007-11-01 15:37:52.000000000 +0100
++++ libgsm-1.0.10/Makefile 2007-11-01 15:48:02.000000000 +0100
+@@ -151,7 +151,7 @@
+
+ HEADERS = $(INC)/proto.h \
+ $(INC)/unproto.h \
+- $(INC)/config.h \
++ $(INC)/gsm_config.h \
+ $(INC)/private.h \
+ $(INC)/gsm.h \
+ $(INC)/toast.h \
+diff -urNad libgsm-1.0.10~/inc/config.h libgsm-1.0.10/inc/config.h
+--- libgsm-1.0.10~/inc/config.h 2007-11-01 15:37:52.000000000 +0100
++++ libgsm-1.0.10/inc/config.h 1970-01-01 01:00:00.000000000 +0100
+@@ -1,37 +0,0 @@
+-/*
+- * Copyright 1992 by Jutta Degener and Carsten Bormann, Technische
+- * Universitaet Berlin. See the accompanying file "COPYRIGHT" for
+- * details. THERE IS ABSOLUTELY NO WARRANTY FOR THIS SOFTWARE.
+- */
+-
+-/*$Header: /tmp_amd/presto/export/kbs/jutta/src/gsm/RCS/config.h,v 1.5 1996/07/02 11:26:20 jutta Exp $*/
+-
+-#ifndef CONFIG_H
+-#define CONFIG_H
+-
+-/*efine SIGHANDLER_T int /* signal handlers are void */
+-/*efine HAS_SYSV_SIGNAL 1 /* sigs not blocked/reset? */
+-
+-#define HAS_STDLIB_H 1 /* /usr/include/stdlib.h */
+-#define HAS_LIMITS_H 1 /* /usr/include/limits.h */
+-#define HAS_FCNTL_H 1 /* /usr/include/fcntl.h */
+-#define HAS_ERRNO_DECL 1 /* errno.h declares errno */
+-
+-#define HAS_FSTAT 1 /* fstat syscall */
+-#define HAS_FCHMOD 1 /* fchmod syscall */
+-#define HAS_CHMOD 1 /* chmod syscall */
+-#define HAS_FCHOWN 1 /* fchown syscall */
+-#define HAS_CHOWN 1 /* chown syscall */
+-/*efine HAS__FSETMODE 1 /* _fsetmode -- set file mode */
+-
+-#define HAS_STRING_H 1 /* /usr/include/string.h */
+-/*efine HAS_STRINGS_H 1 /* /usr/include/strings.h */
+-
+-#define HAS_UNISTD_H 1 /* /usr/include/unistd.h */
+-#define HAS_UTIME 1 /* POSIX utime(path, times) */
+-/*efine HAS_UTIMES 1 /* use utimes() syscall instead */
+-#define HAS_UTIME_H 1 /* UTIME header file */
+-#define HAS_UTIMBUF 1 /* struct utimbuf */
+-/*efine HAS_UTIMEUSEC 1 /* microseconds in utimbuf? */
+-
+-#endif /* CONFIG_H */
+diff -urNad libgsm-1.0.10~/inc/gsm_config.h libgsm-1.0.10/inc/gsm_config.h
+--- libgsm-1.0.10~/inc/gsm_config.h 1970-01-01 01:00:00.000000000 +0100
++++ libgsm-1.0.10/inc/gsm_config.h 2007-11-01 15:46:19.000000000 +0100
+@@ -0,0 +1,37 @@
++/*
++ * Copyright 1992 by Jutta Degener and Carsten Bormann, Technische
++ * Universitaet Berlin. See the accompanying file "COPYRIGHT" for
++ * details. THERE IS ABSOLUTELY NO WARRANTY FOR THIS SOFTWARE.
++ */
++
++/*$Header: /tmp_amd/presto/export/kbs/jutta/src/gsm/RCS/config.h,v 1.5 1996/07/02 11:26:20 jutta Exp $*/
++
++#ifndef CONFIG_H
++#define CONFIG_H
++
++/*efine SIGHANDLER_T int -* signal handlers are void */
++/*efine HAS_SYSV_SIGNAL 1 -* sigs not blocked/reset? */
++
++#define HAS_STDLIB_H 1 /* /usr/include/stdlib.h */
++#define HAS_STDIO_H 1 /* /usr/include/stdio.h */
++/*efine HAS_LIMITS_H 1 -* /usr/include/limits.h */
++#define HAS_FCNTL_H 1 /* /usr/include/fcntl.h */
++
++#define HAS_FSTAT 1 /* fstat syscall */
++#define HAS_FCHMOD 1 /* fchmod syscall */
++#define HAS_CHMOD 1 /* chmod syscall */
++#define HAS_FCHOWN 1 /* fchown syscall */
++#define HAS_CHOWN 1 /* chown syscall */
++/*efine HAS__FSETMODE 1 -* _fsetmode -- set file mode */
++
++#define HAS_STRING_H 1 /* /usr/include/string.h */
++/*efine HAS_STRINGS_H 1 -* /usr/include/strings.h */
++
++#define HAS_UNISTD_H 1 /* /usr/include/unistd.h */
++#define HAS_UTIME 1 /* POSIX utime(path, times) */
++/*efine HAS_UTIMES 1 -* use utimes() syscall instead */
++#define HAS_UTIME_H 1 /* UTIME header file */
++/*efine HAS_UTIMBUF 1 -* struct utimbuf */
++/*efine HAS_UTIMEUSEC 1 -* microseconds in utimbuf? */
++
++#endif /* CONFIG_H */
+diff -urNad libgsm-1.0.10~/inc/toast.h libgsm-1.0.10/inc/toast.h
+--- libgsm-1.0.10~/inc/toast.h 2007-11-01 15:37:52.000000000 +0100
++++ libgsm-1.0.10/inc/toast.h 2007-11-01 15:48:17.000000000 +0100
+@@ -9,7 +9,7 @@
+ #ifndef TOAST_H
+ #define TOAST_H /* Guard against multiple includes */
+
+-#include "config.h"
++#include "gsm_config.h"
+
+ #include
+ #include
+diff -urNad libgsm-1.0.10~/src/code.c libgsm-1.0.10/src/code.c
+--- libgsm-1.0.10~/src/code.c 2007-11-01 15:37:52.000000000 +0100
++++ libgsm-1.0.10/src/code.c 2007-11-01 15:48:34.000000000 +0100
+@@ -6,7 +6,7 @@
+
+ /* $Header: /tmp_amd/presto/export/kbs/jutta/src/gsm/RCS/code.c,v 1.3 1996/07/02 09:59:05 jutta Exp $ */
+
+-#include "config.h"
++#include "gsm_config.h"
+
+
+ #ifdef HAS_STDLIB_H
+diff -urNad libgsm-1.0.10~/src/gsm_create.c libgsm-1.0.10/src/gsm_create.c
+--- libgsm-1.0.10~/src/gsm_create.c 1996-07-02 16:32:44.000000000 +0200
++++ libgsm-1.0.10/src/gsm_create.c 2007-11-01 15:48:48.000000000 +0100
+@@ -6,7 +6,7 @@
+
+ static char const ident[] = "$Header: /tmp_amd/presto/export/kbs/jutta/src/gsm/RCS/gsm_create.c,v 1.4 1996/07/02 09:59:05 jutta Exp $";
+
+-#include "config.h"
++#include "gsm_config.h"
+
+ #ifdef HAS_STRING_H
+ #include
+diff -urNad libgsm-1.0.10~/src/gsm_destroy.c libgsm-1.0.10/src/gsm_destroy.c
+--- libgsm-1.0.10~/src/gsm_destroy.c 1996-07-02 16:32:39.000000000 +0200
++++ libgsm-1.0.10/src/gsm_destroy.c 2007-11-01 15:48:57.000000000 +0100
+@@ -7,7 +7,7 @@
+ /* $Header: /tmp_amd/presto/export/kbs/jutta/src/gsm/RCS/gsm_destroy.c,v 1.3 1994/11/28 19:52:25 jutta Exp $ */
+
+ #include "gsm.h"
+-#include "config.h"
++#include "gsm_config.h"
+ #include "proto.h"
+
+ #ifdef HAS_STDLIB_H
+diff -urNad libgsm-1.0.10~/tls/taste.c libgsm-1.0.10/tls/taste.c
+--- libgsm-1.0.10~/tls/taste.c 1996-07-02 16:33:05.000000000 +0200
++++ libgsm-1.0.10/tls/taste.c 2007-11-01 15:49:54.000000000 +0100
+@@ -10,7 +10,7 @@
+ #include
+ #include
+
+-#include "config.h"
++#include "gsm_config.h"
+
+ #ifdef HAS_STDLIB_H
+ # include
diff --git a/build/openembedded/libgsm/libgsm-1.0.13/04_includes.patch b/build/openembedded/libgsm/libgsm-1.0.13/04_includes.patch
new file mode 100644
index 000000000..2769b40b0
--- /dev/null
+++ b/build/openembedded/libgsm/libgsm-1.0.13/04_includes.patch
@@ -0,0 +1,43 @@
+diff -urNad libgsm-1.0.10~/inc/toast.h libgsm-1.0.10/inc/toast.h
+--- libgsm-1.0.10~/inc/toast.h 2007-11-01 15:37:52.000000000 +0100
++++ libgsm-1.0.10/inc/toast.h 2007-11-01 15:52:33.000000000 +0100
+@@ -16,11 +16,12 @@
+
+ #include
+ #include
++#include
+ #include
+
+ #include
+-#ifndef HAS_ERRNO_DECL
+- extern int errno;
++#ifndef errno
++ extern int errno;
+ #endif
+
+ #ifdef HAS_LIMITS_H
+@@ -37,6 +38,10 @@
+ # endif
+ #endif
+
++#ifdef HAS_STDIO_H
++# include
++#endif
++
+ #include "gsm.h"
+
+ #ifndef S_ISREG
+diff -urNad libgsm-1.0.10~/src/code.c libgsm-1.0.10/src/code.c
+--- libgsm-1.0.10~/src/code.c 2007-11-01 15:37:52.000000000 +0100
++++ libgsm-1.0.10/src/code.c 2007-11-01 15:52:33.000000000 +0100
+@@ -9,8 +9,8 @@
+ #include "config.h"
+
+
+-#ifdef HAS_STDLIB_H
+-#include
++#ifdef HAS_STRING_H
++#include
+ #else
+ # include "proto.h"
+ extern char * memcpy P((char *, char *, int));
diff --git a/build/openembedded/libgsm/libgsm-1.0.13/05_compiler_warnings.patch b/build/openembedded/libgsm/libgsm-1.0.13/05_compiler_warnings.patch
new file mode 100644
index 000000000..c40100c92
--- /dev/null
+++ b/build/openembedded/libgsm/libgsm-1.0.13/05_compiler_warnings.patch
@@ -0,0 +1,98 @@
+diff -urNad libgsm-1.0.10~/src/debug.c libgsm-1.0.10/src/debug.c
+--- libgsm-1.0.10~/src/debug.c 2007-11-01 15:37:52.000000000 +0100
++++ libgsm-1.0.10/src/debug.c 2007-11-01 15:53:42.000000000 +0100
+@@ -49,7 +49,7 @@
+ fprintf( stderr, "%s [%d .. %d]: ", name, from, to );
+ while (from <= to) {
+
+- fprintf(stderr, "%d ", ptr[ from ] );
++ fprintf(stderr, "%ld ", ptr[ from ] );
+ from++;
+ if (nprinted++ >= 7) {
+ nprinted = 0;
+@@ -63,14 +63,14 @@
+ char * name,
+ longword value )
+ {
+- fprintf(stderr, "%s: %d\n", name, (long)value );
++ fprintf(stderr, "%s: %ld\n", name, (long)value );
+ }
+
+ void gsm_debug_word P2( (name, value),
+ char * name,
+ word value )
+ {
+- fprintf(stderr, "%s: %d\n", name, (long)value);
++ fprintf(stderr, "%s: %ld\n", name, (long)value);
+ }
+
+ #endif
+diff -urNad libgsm-1.0.10~/src/toast.c libgsm-1.0.10/src/toast.c
+--- libgsm-1.0.10~/src/toast.c 2007-11-01 15:37:52.000000000 +0100
++++ libgsm-1.0.10/src/toast.c 2007-11-01 15:53:42.000000000 +0100
+@@ -251,8 +251,8 @@
+ {
+ char * s;
+ if (!(s = malloc(len))) {
+- fprintf(stderr, "%s: failed to malloc %d bytes -- abort\n",
+- progname, len);
++ fprintf(stderr, "%s: failed to malloc %ld bytes -- abort\n",
++ progname, (long) len);
+ onintr();
+ exit(1);
+ }
+@@ -270,7 +270,7 @@
+ maxlen = strlen(name) + 1 + strlen(want) + strlen(cut);
+ p = strcpy(emalloc(maxlen), name);
+
+- if (s = suffix(p, cut)) strcpy(s, want);
++ if ((s = suffix(p, cut))) strcpy(s, want);
+ else if (*want && !suffix(p, want)) strcat(p, want);
+
+ return p;
+@@ -386,7 +386,7 @@
+ ut[0] = instat.st_atime;
+ ut[1] = instat.st_mtime;
+
+- (void) utime(outname, ut);
++ (void) utime(outname, (struct utimbuf *)ut);
+
+ #endif /* UTIMBUF */
+ }
+@@ -416,7 +416,7 @@
+ }
+ if (st->st_nlink > 1 && !f_cat && !f_precious) {
+ fprintf(stderr,
+- "%s: \"%s\" has %s other link%s -- unchanged.\n",
++ "%s: \"%s\" has %d other link%s -- unchanged.\n",
+ progname,name,st->st_nlink - 1,"s" + (st->st_nlink<=2));
+ return 0;
+ }
+@@ -585,8 +585,8 @@
+
+ if (cc != sizeof(s)) {
+ if (cc >= 0) fprintf(stderr,
+- "%s: incomplete frame (%d byte%s missing) from %s\n",
+- progname, sizeof(s) - cc,
++ "%s: incomplete frame (%ld byte%s missing) from %s\n",
++ progname, (long) sizeof(s) - cc,
+ "s" + (sizeof(s) - cc == 1),
+ inname ? inname : "stdin" );
+ gsm_destroy(r);
+@@ -624,8 +624,6 @@
+
+ static int process P1((name), char * name)
+ {
+- int step = 0;
+-
+ out = (FILE *)0;
+ in = (FILE *)0;
+
+@@ -779,7 +777,6 @@
+ case 'h': help(); exit(0);
+
+ default:
+- usage:
+ fprintf(stderr,
+ "Usage: %s [-fcpdhvuaslFC] [files...] (-h for help)\n",
+ progname);
diff --git a/build/openembedded/libgsm/libgsm.inc b/build/openembedded/libgsm/libgsm.inc
new file mode 100644
index 000000000..a731e81b8
--- /dev/null
+++ b/build/openembedded/libgsm/libgsm.inc
@@ -0,0 +1,34 @@
+DESCRIPTION = "GSM Audio Library"
+SECTION = "libs"
+PRIORITY = "optional"
+LICENSE = "libgsm"
+
+INC_PR = "r2"
+
+SRC_URI = "http://www.quut.com/gsm/gsm-${PV}.tar.gz \
+ file://01_makefile.patch \
+ file://02_cplusplus.patch \
+ file://03_config.patch \
+ file://04_includes.patch \
+ file://05_compiler_warnings.patch \
+ "
+
+CFLAGS += "-c -g -fPIC -Wall -D_GNU_SOURCE -D_REENTRANT -DNeedFunctionPrototypes=1 -DWAV49 -I./inc"
+
+PARALLEL_MAKE = ""
+
+do_compile() {
+ unset LD
+ oe_runmake CCFLAGS="${CFLAGS}"
+}
+
+do_install() {
+ oe_libinstall -a -C lib libgsm ${D}${libdir}
+ oe_libinstall -so -C lib libgsm ${D}${libdir}
+ install -d ${D}${includedir}/gsm
+ install -m 0644 ${S}/inc/gsm.h ${D}${includedir}/gsm/
+ cd ${D}${includedir}
+ ln -s gsm/gsm.h gsm.h
+}
+
+LIC_FILES_CHKSUM = "file://COPYRIGHT;md5=fc1372895b173aaf543a122db37e04f5"
\ No newline at end of file
diff --git a/build/openembedded/libgsm/libgsm_1.0.13.bb b/build/openembedded/libgsm/libgsm_1.0.13.bb
new file mode 100644
index 000000000..e6bf9baf6
--- /dev/null
+++ b/build/openembedded/libgsm/libgsm_1.0.13.bb
@@ -0,0 +1,8 @@
+require libgsm.inc
+
+PR = "${INC_PR}.0"
+
+S = "${WORKDIR}/gsm-1.0-pl13/"
+
+SRC_URI[md5sum] = "c1ba392ce61dc4aff1c29ea4e92f6df4"
+SRC_URI[sha256sum] = "52c518244d428c2e56c543b98c9135f4a76ff780c32455580b793f60a0a092ad"
diff --git a/build/openembedded/libilbc-rfc3951_git.bb b/build/openembedded/libilbc-rfc3951_git.bb
new file mode 100644
index 000000000..250976172
--- /dev/null
+++ b/build/openembedded/libilbc-rfc3951_git.bb
@@ -0,0 +1,13 @@
+DESCRIPTION = "iLBC codec as published in IETF RFC 3951"
+SECTION = "libs"
+PRIORITY = "optional"
+LICENSE = "LGPLv3"
+PR = "r1"
+
+SRC_URI = "git://git.linphone.org/libilbc-rfc3951.git;protocol=git"
+SRCREV = "b9490e0cbdda6a4ec29f7c47d81d3997004fedba"
+S = "${WORKDIR}/git"
+
+LIC_FILES_CHKSUM = "file://COPYING;md5=586c8a6efdeabd095cc4206ce4d0699b"
+
+inherit autotools pkgconfig
diff --git a/build/openembedded/linphone-plugins.bb b/build/openembedded/linphone-plugins.bb
new file mode 100644
index 000000000..4d31895cd
--- /dev/null
+++ b/build/openembedded/linphone-plugins.bb
@@ -0,0 +1,9 @@
+DESCRIPTION = "Plugins for linphone to have additional codecs."
+LICENSE = ""
+ALLOW_EMPTY_${PN} = "1"
+PACKAGES = "${PN}"
+DEPENDS_${PN} = "linphone msamr msilbc msx264"
+RDEPENDS_${PN} = "linphonec msamr msilbc msx264"
+
+LIC_FILES_CHKSUM = "file://${COREBASE}/LICENSE;md5=3f40d7994397109285ec7b81fdeb3b58 \
+ file://${COREBASE}/meta/COPYING.MIT;md5=3da9cfbcb788c80a0384361b4de20420"
diff --git a/build/openembedded/linphone/linphone-common.inc b/build/openembedded/linphone/linphone-common.inc
new file mode 100644
index 000000000..64309228a
--- /dev/null
+++ b/build/openembedded/linphone/linphone-common.inc
@@ -0,0 +1,66 @@
+SECTION = "x11/network"
+SECTION_liblinphone = "libs/network"
+SECTION_libmediastreamer = "libs/network"
+SECTION_libortp = "libs/network"
+SECTION_linphonec = "console/network"
+
+SRC_URI_append_igep0020 = " file://alsa_8khz.patch"
+
+DEPENDS_${PN} = "intltool-native speex alsa-lib spandsp belle-sip liblinphone libxv ffmpeg libv4l libgsm"
+DEPENDS_liblinphone = "libmediastreamer libortp"
+DEPENDS_libmediastreamer = "speex alsa-lib libortp"
+
+PROVIDES = "linphone linphonec liblinphone libmediastreamer libortp"
+
+inherit autotools pkgconfig gettext
+
+INSANE_SKIP_linphone += "dev-deps"
+INSANE_SKIP_liblinphone += "dev-deps"
+
+do_install_append(){
+ install -d ${D}${bindir}
+}
+
+EXTRA_OECONF = " \
+ --disable-tests \
+ --with-ffmpeg=${STAGING_DIR_HOST}${layout_exec_prefix} --enable-video --disable-vp8 \
+ --disable-glx \
+ --enable-alsa --disable-pulseaudio \
+ --without-readline \
+ --with-speex=${STAGING_DIR_HOST}${layout_exec_prefix} \
+ --disable-manual --enable-tests=yes \
+ --enable-console_ui=no \
+ --enable-gtk_ui=no \
+ --with-realprefix=/usr \
+ "
+
+EXTRA_OEMAKE = " V=1"
+
+PACKAGES = "${PN}-dbg ${PN}-dev ${PN}-doc ${PN}c ${PN}-common linphone-rings liblinphone libmediastreamer-bin libmediastreamer libortp ${PN}-utils ${PN}-tests"
+
+FILES_${PN}-common = "\
+ ${bindir}/lp-gen-wrappers \
+ ${datadir}/pixmaps \
+ ${datadir}/applications \
+ ${datadir}/gnome \
+ ${datadir}/tutorials \
+ ${datadir}/linphone \
+ ${datadir}/sounds/linphone/hello8000.wav \
+ ${datadir}/sounds/linphone/hello16000.wav \
+ ${datadir}/sounds/linphone/incoming_chat.wav \
+ ${datadir}/sounds/linphone/ringback.wav \
+ ${datadir}/images/nowebcamCIF.jpg \
+ ${datadir}/appdata/linphone.appdata.xml \
+ ${datadir}/icons \
+ "
+FILES_${PN}-tests = "${bindir}/xml2lpc_test ${bindir}/lpc2xml_test"
+FILES_${PN} = "${bindir}/linphone"
+FILES_${PN}c = "${bindir}/linphonec ${bindir}/linphone-daemon ${bindir}/linphone-daemon-pipetest ${bindir}/linphonecsh ${bindir}/sipomatic ${bindir}/auto_answer"
+FILES_${PN}-rings = "${datadir}/sounds/linphone/rings"
+FILES_liblinphone = "${libdir}/liblinphone.so.*"
+FILES_libmediastreamer-bin = "${bindir}/mediastream ${bindir}/msaudiocmp"
+FILES_libmediastreamer = "${libdir}/libmediastreamer_base.so.* ${libdir}/libmediastreamer_voip.so.* ${libdir}/mediastreamer/ ${libdir}/mediastreamer/plugins"
+FILES_libortp = "${libdir}/libortp.so.*"
+FILES_${PN}-dev += "${libdir}/*.a ${libdir}/*.la ${libdir}/pkgconfig ${includedir}"
+FILES_${PN}-utils = "${bindir}/test_ecc ${bindir}/test_lsd"
+FILES_${PN}-doc = "${docdir}/ortp-0.24.1 ${docdir}/mediastreamer-2.11.1 ${docdir}/linphone-3.8.1-linphone-daemon ${mandir}"
diff --git a/build/openembedded/linphone/linphone-common_git.inc b/build/openembedded/linphone/linphone-common_git.inc
new file mode 100644
index 000000000..3d92b7f46
--- /dev/null
+++ b/build/openembedded/linphone/linphone-common_git.inc
@@ -0,0 +1,37 @@
+
+SRCREV = "cfa356c8680d26eec970c7b54beea0717b91f2b0"
+L_GIT_SRC_URI = "gitosis@git.linphone.org:linphone-daemon"
+PR_append = "+gitr${SRCREV}"
+
+LINPHONE_TMP_DIR="/tmp/LINPHONE_TMP_${SRCREV}"
+SRC_URI = "file://${LINPHONE_TMP_DIR}/linphone.tar.gz"
+
+S = "${WORKDIR}/linphone"
+
+# bitbake git fetcher currently doesn't handle git submodules
+# There is also a problem with autogen and AC_SUBST
+# note: don't use a ssh key with password, it does not work.
+do_fetch_prepend () {
+ import os,bb
+ bb.note("Hack preparing clone in %s" %"${LINPHONE_TMP_DIR}")
+ os.system("rm -rf ${LINPHONE_TMP_DIR}")
+ os.system("mkdir -p ${LINPHONE_TMP_DIR}")
+
+ bb.note("Hack cloning linphone !recursively")
+ os.system("cd ${LINPHONE_TMP_DIR}; git clone --recursive ${L_GIT_SRC_URI} linphone")
+
+ bb.note("Hack launching autogen.sh manually")
+ os.system("cd ${LINPHONE_TMP_DIR}/linphone; ./autogen.sh")
+
+ bb.note("Hack preparing linphone.tar.gz")
+ # we need to keep the .git since the versioning in linphone is done through `git describe`
+ os.system("cd ${LINPHONE_TMP_DIR}; tar czf linphone.tar.gz linphone")
+}
+
+require linphone-common.inc
+
+
+#Required to avoid compile errors on May 2011.
+EXTRA_OECONF +=" --disable-strict"
+
+LIC_FILES_CHKSUM = "file://COPYING;md5=9f9938e31db89d55a796e86808c96848"
diff --git a/build/openembedded/linphone/linphone-common_local.inc b/build/openembedded/linphone/linphone-common_local.inc
new file mode 100644
index 000000000..a77b212e3
--- /dev/null
+++ b/build/openembedded/linphone/linphone-common_local.inc
@@ -0,0 +1,15 @@
+SRC_URI = "file://${HOME}/linphone-3.6.1.tar.gz"
+
+S = "${WORKDIR}/linphone-3.6.1"
+
+require linphone-common.inc
+
+do_configure_prepend () {
+ ./autogen.sh
+ libtoolize --copy --force
+}
+
+#Required to avoid compile errors on May 2011.
+EXTRA_OECONF +=" --disable-strict --disable-glx"
+
+LIC_FILES_CHKSUM = "file://COPYING;md5=94d55d512a9ba36caa9b7df079bae19f"
diff --git a/build/openembedded/linphone/linphone_+git-nogtk-gsm-video-x11.bb b/build/openembedded/linphone/linphone_+git-nogtk-gsm-video-x11.bb
new file mode 100644
index 000000000..0d55b7665
--- /dev/null
+++ b/build/openembedded/linphone/linphone_+git-nogtk-gsm-video-x11.bb
@@ -0,0 +1,16 @@
+## THIS unusable work in progress ##
+
+DESCRIPTION = "Audio/video SIP-based IP phone (console edition)"
+HOMEPAGE = "http://www.linphone.org/?lang=us"
+LICENSE = "GPLv2"
+PR="r0"
+
+DEFAULT_PREFERENCE = "3"
+OVERRIDES_append = ":console"
+OVERRIDES_append = ":gsm"
+OVERRIDES_append = ":video"
+OVERRIDES_append = ":x11"
+
+#PARALLEL_MAKE="V=1"
+
+require linphone-common_git.inc
diff --git a/build/openembedded/linphone/linphone_+git-nogtk-gsm-video.bb b/build/openembedded/linphone/linphone_+git-nogtk-gsm-video.bb
new file mode 100644
index 000000000..ae1120632
--- /dev/null
+++ b/build/openembedded/linphone/linphone_+git-nogtk-gsm-video.bb
@@ -0,0 +1,15 @@
+## THIS unusable work in progress ##
+
+DESCRIPTION = "Audio/video SIP-based IP phone (console edition)"
+HOMEPAGE = "http://www.linphone.org/?lang=us"
+LICENSE = "GPLv2"
+PR="r14"
+
+DEFAULT_PREFERENCE = "3"
+OVERRIDES_append = ":console"
+OVERRIDES_append = ":gsm"
+OVERRIDES_append = ":video"
+
+#PARALLEL_MAKE="V=1"
+
+require linphone-common_git.inc
diff --git a/build/openembedded/linphone/linphone_+git-nogtk-gsm.bb b/build/openembedded/linphone/linphone_+git-nogtk-gsm.bb
new file mode 100644
index 000000000..a9496c1ef
--- /dev/null
+++ b/build/openembedded/linphone/linphone_+git-nogtk-gsm.bb
@@ -0,0 +1,14 @@
+
+
+DESCRIPTION = "Audio/video SIP-based IP phone (console edition)"
+HOMEPAGE = "http://www.linphone.org/?lang=us"
+LICENSE = "GPLv2"
+PR="r15"
+
+DEFAULT_PREFERENCE = "3"
+OVERRIDES_append = ":console"
+OVERRIDES_append = ":gsm"
+
+#PARALLEL_MAKE="V=1"
+
+require linphone-common_git.inc
diff --git a/build/openembedded/linphone/linphone_+git-nogtk.bb b/build/openembedded/linphone/linphone_+git-nogtk.bb
new file mode 100644
index 000000000..a9228042a
--- /dev/null
+++ b/build/openembedded/linphone/linphone_+git-nogtk.bb
@@ -0,0 +1,13 @@
+## THIS unusable work in progress ##
+
+DESCRIPTION = "Audio/video SIP-based IP phone (console edition)"
+HOMEPAGE = "http://www.linphone.org/?lang=us"
+LICENSE = "GPLv2"
+PR="r14"
+
+DEFAULT_PREFERENCE = "3"
+OVERRIDES_append = ":console"
+
+#PARALLEL_MAKE="V=1"
+
+require linphone-common_git.inc
diff --git a/build/openembedded/linphone/linphone_+local-nogtk-gsm-video-x11.bb b/build/openembedded/linphone/linphone_+local-nogtk-gsm-video-x11.bb
new file mode 100644
index 000000000..427eb94bc
--- /dev/null
+++ b/build/openembedded/linphone/linphone_+local-nogtk-gsm-video-x11.bb
@@ -0,0 +1,16 @@
+## THIS unusable work in progress ##
+
+DESCRIPTION = "Audio/video SIP-based IP phone (console edition)"
+HOMEPAGE = "http://www.linphone.org/?lang=us"
+LICENSE = "GPLv2"
+PR="r9"
+
+DEFAULT_PREFERENCE = "-1"
+OVERRIDES_append = ":console"
+OVERRIDES_append = ":gsm"
+OVERRIDES_append = ":video"
+OVERRIDES_append = ":x11"
+
+#PARALLEL_MAKE="V=1"
+
+require linphone-common_local.inc
diff --git a/build/openembedded/linphone/linphone_+local-nogtk-gsm-video.bb b/build/openembedded/linphone/linphone_+local-nogtk-gsm-video.bb
new file mode 100644
index 000000000..9a599dc4f
--- /dev/null
+++ b/build/openembedded/linphone/linphone_+local-nogtk-gsm-video.bb
@@ -0,0 +1,15 @@
+## THIS unusable work in progress ##
+
+DESCRIPTION = "Audio/video SIP-based IP phone (console edition)"
+HOMEPAGE = "http://www.linphone.org/?lang=us"
+LICENSE = "GPLv2"
+PR="r9"
+
+DEFAULT_PREFERENCE = "-1"
+OVERRIDES_append = ":console"
+OVERRIDES_append = ":gsm"
+OVERRIDES_append = ":video"
+
+#PARALLEL_MAKE="V=1"
+
+require linphone-common_local.inc
diff --git a/build/openembedded/linphone/linphone_+local-nogtk-gsm.bb b/build/openembedded/linphone/linphone_+local-nogtk-gsm.bb
new file mode 100644
index 000000000..d48a1a17e
--- /dev/null
+++ b/build/openembedded/linphone/linphone_+local-nogtk-gsm.bb
@@ -0,0 +1,14 @@
+## THIS unusable work in progress ##
+
+DESCRIPTION = "Audio/video SIP-based IP phone (console edition)"
+HOMEPAGE = "http://www.linphone.org/?lang=us"
+LICENSE = "GPLv2"
+PR="r9"
+
+DEFAULT_PREFERENCE = "-1"
+OVERRIDES_append = ":console"
+OVERRIDES_append = ":gsm"
+
+#PARALLEL_MAKE="V=1"
+
+require linphone-common_local.inc
diff --git a/build/openembedded/linphone/linphone_+local-nogtk.bb b/build/openembedded/linphone/linphone_+local-nogtk.bb
new file mode 100644
index 000000000..9a54c256a
--- /dev/null
+++ b/build/openembedded/linphone/linphone_+local-nogtk.bb
@@ -0,0 +1,13 @@
+## THIS unusable work in progress ##
+
+DESCRIPTION = "Audio/video SIP-based IP phone (console edition)"
+HOMEPAGE = "http://www.linphone.org/?lang=us"
+LICENSE = "GPLv2"
+PR="r9"
+
+DEFAULT_PREFERENCE = "-1"
+OVERRIDES_append = ":console"
+
+#PARALLEL_MAKE="V=1"
+
+require linphone-common_local.inc
diff --git a/build/openembedded/linphone/linphone_git.bb b/build/openembedded/linphone/linphone_git.bb
new file mode 100644
index 000000000..f1c65a799
--- /dev/null
+++ b/build/openembedded/linphone/linphone_git.bb
@@ -0,0 +1,39 @@
+DESCRIPTION = "Audio/video SIP-based IP phone (console edition)"
+HOMEPAGE = "http://www.linphone.org/?lang=us"
+LICENSE = "GPLv2"
+
+SRCREV = "855f3aa1b83fd979bb8ff4c6373522bc72fe77ec"
+L_GIT_SRC_URI = "gitosis@git.linphone.org:linphone-daemon"
+
+LINPHONE_TMP_DIR="/tmp/LINPHONE_TMP_${SRCREV}"
+SRC_URI = "file://${LINPHONE_TMP_DIR}/linphone.tar.gz"
+
+S = "${WORKDIR}/linphone"
+
+# bitbake git fetcher currently doesn't handle git submodules
+# There is also a problem with autogen and AC_SUBST
+# note: don't use a ssh key with password, it does not work.
+do_fetch_prepend () {
+ import os,bb
+ bb.note("Hack preparing clone in %s" %"${LINPHONE_TMP_DIR}")
+ os.system("rm -rf ${LINPHONE_TMP_DIR}")
+ os.system("mkdir -p ${LINPHONE_TMP_DIR}")
+
+ bb.note("Hack cloning linphone !recursively")
+ os.system("cd ${LINPHONE_TMP_DIR}; git clone ${L_GIT_SRC_URI} linphone; cd linphone; git checkout ${SRCREV}; git submodule update --recursive --init")
+
+ bb.note("Hack launching autogen.sh manually")
+ os.system("cd ${LINPHONE_TMP_DIR}/linphone; ./autogen.sh")
+
+ bb.note("Hack preparing linphone.tar.gz")
+ # we need to keep the .git since the versioning in linphone is done through `git describe`
+ os.system("cd ${LINPHONE_TMP_DIR}; tar czf linphone.tar.gz linphone")
+}
+
+require linphone-common.inc
+
+
+#Required to avoid compile errors on May 2011.
+EXTRA_OECONF +=" --disable-strict"
+
+LIC_FILES_CHKSUM = "file://COPYING;md5=9f9938e31db89d55a796e86808c96848"
diff --git a/build/openembedded/msamr/msamr-common.inc b/build/openembedded/msamr/msamr-common.inc
new file mode 100644
index 000000000..4f35dafc3
--- /dev/null
+++ b/build/openembedded/msamr/msamr-common.inc
@@ -0,0 +1,20 @@
+DESCRIPTION = "Mediastreamer2 plugin adding support for AMR codec"
+SECTION = "libs"
+PRIORITY = "optional"
+LICENSE = "GPLv3"
+
+LIC_FILES_CHKSUM = "file://COPYING;md5=d32239bcb673463ab874e80d47fae504"
+
+DEPENDS = "linphone opencore-amr"
+DEPENDS_append_wideband = " vo-amrwbenc"
+
+MSAMR_WIDEBAND = ""
+MSAMR_WIDEBAND_wideband = "--enable-wideband"
+
+EXTRA_OECONF = "\
+ ${MSAMR_WIDEBAND} \
+ "
+
+FILES_${PN} = "${libdir}/mediastreamer/plugins/*.so.*"
+FILES_${PN}-dev = "${libdir}/mediastreamer/plugins/*.la ${libdir}/mediastreamer/plugins/*.so"
+inherit autotools pkgconfig
diff --git a/build/openembedded/msamr/msamr_git+wb.bb b/build/openembedded/msamr/msamr_git+wb.bb
new file mode 100644
index 000000000..63f1caa8b
--- /dev/null
+++ b/build/openembedded/msamr/msamr_git+wb.bb
@@ -0,0 +1,8 @@
+PR = "r2"
+SRC_URI = "git://git.linphone.org/msamr.git;protocol=git"
+SRCREV = "6ed342ed00526c21e85f8a06538fe3da2c7a24f4"
+S = "${WORKDIR}/git"
+
+OVERRIDES_append = ":wideband"
+
+require msamr-common.inc
diff --git a/build/openembedded/msamr/msamr_git.bb b/build/openembedded/msamr/msamr_git.bb
new file mode 100644
index 000000000..07b394dc2
--- /dev/null
+++ b/build/openembedded/msamr/msamr_git.bb
@@ -0,0 +1,6 @@
+PR = "r2"
+SRC_URI = "git://git.linphone.org/msamr.git;protocol=git"
+SRCREV = "6ed342ed00526c21e85f8a06538fe3da2c7a24f4"
+S = "${WORKDIR}/git"
+
+require msamr-common.inc
diff --git a/build/openembedded/msamr/msamr_local+wb.bb b/build/openembedded/msamr/msamr_local+wb.bb
new file mode 100644
index 000000000..cb89a1bde
--- /dev/null
+++ b/build/openembedded/msamr/msamr_local+wb.bb
@@ -0,0 +1,13 @@
+PR = "r1"
+SRC_URI = "file://${HOME}/msamr-0.0.2.tar.gz"
+S = "${WORKDIR}/msamr-0.0.2"
+
+do_configure_prepend () {
+ ./autogen.sh
+}
+
+OVERRIDES_append = ":wideband"
+
+DEFAULT_PREFERENCE="-1"
+
+require msamr-common.inc
diff --git a/build/openembedded/msamr/msamr_local.bb b/build/openembedded/msamr/msamr_local.bb
new file mode 100644
index 000000000..f6349af74
--- /dev/null
+++ b/build/openembedded/msamr/msamr_local.bb
@@ -0,0 +1,11 @@
+PR = "r1"
+SRC_URI = "file://${HOME}/msamr-0.0.2.tar.gz"
+S = "${WORKDIR}/msamr-0.0.2"
+
+do_configure_prepend () {
+ ./autogen.sh
+}
+
+DEFAULT_PREFERENCE="-1"
+
+require msamr-common.inc
diff --git a/build/openembedded/msilbc/msilbc-common.inc b/build/openembedded/msilbc/msilbc-common.inc
new file mode 100644
index 000000000..6651f9a70
--- /dev/null
+++ b/build/openembedded/msilbc/msilbc-common.inc
@@ -0,0 +1,12 @@
+DESCRIPTION = "Mediastreamer2 plugin adding support for ILBC codec"
+SECTION = "libs"
+PRIORITY = "optional"
+LICENSE = "GPLv2"
+DEPENDS = "linphone libilbc-rfc3951"
+
+FILES_${PN} = "${libdir}/mediastreamer/plugins/*.so.*"
+FILES_${PN}-dev = "${libdir}/mediastreamer/plugins/*.la ${libdir}/mediastreamer/plugins/*.so"
+
+LIC_FILES_CHKSUM = "file://COPYING;md5=59530bdf33659b29e73d4adb9f9f6552"
+
+inherit autotools pkgconfig
diff --git a/build/openembedded/msilbc/msilbc_git.bb b/build/openembedded/msilbc/msilbc_git.bb
new file mode 100644
index 000000000..3ed186094
--- /dev/null
+++ b/build/openembedded/msilbc/msilbc_git.bb
@@ -0,0 +1,6 @@
+PR = "r1"
+SRC_URI = "git://git.linphone.org/msilbc.git;protocol=git"
+SRCREV = "2bf845d7f537eb671dd32ca5b0cc932e8bb48952"
+S = "${WORKDIR}/git"
+
+require msilbc-common.inc
diff --git a/build/openembedded/msilbc/msilbc_local.bb b/build/openembedded/msilbc/msilbc_local.bb
new file mode 100644
index 000000000..72b08b239
--- /dev/null
+++ b/build/openembedded/msilbc/msilbc_local.bb
@@ -0,0 +1,11 @@
+PR = "r0"
+SRC_URI = "file://${HOME}/msilbc-2.0.3.tar.gz"
+S = "${WORKDIR}/msilbc-2.0.3"
+
+do_configure_prepend () {
+ ./autogen.sh
+}
+
+DEFAULT_PREFERENCE="-1"
+
+require msilbc-common.inc
diff --git a/build/openembedded/msimx6vpu-h264/msimx6vpu-h264.inc b/build/openembedded/msimx6vpu-h264/msimx6vpu-h264.inc
new file mode 100644
index 000000000..74c31cdb1
--- /dev/null
+++ b/build/openembedded/msimx6vpu-h264/msimx6vpu-h264.inc
@@ -0,0 +1,12 @@
+SECTION = "libs"
+
+DEPENDS_${PN} = "libmediastreamer imx-lib imx-vpu"
+DEPENDS = "libmediastreamer imx-lib imx-vpu"
+
+PROVIDES = "msimx6vpu-h264 msimx6vpu-h264-dbg"
+
+inherit autotools gettext
+
+FILES_${PN} = "${libdir}/mediastreamer/plugins/*.so.*"
+FILES_${PN}-dev = "${libdir}/mediastreamer/plugins/*.la ${libdir}/mediastreamer/plugins/*.so"
+FILES_${PN}-dbg = "${libdir}/mediastreamer/plugins/.debug/*.so.* /usr/src/debug"
diff --git a/build/openembedded/msimx6vpu-h264/msimx6vpu-h264_git.bb b/build/openembedded/msimx6vpu-h264/msimx6vpu-h264_git.bb
new file mode 100644
index 000000000..13137448a
--- /dev/null
+++ b/build/openembedded/msimx6vpu-h264/msimx6vpu-h264_git.bb
@@ -0,0 +1,23 @@
+DESCRIPTION = "A H264 encoder/decoder plugin for mediastreamer using Freescale's IMX6's VPU"
+HOMEPAGE = "http://www.linphone.org/?lang=us"
+LICENSE = "GPLv3+"
+
+GIT_SRC_URI = "gitosis@git.linphone.org:msimx6vpu-h264.git"
+SRCREV = "master"
+LIC_FILES_CHKSUM = "file://COPYING;md5=c46082167a314d785d012a244748d803"
+
+TMP_DIR="/tmp/TMP_MSIMX6VPUH264_${SRCREV}"
+SRC_URI = "file://${TMP_DIR}/msimx6vpu-h264.tar.gz"
+
+S = "${WORKDIR}/msimx6vpu-h264"
+
+# note: don't use a ssh key with password, it does not work.
+do_fetch_prepend () {
+ import os,bb
+ os.system("rm -rf ${TMP_DIR}")
+ os.system("mkdir -p ${TMP_DIR}")
+ os.system("cd ${TMP_DIR}; git clone ${GIT_SRC_URI} msimx6vpu-h264")
+ os.system("cd ${TMP_DIR}; tar czf msimx6vpu-h264.tar.gz msimx6vpu-h264")
+}
+
+require msimx6vpu-h264.inc
diff --git a/build/openembedded/msv4l2-display/msv4l2-display-common.inc b/build/openembedded/msv4l2-display/msv4l2-display-common.inc
new file mode 100644
index 000000000..c58e41998
--- /dev/null
+++ b/build/openembedded/msv4l2-display/msv4l2-display-common.inc
@@ -0,0 +1,14 @@
+SECTION = "libs"
+
+DEPENDS_${PN} = "libmediastreamer"
+DEPENDS = "libmediastreamer"
+
+PROVIDES = "msv4l2-display"
+
+EXTRA_OECONF += ' CFLAGS="-DOUTPUT_VIDEO_DEVICE=17"'
+
+inherit autotools gettext
+
+FILES_${PN} = "${libdir}/mediastreamer/plugins/*.so.*"
+FILES_${PN}-dev = "${libdir}/mediastreamer/plugins/*.la ${libdir}/mediastreamer/plugins/*.so"
+FILES_${PN}-dbg = "${libdir}/mediastreamer/plugins/.debug/*.so.* /usr/src/debug"
diff --git a/build/openembedded/msv4l2-display/msv4l2-display_git.bb b/build/openembedded/msv4l2-display/msv4l2-display_git.bb
new file mode 100644
index 000000000..14c7b7c7e
--- /dev/null
+++ b/build/openembedded/msv4l2-display/msv4l2-display_git.bb
@@ -0,0 +1,23 @@
+DESCRIPTION = "V4L2 display filter plugin for mediastreamer/linphone"
+HOMEPAGE = "http://www.linphone.org/?lang=us"
+LICENSE = "GPLv3+"
+
+GIT_SRC_URI = "gitosis@git.linphone.org:msv4l2-display.git"
+SRCREV = "master"
+LIC_FILES_CHKSUM = "file://COPYING;md5=c46082167a314d785d012a244748d803"
+
+TMP_DIR="/tmp/TMP_MSV4L2Display_${SRCREV}"
+SRC_URI = "file://${TMP_DIR}/msv4l2-display.tar.gz"
+
+S = "${WORKDIR}/msv4l2-display"
+
+# note: don't use a ssh key with password, it does not work.
+do_fetch_prepend () {
+ import os,bb
+ os.system("rm -rf ${TMP_DIR}")
+ os.system("mkdir -p ${TMP_DIR}")
+ os.system("cd ${TMP_DIR}; git clone ${GIT_SRC_URI} msv4l2-display")
+ os.system("cd ${TMP_DIR}; tar czf msv4l2-display.tar.gz msv4l2-display")
+}
+
+require msv4l2-display-common.inc
diff --git a/build/openembedded/mswebrtc/mswebrtc-common.inc b/build/openembedded/mswebrtc/mswebrtc-common.inc
new file mode 100644
index 000000000..bdccee2f4
--- /dev/null
+++ b/build/openembedded/mswebrtc/mswebrtc-common.inc
@@ -0,0 +1,15 @@
+DESCRIPTION = "Mediastreamer2 plugin adding support for WebRTC features (iSAC codec, AEC...)"
+SECTION = "libs"
+PRIORITY = "optional"
+LICENSE = "GPLv2"
+DEPENDS = "linphone"
+
+EXTRA_OECONF = "--disable-isac"
+
+FILES_${PN} = "${libdir}/mediastreamer/plugins/*.so.*"
+FILES_${PN}-dev = "${libdir}/mediastreamer/plugins/*.la ${libdir}/mediastreamer/plugins/*.so"
+FILES_${PN}-dbg = "${libdir}/mediastreamer/plugins/.debug/*.so.* /usr/src/debug"
+
+LIC_FILES_CHKSUM = "file://COPYING;md5=59530bdf33659b29e73d4adb9f9f6552"
+
+inherit autotools pkgconfig
diff --git a/build/openembedded/mswebrtc/mswebrtc_git.bb b/build/openembedded/mswebrtc/mswebrtc_git.bb
new file mode 100644
index 000000000..526f70554
--- /dev/null
+++ b/build/openembedded/mswebrtc/mswebrtc_git.bb
@@ -0,0 +1,6 @@
+PR = "r3"
+SRC_URI = "git://git.linphone.org/mswebrtc.git;protocol=git"
+SRCREV = "a9b5929928dd58299ceaed0aeb507c82bae80b55"
+S = "${WORKDIR}/git"
+
+require mswebrtc-common.inc
diff --git a/build/openembedded/mswebrtc/mswebrtc_local.bb b/build/openembedded/mswebrtc/mswebrtc_local.bb
new file mode 100644
index 000000000..8921a3c8e
--- /dev/null
+++ b/build/openembedded/mswebrtc/mswebrtc_local.bb
@@ -0,0 +1,11 @@
+PR = "r0"
+SRC_URI = "file://${HOME}/mswebrtc-1.0.tar.gz"
+S = "${WORKDIR}/mswebrtc-1.0"
+
+do_configure_prepend () {
+ ./autogen.sh
+}
+
+DEFAULT_PREFERENCE="-1"
+
+require mswebrtc-common.inc
diff --git a/build/openembedded/msx264/msx264-common.inc b/build/openembedded/msx264/msx264-common.inc
new file mode 100644
index 000000000..b340dc99f
--- /dev/null
+++ b/build/openembedded/msx264/msx264-common.inc
@@ -0,0 +1,12 @@
+DESCRIPTION = "Mediastreamer2 plugin adding support for H264 codec"
+SECTION = "libs"
+PRIORITY = "optional"
+LICENSE = "GPLv3"
+
+LIC_FILES_CHKSUM = "file://COPYING;md5=59530bdf33659b29e73d4adb9f9f6552"
+
+DEPENDS = "linphone x264"
+
+FILES_${PN} = "${libdir}/mediastreamer/plugins/*.so.*"
+FILES_${PN}-dev = "${libdir}/mediastreamer/plugins/*.la ${libdir}/mediastreamer/plugins/*.so"
+inherit autotools pkgconfig
diff --git a/build/openembedded/msx264/msx264_git.bb b/build/openembedded/msx264/msx264_git.bb
new file mode 100644
index 000000000..40a26e5fe
--- /dev/null
+++ b/build/openembedded/msx264/msx264_git.bb
@@ -0,0 +1,6 @@
+PR = "r1"
+SRC_URI = "git://git.linphone.org/msx264.git;protocol=git"
+SRCREV = "f1fd3d6be817dd5c1b8a46f68de04421f75cf056"
+S = "${WORKDIR}/git"
+
+require msx264-common.inc
diff --git a/build/openembedded/msx264/msx264_local.bb b/build/openembedded/msx264/msx264_local.bb
new file mode 100644
index 000000000..ef7ab1a82
--- /dev/null
+++ b/build/openembedded/msx264/msx264_local.bb
@@ -0,0 +1,11 @@
+PR = "r1"
+SRC_URI = "file://${HOME}/msx264-1.4.2.tar.gz"
+S = "${WORKDIR}/msx264-1.4.2"
+
+do_configure_prepend () {
+ ./autogen.sh
+}
+
+DEFAULT_PREFERENCE="-1"
+
+require msx264-common.inc
diff --git a/build/openembedded/opencore-amr_0.1.3.bb b/build/openembedded/opencore-amr_0.1.3.bb
new file mode 100644
index 000000000..7c338543d
--- /dev/null
+++ b/build/openembedded/opencore-amr_0.1.3.bb
@@ -0,0 +1,12 @@
+DESCRIPTION = "OpenCORE Adaptive Multi Rate (AMR) speech codec library implementation"
+SECTION = "libs"
+PRIORITY = "optional"
+LICENSE = "Apache"
+
+PR = "r1"
+SRC_URI = "${SOURCEFORGE_MIRROR}/opencore-amr/${PN}-${PV}.tar.gz"
+
+inherit autotools pkgconfig
+SRC_URI[md5sum] = "09d2c5dfb43a9f6e9fec8b1ae678e725"
+SRC_URI[sha256sum] = "106bf811c1f36444d7671d8fd2589f8b2e0cca58a2c764da62ffc4a070595385"
+LIC_FILES_CHKSUM = "file://COPYING;md5=dd2c2486aca02190153cf399e508c7e7"
\ No newline at end of file
diff --git a/build/openembedded/polarssl/polarssl-linphone/darwin.patch b/build/openembedded/polarssl/polarssl-linphone/darwin.patch
new file mode 100644
index 000000000..9faf5c1b7
--- /dev/null
+++ b/build/openembedded/polarssl/polarssl-linphone/darwin.patch
@@ -0,0 +1,14 @@
+diff -urN a/library/Makefile b/library/Makefile
+--- a/library/Makefile 2013-07-29 17:26:14.000000000 +0200
++++ b/library/Makefile 2013-07-29 17:26:58.000000000 +0200
+@@ -26,7 +26,9 @@
+
+ DLEXT=so
+ # OSX shared library extension:
+-# DLEXT=dylib
++ifdef DARWIN
++DLEXT=dylib
++endif
+
+ # Windows shared library extension:
+ ifdef WINDOWS
diff --git a/build/openembedded/polarssl/polarssl-linphone/soname.patch b/build/openembedded/polarssl/polarssl-linphone/soname.patch
new file mode 100644
index 000000000..29a1ca955
--- /dev/null
+++ b/build/openembedded/polarssl/polarssl-linphone/soname.patch
@@ -0,0 +1,51 @@
+diff -urN polarssl.orig/library/Makefile polarssl.new/library/Makefile
+--- polarssl.orig/library/Makefile 2013-08-22 10:24:46.353700982 +0200
++++ polarssl.new/library/Makefile 2013-08-22 10:21:43.933733318 +0200
+@@ -28,12 +28,14 @@
+ # OSX shared library extension:
+ ifdef DARWIN
+ DLEXT=dylib
++SONAME=libpolarssl.0.dylib
+ endif
+
+ # Windows shared library extension:
+ ifdef WINDOWS
+ DLEXT=dll
+ LDFLAGS += -lws2_32
++SONAME=libpolarssl-0.dll
+ endif
+
+ OBJS= aes.o arc4.o asn1parse.o \
+@@ -73,15 +75,17 @@
+
+ libpolarssl.so: libpolarssl.a
+ echo " LD $@"
+- $(CC) ${LDFLAGS} -shared -Wl,-soname,$(SONAME) -o $@ $(OBJS)
++ $(CC) ${LDFLAGS} -shared -Wl,-soname,$(SONAME) -o $(SONAME) $(OBJS)
++ ln -s $(SONAME) $@
+
+ libpolarssl.dylib: libpolarssl.a
+ echo " LD $@"
+- $(CC) ${LDFLAGS} -dynamiclib -o $@ $(OBJS)
++ $(CC) ${LDFLAGS} -dynamiclib -Wl,-install_name,$(SONAME) -o $(SONAME) $(OBJS)
++ ln -s $(SONAME) $@
+
+ libpolarssl.dll: libpolarssl.a
+ echo " LD $@"
+- $(CC) -shared -Wl,-soname,$@,--out-implib,$@.a -o $@ $(OBJS) -lws2_32 -lwinmm -lgdi32
++ $(CC) -shared -Wl,-soname,$(SONAME),--out-implib,$@.a -o $(SONAME) $(OBJS) -lws2_32 -lwinmm -lgdi32
+
+ .c.o:
+ echo " CC $<"
+diff -urN polarssl.orig/Makefile polarssl.new/Makefile
+--- polarssl.orig/Makefile 2013-08-22 10:24:34.585703377 +0200
++++ polarssl.new/Makefile 2013-08-22 10:19:26.801749343 +0200
+@@ -21,7 +21,7 @@
+ cp -r include/polarssl $(DESTDIR)/include
+
+ mkdir -p $(DESTDIR)/lib
+- cp library/libpolarssl.* $(DESTDIR)/lib
++ cp -d library/libpolarssl* $(DESTDIR)/lib
+
+ mkdir -p $(DESTDIR)/bin
+ cp library/libpolarssl*.dll $(DESTDIR)/bin
diff --git a/build/openembedded/polarssl/polarssl.inc b/build/openembedded/polarssl/polarssl.inc
new file mode 100644
index 000000000..c9957212e
--- /dev/null
+++ b/build/openembedded/polarssl/polarssl.inc
@@ -0,0 +1,17 @@
+DESCRIPTION = "SSL/TLS library"
+LICENSE = "GPL"
+
+inherit pkgconfig
+
+EXTRA_OEMAKE += " SHARED=1"
+
+PROVIDES = "polarssl polarssl-dev"
+ALLOW_EMPTY_${PN} = "1"
+
+PACKAGES += "${PN}-utils"
+
+MAKE_DESTDIR = "DESTDIR=${D}/${prefix}"
+
+FILES_${PN} += "${sharedlibdir}/*${SOLIBSDEV}"
+FILES_${PN}-dev += "!${sharedlibdir}/*${SOLIBSDEV}"
+FILES_${PN}-utils += "${bindir}/polarssl_*"
diff --git a/build/openembedded/polarssl/polarssl_linphone.bb b/build/openembedded/polarssl/polarssl_linphone.bb
new file mode 100644
index 000000000..41ed5060e
--- /dev/null
+++ b/build/openembedded/polarssl/polarssl_linphone.bb
@@ -0,0 +1,14 @@
+require polarssl.inc
+
+SRCREV="cecb44e4f13f42f793dde34b42793e1ebcce91a5"
+
+S = "${WORKDIR}/git"
+
+do_fetch_prepend () {
+ import bb
+ bb.note("Will checkout in %s" % "${S}" )
+}
+
+SRC_URI = "git://git.linphone.org/polarssl.git"
+LIC_FILES_CHKSUM = "file://LICENSE;md5=751419260aa954499f7abaabaa882bbe"
+
diff --git a/build/openembedded/spandsp_0.0.6-pre18.bb b/build/openembedded/spandsp_0.0.6-pre18.bb
new file mode 100644
index 000000000..c87ff3c9e
--- /dev/null
+++ b/build/openembedded/spandsp_0.0.6-pre18.bb
@@ -0,0 +1,24 @@
+PR = "r0"
+
+SRC_URI = "http://www.soft-switch.org/downloads/spandsp/${PN}-0.0.6pre18.tgz"
+
+S = "${WORKDIR}/spandsp-0.0.6"
+
+# *cough*
+do_configure_append() {
+ rm config.log
+}
+
+DESCRIPTION = "A library of many DSP functions for telephony."
+HOMEPAGE = "http://www.soft-switch.org"
+SECTION = "libs"
+LICENSE = "LGPL"
+DEPENDS_${PN} = "tiff libxml2"
+
+inherit autotools
+
+#PARALLEL_MAKE = ""
+
+SRC_URI[md5sum] = "98330bc00a581ed8d71ebe34afabbcf9"
+SRC_URI[sha256sum] = "835cd886105e4e39791f0e8cfe004c39b069f2e6dcb0795a68a6c79b5d14af2c"
+LIC_FILES_CHKSUM = "file://COPYING;md5=8791c23ddf418deb5be264cffb5fa6bc"
diff --git a/build/openembedded/vo-amrwbenc_0.1.2.bb b/build/openembedded/vo-amrwbenc_0.1.2.bb
new file mode 100644
index 000000000..a34b7de3b
--- /dev/null
+++ b/build/openembedded/vo-amrwbenc_0.1.2.bb
@@ -0,0 +1,13 @@
+DESCRIPTION = "VisualOn AMR-WB encoder library"
+SECTION = "libs"
+PRIORITY = "optional"
+LICENSE = "Apache"
+DEPENDS = "opencore-amr"
+
+PR = "r1"
+SRC_URI = "${SOURCEFORGE_MIRROR}/opencore-amr/${PN}-${PV}.tar.gz"
+
+inherit autotools pkgconfig
+SRC_URI[md5sum] = "588205f686adc23532e31fe3646ddcb6"
+SRC_URI[sha256sum] = "dd8c33e57bc415754f31fbb1b1536563bf731fc14e55f8182564e4c0fbb26435"
+LIC_FILES_CHKSUM = "file://COPYING;md5=dd2c2486aca02190153cf399e508c7e7"
\ No newline at end of file
diff --git a/build/openembedded/x264_git.bb b/build/openembedded/x264_git.bb
new file mode 100644
index 000000000..481da9986
--- /dev/null
+++ b/build/openembedded/x264_git.bb
@@ -0,0 +1,18 @@
+DESCRIPTION = "x264 is a free software library and application for encoding video streams into the H.264/MPEG-4 AVC format."
+SECTION = "libs/multimedia"
+PRIORITY = "optional"
+LICENSE = "GPLv2"
+HOMEPAGE = "http://www.videolan.org/developers/x264.html"
+PR = "r1"
+
+SRC_URI = "git://git.videolan.org/x264.git;protocol=git"
+SRCREV = "e89c4cfc9f37d0b7684507974b333545b5bcc37a"
+S = "${WORKDIR}/git"
+
+EXTRA_OECONF += "--disable-lavf --enable-pic"
+EXTRA_OEMAKE = ""
+AS = "${TARGET_PREFIX}gcc"
+
+LIC_FILES_CHKSUM = "file://COPYING;md5=94d55d512a9ba36caa9b7df079bae19f"
+
+inherit autotools pkgconfig
diff --git a/build/wince/liblinphone.sln b/build/wince/liblinphone.sln
deleted file mode 100644
index fe4efff27..000000000
--- a/build/wince/liblinphone.sln
+++ /dev/null
@@ -1,48 +0,0 @@
-
-Microsoft Visual Studio Solution File, Format Version 10.00
-# Visual Studio 2008
-Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "liblinphone", "liblinphone.vcproj", "{290078F0-3B63-47BF-A2A9-E1AF5411F5E7}"
-EndProject
-Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "linphonec", "linphonec\linphonec.vcproj", "{92574924-BF59-4DAA-994B-9978B80E5797}"
-EndProject
-Global
- GlobalSection(SolutionConfigurationPlatforms) = preSolution
- Debug|Win32 = Debug|Win32
- Debug|Windows Mobile 6 Professional SDK (ARMV4I) = Debug|Windows Mobile 6 Professional SDK (ARMV4I)
- Debug|Windows Mobile 6 Standard SDK (ARMV4I) = Debug|Windows Mobile 6 Standard SDK (ARMV4I)
- Release|Win32 = Release|Win32
- Release|Windows Mobile 6 Professional SDK (ARMV4I) = Release|Windows Mobile 6 Professional SDK (ARMV4I)
- Release|Windows Mobile 6 Standard SDK (ARMV4I) = Release|Windows Mobile 6 Standard SDK (ARMV4I)
- EndGlobalSection
- GlobalSection(ProjectConfigurationPlatforms) = postSolution
- {290078F0-3B63-47BF-A2A9-E1AF5411F5E7}.Debug|Win32.ActiveCfg = Debug|Win32
- {290078F0-3B63-47BF-A2A9-E1AF5411F5E7}.Debug|Win32.Build.0 = Debug|Win32
- {290078F0-3B63-47BF-A2A9-E1AF5411F5E7}.Debug|Windows Mobile 6 Professional SDK (ARMV4I).ActiveCfg = Debug|Windows Mobile 6 Professional SDK (ARMV4I)
- {290078F0-3B63-47BF-A2A9-E1AF5411F5E7}.Debug|Windows Mobile 6 Professional SDK (ARMV4I).Build.0 = Debug|Windows Mobile 6 Professional SDK (ARMV4I)
- {290078F0-3B63-47BF-A2A9-E1AF5411F5E7}.Debug|Windows Mobile 6 Professional SDK (ARMV4I).Deploy.0 = Debug|Windows Mobile 6 Professional SDK (ARMV4I)
- {290078F0-3B63-47BF-A2A9-E1AF5411F5E7}.Debug|Windows Mobile 6 Standard SDK (ARMV4I).ActiveCfg = Debug|Windows Mobile 6 Standard SDK (ARMV4I)
- {290078F0-3B63-47BF-A2A9-E1AF5411F5E7}.Debug|Windows Mobile 6 Standard SDK (ARMV4I).Build.0 = Debug|Windows Mobile 6 Standard SDK (ARMV4I)
- {290078F0-3B63-47BF-A2A9-E1AF5411F5E7}.Debug|Windows Mobile 6 Standard SDK (ARMV4I).Deploy.0 = Debug|Windows Mobile 6 Standard SDK (ARMV4I)
- {290078F0-3B63-47BF-A2A9-E1AF5411F5E7}.Release|Win32.ActiveCfg = Release|Win32
- {290078F0-3B63-47BF-A2A9-E1AF5411F5E7}.Release|Win32.Build.0 = Release|Win32
- {290078F0-3B63-47BF-A2A9-E1AF5411F5E7}.Release|Windows Mobile 6 Professional SDK (ARMV4I).ActiveCfg = Release|Windows Mobile 6 Professional SDK (ARMV4I)
- {290078F0-3B63-47BF-A2A9-E1AF5411F5E7}.Release|Windows Mobile 6 Professional SDK (ARMV4I).Build.0 = Release|Windows Mobile 6 Professional SDK (ARMV4I)
- {290078F0-3B63-47BF-A2A9-E1AF5411F5E7}.Release|Windows Mobile 6 Professional SDK (ARMV4I).Deploy.0 = Release|Windows Mobile 6 Professional SDK (ARMV4I)
- {290078F0-3B63-47BF-A2A9-E1AF5411F5E7}.Release|Windows Mobile 6 Standard SDK (ARMV4I).ActiveCfg = Release|Windows Mobile 6 Standard SDK (ARMV4I)
- {290078F0-3B63-47BF-A2A9-E1AF5411F5E7}.Release|Windows Mobile 6 Standard SDK (ARMV4I).Build.0 = Release|Windows Mobile 6 Standard SDK (ARMV4I)
- {290078F0-3B63-47BF-A2A9-E1AF5411F5E7}.Release|Windows Mobile 6 Standard SDK (ARMV4I).Deploy.0 = Release|Windows Mobile 6 Standard SDK (ARMV4I)
- {92574924-BF59-4DAA-994B-9978B80E5797}.Debug|Win32.ActiveCfg = Debug|Windows Mobile 6 Professional SDK (ARMV4I)
- {92574924-BF59-4DAA-994B-9978B80E5797}.Debug|Windows Mobile 6 Professional SDK (ARMV4I).ActiveCfg = Debug|Windows Mobile 6 Professional SDK (ARMV4I)
- {92574924-BF59-4DAA-994B-9978B80E5797}.Debug|Windows Mobile 6 Professional SDK (ARMV4I).Build.0 = Debug|Windows Mobile 6 Professional SDK (ARMV4I)
- {92574924-BF59-4DAA-994B-9978B80E5797}.Debug|Windows Mobile 6 Professional SDK (ARMV4I).Deploy.0 = Debug|Windows Mobile 6 Professional SDK (ARMV4I)
- {92574924-BF59-4DAA-994B-9978B80E5797}.Debug|Windows Mobile 6 Standard SDK (ARMV4I).ActiveCfg = Debug|Windows Mobile 6 Professional SDK (ARMV4I)
- {92574924-BF59-4DAA-994B-9978B80E5797}.Release|Win32.ActiveCfg = Release|Windows Mobile 6 Professional SDK (ARMV4I)
- {92574924-BF59-4DAA-994B-9978B80E5797}.Release|Windows Mobile 6 Professional SDK (ARMV4I).ActiveCfg = Release|Windows Mobile 6 Professional SDK (ARMV4I)
- {92574924-BF59-4DAA-994B-9978B80E5797}.Release|Windows Mobile 6 Professional SDK (ARMV4I).Build.0 = Release|Windows Mobile 6 Professional SDK (ARMV4I)
- {92574924-BF59-4DAA-994B-9978B80E5797}.Release|Windows Mobile 6 Professional SDK (ARMV4I).Deploy.0 = Release|Windows Mobile 6 Professional SDK (ARMV4I)
- {92574924-BF59-4DAA-994B-9978B80E5797}.Release|Windows Mobile 6 Standard SDK (ARMV4I).ActiveCfg = Release|Windows Mobile 6 Professional SDK (ARMV4I)
- EndGlobalSection
- GlobalSection(SolutionProperties) = preSolution
- HideSolutionNode = FALSE
- EndGlobalSection
-EndGlobal
diff --git a/build/wince/liblinphone.vcproj b/build/wince/liblinphone.vcproj
deleted file mode 100644
index 9909c8a1f..000000000
--- a/build/wince/liblinphone.vcproj
+++ /dev/null
@@ -1,585 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/build/wince/linphonec/linphonec.vcproj b/build/wince/linphonec/linphonec.vcproj
deleted file mode 100644
index 058ce425c..000000000
--- a/build/wince/linphonec/linphonec.vcproj
+++ /dev/null
@@ -1,240 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/build/windows10/liblinphone-tester/App.xaml b/build/windows10/liblinphone-tester/App.xaml
deleted file mode 100644
index 8ed2e3f95..000000000
--- a/build/windows10/liblinphone-tester/App.xaml
+++ /dev/null
@@ -1,16 +0,0 @@
-
-
-
-
-
-
-
-
-
-
diff --git a/build/windows10/liblinphone-tester/App.xaml.cs b/build/windows10/liblinphone-tester/App.xaml.cs
deleted file mode 100644
index e80598ed7..000000000
--- a/build/windows10/liblinphone-tester/App.xaml.cs
+++ /dev/null
@@ -1,114 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.IO;
-using System.Linq;
-using System.Runtime.InteropServices.WindowsRuntime;
-using Windows.ApplicationModel;
-using Windows.ApplicationModel.Activation;
-using Windows.Foundation;
-using Windows.Foundation.Collections;
-using Windows.UI.Xaml;
-using Windows.UI.Xaml.Controls;
-using Windows.UI.Xaml.Controls.Primitives;
-using Windows.UI.Xaml.Data;
-using Windows.UI.Xaml.Input;
-using Windows.UI.Xaml.Media;
-using Windows.UI.Xaml.Navigation;
-
-// The Blank Application template is documented at http://go.microsoft.com/fwlink/?LinkId=402347&clcid=0x409
-
-namespace liblinphone_tester
-{
- ///
- /// Provides application-specific behavior to supplement the default Application class.
- ///
- sealed partial class App : Application
- {
- ///
- /// Allows tracking page views, exceptions and other telemetry through the Microsoft Application Insights service.
- ///
- public static Microsoft.ApplicationInsights.TelemetryClient TelemetryClient;
-
- ///
- /// Initializes the singleton application object. This is the first line of authored code
- /// executed, and as such is the logical equivalent of main() or WinMain().
- ///
- public App()
- {
- TelemetryClient = new Microsoft.ApplicationInsights.TelemetryClient();
-
- this.InitializeComponent();
- this.Suspending += OnSuspending;
- }
-
- ///
- /// Invoked when the application is launched normally by the end user. Other entry points
- /// will be used such as when the application is launched to open a specific file.
- ///
- /// Details about the launch request and process.
- protected override void OnLaunched(LaunchActivatedEventArgs e)
- {
-
-#if DEBUG
- if (System.Diagnostics.Debugger.IsAttached)
- {
- this.DebugSettings.EnableFrameRateCounter = true;
- }
-#endif
-
- Frame rootFrame = Window.Current.Content as Frame;
-
- // Do not repeat app initialization when the Window already has content,
- // just ensure that the window is active
- if (rootFrame == null)
- {
- // Create a Frame to act as the navigation context and navigate to the first page
- rootFrame = new Frame();
-
- rootFrame.NavigationFailed += OnNavigationFailed;
-
- if (e.PreviousExecutionState == ApplicationExecutionState.Terminated)
- {
- //TODO: Load state from previously suspended application
- }
-
- // Place the frame in the current Window
- Window.Current.Content = rootFrame;
- }
-
- if (rootFrame.Content == null)
- {
- // When the navigation stack isn't restored navigate to the first page,
- // configuring the new page by passing required information as a navigation
- // parameter
- rootFrame.Navigate(typeof(MainPage), e.Arguments);
- }
- // Ensure the current window is active
- Window.Current.Activate();
- }
-
- ///
- /// Invoked when Navigation to a certain page fails
- ///
- /// The Frame which failed navigation
- /// Details about the navigation failure
- void OnNavigationFailed(object sender, NavigationFailedEventArgs e)
- {
- throw new Exception("Failed to load Page " + e.SourcePageType.FullName);
- }
-
- ///
- /// Invoked when application execution is being suspended. Application state is saved
- /// without knowing whether the application will be terminated or resumed with the contents
- /// of memory still intact.
- ///
- /// The source of the suspend request.
- /// Details about the suspend request.
- private void OnSuspending(object sender, SuspendingEventArgs e)
- {
- var deferral = e.SuspendingOperation.GetDeferral();
- //TODO: Save application state and stop any background activity
- deferral.Complete();
- }
- }
-}
diff --git a/build/windows10/liblinphone-tester/ApplicationInsights.config b/build/windows10/liblinphone-tester/ApplicationInsights.config
deleted file mode 100644
index 8a6452a34..000000000
--- a/build/windows10/liblinphone-tester/ApplicationInsights.config
+++ /dev/null
@@ -1,23 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/build/windows10/liblinphone-tester/Assets/Logo.png b/build/windows10/liblinphone-tester/Assets/Logo.png
deleted file mode 100644
index ecfbad551..000000000
Binary files a/build/windows10/liblinphone-tester/Assets/Logo.png and /dev/null differ
diff --git a/build/windows10/liblinphone-tester/Assets/SmallLogo.png b/build/windows10/liblinphone-tester/Assets/SmallLogo.png
deleted file mode 100644
index ecfbad551..000000000
Binary files a/build/windows10/liblinphone-tester/Assets/SmallLogo.png and /dev/null differ
diff --git a/build/windows10/liblinphone-tester/Assets/SplashScreen.png b/build/windows10/liblinphone-tester/Assets/SplashScreen.png
deleted file mode 100644
index 0d83a7980..000000000
Binary files a/build/windows10/liblinphone-tester/Assets/SplashScreen.png and /dev/null differ
diff --git a/build/windows10/liblinphone-tester/Assets/WideLogo.scale-100.png b/build/windows10/liblinphone-tester/Assets/WideLogo.scale-100.png
deleted file mode 100644
index 85fd4db8f..000000000
Binary files a/build/windows10/liblinphone-tester/Assets/WideLogo.scale-100.png and /dev/null differ
diff --git a/build/windows10/liblinphone-tester/DataModel/UnitTestDataSource.cs b/build/windows10/liblinphone-tester/DataModel/UnitTestDataSource.cs
deleted file mode 100644
index a5bc5bdc3..000000000
--- a/build/windows10/liblinphone-tester/DataModel/UnitTestDataSource.cs
+++ /dev/null
@@ -1,251 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-using liblinphone_tester_runtime_component;
-using System.Collections.ObjectModel;
-using Windows.UI.Xaml;
-using Windows.UI.Xaml.Controls;
-using Windows.UI.Xaml.Data;
-using Windows.UI.Xaml.Media;
-using System.ComponentModel;
-using Windows.UI;
-using Windows.UI.Xaml.Documents;
-using Windows.UI.Core;
-
-namespace liblinphone_tester.DataModel
-{
- public class OutputTrace
- {
- public OutputTrace(String lev, String msg)
- {
- Level = lev;
- Msg = msg;
- }
-
- public String Level { get; private set; }
- public String Msg { get; private set; }
- }
-
- public class UnitTestSuite
- {
- public UnitTestSuite(string name)
- {
- Name = name;
- Cases = new ObservableCollection();
- Selected = false;
- }
-
- public string Name { get; private set; }
- public bool Selected
- {
- get { return Cases.All(x => x.Selected); }
- set
- {
- foreach (UnitTestCase c in Cases)
- {
- c.Selected = value;
- }
- }
- }
- public ObservableCollection Cases { get; private set; }
- }
-
- public enum UnitTestCaseState
- {
- NotRun,
- Success,
- Failure
- }
-
- public class UnitTestCase : INotifyPropertyChanged
- {
- public UnitTestCase(UnitTestSuite suite, string name)
- {
- _suite = new WeakReference(suite);
- Name = name;
- Selected = false;
- State = UnitTestCaseState.NotRun;
- Traces = new ObservableCollection();
- }
-
- public UnitTestSuite Suite
- {
- get { return _suite.Target as UnitTestSuite; }
- }
- public string Name { get; private set; }
- public bool Selected
- {
- get { return _selected; }
- set
- {
- _selected = value;
- RaisePropertyChanged("Selected");
- }
- }
- public UnitTestCaseState State
- {
- get { return _state; }
- set
- {
- _state = value;
- RaisePropertyChanged("State");
- }
- }
- public ObservableCollection Traces
- {
- get { return _traces; }
- set
- {
- _traces = value;
- RaisePropertyChanged("Traces");
- }
- }
- public CoreDispatcher Dispatcher { get; set; }
-
- public event PropertyChangedEventHandler PropertyChanged;
-
- protected void RaisePropertyChanged(string name)
- {
- if (PropertyChanged != null)
- {
- PropertyChanged(this, new PropertyChangedEventArgs(name));
- }
- }
-
- private WeakReference _suite;
- private bool _selected;
- private UnitTestCaseState _state;
- private ObservableCollection _traces;
- }
-
- public sealed class UnitTestDataSource
- {
- private static UnitTestDataSource _unitTestDataSource = new UnitTestDataSource();
- private ObservableCollection _suites = new ObservableCollection();
-
- public ObservableCollection Suites
- {
- get { return this._suites; }
- }
-
- public static IEnumerable GetSuites(LibLinphoneTester tester)
- {
- return _unitTestDataSource.FillSuites(tester);
- }
-
- private IEnumerable FillSuites(LibLinphoneTester tester)
- {
- if (this.Suites.Count != 0) return this.Suites;
- for (int i = 0; i < tester.nbTestSuites(); i++)
- {
- UnitTestSuite suite = new UnitTestSuite(tester.testSuiteName(i));
- for (int j = 0; j < tester.nbTests(suite.Name); j++)
- {
- suite.Cases.Add(new UnitTestCase(suite, tester.testName(suite.Name, j)));
- }
- this.Suites.Add(suite);
- }
- return this.Suites;
- }
- }
-
- public sealed class UnitTestCaseStateToSymbolConverter : IValueConverter
- {
- object IValueConverter.Convert(object value, Type targetType, object parametr, string language)
- {
- if (!value.GetType().Equals(typeof(UnitTestCaseState)))
- {
- throw new ArgumentException("Only UnitTestCaseState is supported");
- }
- if (targetType.Equals(typeof(Symbol)))
- {
- switch ((UnitTestCaseState)value)
- {
- case UnitTestCaseState.Success:
- return Symbol.Like;
- case UnitTestCaseState.Failure:
- return Symbol.Dislike;
- case UnitTestCaseState.NotRun:
- default:
- return Symbol.Help;
- }
- }
- else
- {
- throw new ArgumentException(string.Format("Unsupported type {0}", targetType.FullName));
- }
- }
-
- object IValueConverter.ConvertBack(object value, Type targetType, object parameter, string language)
- {
- throw new NotImplementedException();
- }
- }
-
- public sealed class UnitTestCaseStateToSymbolColorConverter : IValueConverter
- {
- object IValueConverter.Convert(object value, Type targetType, object parameter, string language)
- {
- if (!value.GetType().Equals(typeof(UnitTestCaseState)))
- {
- throw new ArgumentException("Only UnitTestCaseState is supported");
- }
- if (targetType.Equals(typeof(Brush)))
- {
- switch ((UnitTestCaseState)value)
- {
- case UnitTestCaseState.Success:
- return new SolidColorBrush(Colors.ForestGreen);
- case UnitTestCaseState.Failure:
- return new SolidColorBrush(Colors.IndianRed);
- case UnitTestCaseState.NotRun:
- default:
- return new SolidColorBrush(Colors.LightGray);
- }
- }
- else
- {
- throw new ArgumentException(string.Format("Unsupported format {0}", targetType.FullName));
- }
- }
-
- object IValueConverter.ConvertBack(object value, Type targetType, object parameter, string language)
- {
- throw new NotImplementedException();
- }
- }
-
- public sealed class OutputTraceLevelToColorConverter : IValueConverter
- {
- object IValueConverter.Convert(object value, Type targetType, object parameter, string language)
- {
- if (!value.GetType().Equals(typeof(String)))
- {
- throw new ArgumentException("Only String is supported");
- }
- if (targetType.Equals(typeof(Brush)))
- {
- if ((String)value == "Error")
- {
- return new SolidColorBrush(Colors.IndianRed);
- }
- else if ((String)value == "Warning")
- {
- return new SolidColorBrush(Colors.Orange);
- }
- return new SolidColorBrush(Colors.Black);
- }
- else
- {
- throw new ArgumentException(string.Format("Unsupported format {0}", targetType.FullName));
- }
- }
-
- object IValueConverter.ConvertBack(object value, Type targetType, object parameter, string language)
- {
- throw new NotImplementedException();
- }
- }
-}
diff --git a/build/windows10/liblinphone-tester/MainPage.xaml b/build/windows10/liblinphone-tester/MainPage.xaml
deleted file mode 100644
index 2cdea219c..000000000
--- a/build/windows10/liblinphone-tester/MainPage.xaml
+++ /dev/null
@@ -1,128 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/build/windows10/liblinphone-tester/MainPage.xaml.cs b/build/windows10/liblinphone-tester/MainPage.xaml.cs
deleted file mode 100644
index 005f38891..000000000
--- a/build/windows10/liblinphone-tester/MainPage.xaml.cs
+++ /dev/null
@@ -1,198 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Collections.ObjectModel;
-using System.IO;
-using System.Linq;
-using System.Runtime.InteropServices.WindowsRuntime;
-using Windows.Foundation;
-using Windows.Foundation.Collections;
-using Windows.UI.Xaml;
-using Windows.UI.Xaml.Controls;
-using Windows.UI.Xaml.Controls.Primitives;
-using Windows.UI.Xaml.Data;
-using Windows.UI.Xaml.Input;
-using Windows.UI.Xaml.Media;
-using Windows.UI.Xaml.Navigation;
-using liblinphone_tester.DataModel;
-using liblinphone_tester_runtime_component;
-using System.Threading.Tasks;
-using Windows.UI.Core;
-using Windows.UI.Xaml.Documents;
-using Windows.Storage;
-
-// The Blank Page item template is documented at http://go.microsoft.com/fwlink/?LinkId=402352&clcid=0x409
-
-namespace liblinphone_tester
-{
- ///
- /// An empty page that can be used on its own or navigated to within a Frame.
- ///
- public sealed partial class MainPage : Page, OutputTraceListener
- {
- public MainPage()
- {
- this.InitializeComponent();
- }
-
- protected override void OnNavigatedTo(NavigationEventArgs e)
- {
- base.OnNavigatedTo(e);
- LibLinphoneTester.Instance.setWritableDirectory(ApplicationData.Current.LocalFolder);
- _suites = UnitTestDataSource.GetSuites(LibLinphoneTester.Instance);
- TryAutoLaunch();
- }
-
- public IEnumerable Suites
- {
- get { return _suites; }
- }
-
- private IEnumerable _suites;
-
- private void SelectAll_Click(object sender, RoutedEventArgs e)
- {
- bool allSelected = Suites.All(x => x.Selected);
- foreach (UnitTestSuite suite in Suites)
- {
- suite.Selected = !allSelected;
- }
- }
-
- private void RunSelected_Click(object sender, RoutedEventArgs e)
- {
- int nbCases = 0;
- foreach (UnitTestSuite suite in Suites)
- {
- foreach (UnitTestCase c in suite.Cases)
- {
- if (c.Selected) nbCases++;
- }
- }
- if (nbCases == 0) return;
-
- PrepareRun(nbCases);
-
- var tup = new Tuple, bool?>(Suites, Verbose.IsChecked);
- var t = Task.Factory.StartNew(async (object parameters) =>
- {
- var p = parameters as Tuple, bool?>;
- IEnumerable suites = p.Item1;
- bool verbose = p.Item2 != null ? (bool)p.Item2 : false;
- foreach (UnitTestSuite suite in suites)
- {
- foreach (UnitTestCase c in suite.Cases)
- {
- if (c.Selected)
- {
- await RunUnitTestCase(c, verbose);
- }
- }
- }
- }, tup);
- }
-
- private void RunSingle_Click(object sender, RoutedEventArgs e)
- {
- PrepareRun(1);
-
- var tup = new Tuple(DisplayedTestCase, Verbose.IsChecked);
- var t = Task.Factory.StartNew(async (object parameters) =>
- {
- var p = parameters as Tuple;
- UnitTestCase c = p.Item1;
- bool verbose = p.Item2 != null ? (bool)p.Item2 : false;
- await RunUnitTestCase(c, verbose);
- }, tup);
- }
-
- private void PrepareRun(int nbCases)
- {
- CommandBar.IsEnabled = false;
- ProgressIndicator.IsEnabled = true;
- ProgressIndicator.Minimum = 0;
- ProgressIndicator.Maximum = nbCases;
- ProgressIndicator.Value = 0;
- LibLinphoneTester.Instance.setOutputTraceListener(this);
- }
-
- private async Task RunUnitTestCase(UnitTestCase c, bool verbose)
- {
- UnitTestCaseState newState = UnitTestCaseState.NotRun;
- await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
- {
- RunningTestCase = c;
- });
- await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
- {
- c.Traces.Clear();
- });
- c.Dispatcher = Dispatcher;
- if (LibLinphoneTester.Instance.run(c.Suite.Name, c.Name, verbose))
- {
- newState = UnitTestCaseState.Failure;
- }
- else
- {
- newState = UnitTestCaseState.Success;
- }
- await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
- {
- c.State = newState;
- ProgressIndicator.Value += 1;
- if (ProgressIndicator.Value == ProgressIndicator.Maximum)
- {
- UnprepareRun();
- }
- });
- }
-
- private void UnprepareRun()
- {
- LibLinphoneTester.Instance.setOutputTraceListener(null);
- RunningTestCase = null;
- ProgressIndicator.IsEnabled = false;
- CommandBar.IsEnabled = true;
- }
-
- private void UnitTestCase_Click(object sender, ItemClickEventArgs e)
- {
- DisplayedTestCase = (e.ClickedItem as UnitTestCase);
- TestResultPage.DataContext = DisplayedTestCase;
- TestResultState.Visibility = Visibility.Visible;
- TestResultRun.Visibility = Visibility.Visible;
- }
-
- public async void outputTrace(String lev, String msg)
- {
- await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
- {
- if (RunningTestCase != null)
- {
- RunningTestCase.Traces.Add(new OutputTrace(lev, msg));
- }
- });
- }
-
- private async void TryAutoLaunch()
- {
- try
- {
- await ApplicationData.Current.LocalFolder.GetFileAsync("autolaunch");
- CommandBar.IsEnabled = false;
- ProgressIndicator.IsIndeterminate = true;
- ProgressIndicator.IsEnabled = true;
- LibLinphoneTester.Instance.runAllToXml();
- if (LibLinphoneTester.Instance.AsyncAction != null)
- {
- LibLinphoneTester.Instance.AsyncAction.Completed += (asyncInfo, asyncStatus) => {
- App.Current.Exit();
- };
- }
- }
- catch (Exception) { }
- }
-
- private UnitTestCase RunningTestCase;
- private UnitTestCase DisplayedTestCase;
- }
-}
diff --git a/build/windows10/liblinphone-tester/Package.appxmanifest b/build/windows10/liblinphone-tester/Package.appxmanifest
deleted file mode 100644
index f7f9cb6dd..000000000
--- a/build/windows10/liblinphone-tester/Package.appxmanifest
+++ /dev/null
@@ -1,49 +0,0 @@
-
-
-
-
-
-
-
-
-
- liblinphone-tester
- Belledonne Communications
- Assets\StoreLogo.png
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/build/windows10/liblinphone-tester/Properties/AssemblyInfo.cs b/build/windows10/liblinphone-tester/Properties/AssemblyInfo.cs
deleted file mode 100644
index d4b2a0c81..000000000
--- a/build/windows10/liblinphone-tester/Properties/AssemblyInfo.cs
+++ /dev/null
@@ -1,29 +0,0 @@
-using System.Reflection;
-using System.Runtime.CompilerServices;
-using System.Runtime.InteropServices;
-
-// General Information about an assembly is controlled through the following
-// set of attributes. Change these attribute values to modify the information
-// associated with an assembly.
-[assembly: AssemblyTitle("liblinphone-tester")]
-[assembly: AssemblyDescription("LibLinphone tester for Windows 10")]
-[assembly: AssemblyConfiguration("")]
-[assembly: AssemblyCompany("Belledonne Communications")]
-[assembly: AssemblyProduct("liblinphone-tester-windows10")]
-[assembly: AssemblyCopyright("Copyright © 2015 Belledonne Communications")]
-[assembly: AssemblyTrademark("")]
-[assembly: AssemblyCulture("")]
-
-// Version information for an assembly consists of the following four values:
-//
-// Major Version
-// Minor Version
-// Build Number
-// Revision
-//
-// You can specify all the values or you can default the Build and Revision Numbers
-// by using the '*' as shown below:
-// [assembly: AssemblyVersion("1.0.*")]
-[assembly: AssemblyVersion("1.0.0.0")]
-[assembly: AssemblyFileVersion("1.0.0.0")]
-[assembly: ComVisible(false)]
\ No newline at end of file
diff --git a/build/windows10/liblinphone-tester/Properties/Default.rd.xml b/build/windows10/liblinphone-tester/Properties/Default.rd.xml
deleted file mode 100644
index 80a960ce3..000000000
--- a/build/windows10/liblinphone-tester/Properties/Default.rd.xml
+++ /dev/null
@@ -1,31 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/build/windows10/liblinphone-tester/liblinphone-tester-runtime-component/liblinphone-tester-runtime-component.vcxproj b/build/windows10/liblinphone-tester/liblinphone-tester-runtime-component/liblinphone-tester-runtime-component.vcxproj
deleted file mode 100644
index a66c5e7ed..000000000
--- a/build/windows10/liblinphone-tester/liblinphone-tester-runtime-component/liblinphone-tester-runtime-component.vcxproj
+++ /dev/null
@@ -1,132 +0,0 @@
-
-
-
-
- Debug
- ARM
-
-
- Debug
- Win32
-
-
- Debug
- x64
-
-
- Release
- ARM
-
-
- Release
- Win32
-
-
- Release
- x64
-
-
-
-
-
-
-
-
-
-
- {acf5ea95-d647-4d0c-8f97-2cd9aae8a2e0}
-
-
- {74cad9d0-d8ae-4896-b71b-b2d9b48f30aa}
-
-
- {8c1bc968-c5c8-4d4b-9ef3-d6a065fc7c97}
-
-
- {6a18bbb9-08d1-41a8-be57-17fc992cc36f}
-
-
- {b84d5c3b-6de5-49c8-b3dd-5eb67b01a527}
-
-
- {266b769a-c04e-424c-9033-7209f0425bc0}
-
-
- {878cf9d3-9761-479e-a715-a1de9f99cb78}
-
-
- {a34f450d-392d-4660-9618-810bd695b3b0}
-
-
- {9eb3fe8d-2d91-4d29-a3bb-98ddb51d45b7}
-
-
-
- {1ce10f06-8fad-437f-b3d7-3b7a8909a190}
- WindowsRuntimeComponent
- liblinphone-tester-runtime-component
- liblinphone_tester_runtime_component
- en-US
- 14.0
- true
- Windows Store
- 10
- 10.0.10240.0
- 10.0.10069.0
-
-
-
- DynamicLibrary
- true
- v140
-
-
- DynamicLibrary
- false
- true
- v140
-
-
-
-
-
-
-
-
-
-
-
- false
-
-
-
- NotUsing
- IN_LINPHONE;_WINRT_DLL;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)
- $(WindowsSDK_WindowsMetadata);$(AdditionalUsingDirectories)
- /bigobj %(AdditionalOptions)
- 28204
- $(ProjectDir)..\..\..\..\coreapi;$(ProjectDir)..\..\..\..\tester\common;$(ProjectDir)..\..\..\..\mediastreamer2\include;$(ProjectDir)..\..\..\..\oRTP\include;%(AdditionalIncludeDirectories)
-
-
- Console
- false
-
-
-
-
- NotUsing
- IN_LINPHONE;_WINRT_DLL;NDEBUG;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)
- $(WindowsSDK_WindowsMetadata);$(AdditionalUsingDirectories)
- /bigobj %(AdditionalOptions)
- 28204
- $(ProjectDir)..\..\..\..\coreapi;$(ProjectDir)..\..\..\..\tester\common;$(ProjectDir)..\..\..\..\mediastreamer2\include;$(ProjectDir)..\..\..\..\oRTP\include;%(AdditionalIncludeDirectories)
-
-
- Console
- false
-
-
-
-
-
-
\ No newline at end of file
diff --git a/build/windows10/liblinphone-tester/liblinphone-tester-static/liblinphone-tester-static.vcxproj b/build/windows10/liblinphone-tester/liblinphone-tester-static/liblinphone-tester-static.vcxproj
deleted file mode 100644
index 80e57dedb..000000000
--- a/build/windows10/liblinphone-tester/liblinphone-tester-static/liblinphone-tester-static.vcxproj
+++ /dev/null
@@ -1,129 +0,0 @@
-
-
-
-
- Debug
- ARM
-
-
- Debug
- Win32
-
-
- Debug
- x64
-
-
- Release
- ARM
-
-
- Release
- Win32
-
-
- Release
- x64
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- {b6cdf482-7da3-43d4-9b12-70150106c191}
-
-
- {025e28a8-9dfb-4015-ad56-19896aa6cc9b}
-
-
- {88e3c241-eb6f-4c84-80dc-89b8961daf80}
-
-
- {2e56b851-9d8d-40e5-84bb-e4ee63b71d25}
-
-
- {c7139899-d8bc-48a3-a437-6844a8baabef}
-
-
-
- {9eb3fe8d-2d91-4d29-a3bb-98ddb51d45b7}
- StaticLibrary
- liblinphone-tester-static
- liblinphone_tester_static
- en-US
- 14.0
- true
- Windows Store
- 10
- 10.0.10240.0
- 10.0.10069.0
-
-
-
- StaticLibrary
- true
- v140
-
-
- StaticLibrary
- false
- true
- v140
-
-
-
-
-
-
-
-
-
-
-
- false
-
-
-
- NotUsing
- false
- true
- $(SolutionDir)$(Platform)\$(Configuration)\include;$(ProjectDir)..\..\..\..\tester;$(ProjectDir)..\..\..\..\tester\common;$(ProjectDir)..\..\liblinphone;$(ProjectDir)..\..\..\..\coreapi;$(ProjectDir)..\..\..\..\include;$(ProjectDir)..\..\..\..\mediastreamer2\include;$(ProjectDir)..\..\..\..\oRTP\include;$(ProjectDir)..\..\..\..\..\belle-sip\include;$(ProjectDir)..\..\..\..\..\sqlite;$(ProjectDir)..\..\..\..\..\zlib;$(ProjectDir)..\..\..\..\..\cunit\build\windows10\cunit\$(Platform)\$(Configuration);%(AdditionalIncludeDirectories)
- BC_CONFIG_FILE="config.h";IN_LINPHONE;MSG_STORAGE_ENABLED;VIDEO_ENABLED;HAVE_CU_GET_SUITE;HAVE_ZLIB;_CRT_SECURE_NO_WARNINGS;_WINSOCK_DEPRECATED_NO_WARNINGS;%(PreprocessorDefinitions)
-
-
- Console
- false
- false
-
-
-
-
-
-
\ No newline at end of file
diff --git a/build/windows10/liblinphone-tester/liblinphone-tester.csproj b/build/windows10/liblinphone-tester/liblinphone-tester.csproj
deleted file mode 100644
index 7cea98872..000000000
--- a/build/windows10/liblinphone-tester/liblinphone-tester.csproj
+++ /dev/null
@@ -1,313 +0,0 @@
-
-
-
-
- Debug
- x86
- {EC78E1D3-6FD8-4CAF-8D3F-6F4F97093BE5}
- AppContainerExe
- Properties
- liblinphone_tester
- liblinphone-tester
- en-US
- UAP
- 10.0.10240.0
- 10.0.10069.0
- 14
- true
- 512
- {A5A43C5B-DE2A-4C0C-9213-0A381AF9435A};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}
- liblinphone-tester_TemporaryKey.pfx
-
-
- true
- bin\ARM\Debug\
- DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP
- ;2008
- full
- ARM
- false
- prompt
- true
-
-
- bin\ARM\Release\
- TRACE;NETFX_CORE;WINDOWS_UWP
- true
- ;2008
- pdbonly
- ARM
- false
- prompt
- true
- true
-
-
- true
- bin\x64\Debug\
- DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP
- ;2008
- full
- x64
- false
- prompt
- true
-
-
- bin\x64\Release\
- TRACE;NETFX_CORE;WINDOWS_UWP
- true
- ;2008
- pdbonly
- x64
- false
- prompt
- true
- true
-
-
- true
- bin\x86\Debug\
- DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP
- ;2008
- full
- x86
- false
- prompt
- true
-
-
- bin\x86\Release\
- TRACE;NETFX_CORE;WINDOWS_UWP
- true
- ;2008
- pdbonly
- x86
- false
- prompt
- true
- true
-
-
-
-
- PreserveNewest
-
-
-
-
-
- App.xaml
-
-
-
- MainPage.xaml
-
-
-
-
-
- Designer
-
-
-
-
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
-
-
-
-
-
-
- MSBuild:Compile
- Designer
-
-
- MSBuild:Compile
- Designer
-
-
-
-
- {1ce10f06-8fad-437f-b3d7-3b7a8909a190}
- liblinphone-tester-runtime-component
-
-
-
-
- 14.0
-
-
-
- XCopy /I /Y $(ProjectDir)..\..\..\tester\tester_hosts $(ProjectDir)Assets\
-XCopy /I /Y $(ProjectDir)..\..\..\tester\certificates\altname $(ProjectDir)Assets\certificates\altname
-XCopy /I /Y $(ProjectDir)..\..\..\tester\certificates\cn $(ProjectDir)Assets\certificates\cn
-XCopy /I /Y $(ProjectDir)..\..\..\tester\images $(ProjectDir)Assets\images
-XCopy /I /Y $(ProjectDir)..\..\..\tester\rcfiles $(ProjectDir)Assets\rcfiles
-XCopy /I /Y $(ProjectDir)..\..\..\tester\sounds $(ProjectDir)Assets\sounds
-
-
-
\ No newline at end of file
diff --git a/build/windows10/liblinphone-tester/liblinphone-tester.sln b/build/windows10/liblinphone-tester/liblinphone-tester.sln
deleted file mode 100644
index 5c6955078..000000000
--- a/build/windows10/liblinphone-tester/liblinphone-tester.sln
+++ /dev/null
@@ -1,446 +0,0 @@
-
-Microsoft Visual Studio Solution File, Format Version 12.00
-# Visual Studio 14
-VisualStudioVersion = 14.0.22823.1
-MinimumVisualStudioVersion = 10.0.40219.1
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "liblinphone-tester", "liblinphone-tester.csproj", "{EC78E1D3-6FD8-4CAF-8D3F-6F4F97093BE5}"
-EndProject
-Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "liblinphone-tester-static", "liblinphone-tester-static\liblinphone-tester-static.vcxproj", "{9EB3FE8D-2D91-4D29-A3BB-98DDB51D45B7}"
-EndProject
-Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "liblinphone", "..\liblinphone\liblinphone.vcxproj", "{C7139899-D8BC-48A3-A437-6844A8BAABEF}"
-EndProject
-Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "mediastreamer2", "..\..\..\mediastreamer2\build\windows10\mediastreamer2\mediastreamer2.vcxproj", "{88E3C241-EB6F-4C84-80DC-89B8961DAF80}"
-EndProject
-Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ortp", "..\..\..\oRTP\build\windows10\ortp\ortp.vcxproj", "{2E56B851-9D8D-40E5-84BB-E4EE63B71D25}"
-EndProject
-Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "srtp", "..\..\..\..\srtp\build\windows10\srtp\srtp.vcxproj", "{59104E4F-A087-442E-ABD4-BCD2A1F0B0FE}"
-EndProject
-Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "xml2", "..\..\..\..\build\xml2\xml2.vcxproj", "{2B04DE79-4D33-4405-AC01-C89E0593A71D}"
-EndProject
-Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "polarssl", "..\..\..\..\polarssl\build\windows10\polarssl\polarssl.vcxproj", "{88768DD9-5110-4AC8-8B0E-41CD7713E1A2}"
-EndProject
-Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "speex", "..\..\..\..\speex\build\windows10\speex\speex.vcxproj", "{971DD379-1C2D-44D2-9285-FDA556C48176}"
-EndProject
-Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "speexdsp", "..\..\..\..\speex\build\windows10\speexdsp\speexdsp.vcxproj", "{104BF91B-8314-4328-A996-90B8DF6052AF}"
-EndProject
-Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "opus", "..\..\..\..\opus\build\windows10\opus\opus.vcxproj", "{81AF1025-E0EE-4AD6-988D-2EF162778693}"
-EndProject
-Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "bzrtp", "..\..\..\..\bzrtp\build\windows10\bzrtp\bzrtp.vcxproj", "{45C7723D-3107-4906-9633-F43ABE8A7147}"
-EndProject
-Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "gsm", "..\..\..\..\gsm\build\windows10\gsm\gsm.vcxproj", "{EF1103C7-8AAC-464B-BA31-86B87246FA72}"
-EndProject
-Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "belle-sip", "..\..\..\..\belle-sip\build\windows10\belle-sip\belle-sip.vcxproj", "{B6CDF482-7DA3-43D4-9B12-70150106C191}"
-EndProject
-Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "antlr3c", "..\..\..\..\antlr3\runtime\C\build\windows10\antlr3c\antlr3c.vcxproj", "{01CCCCC9-CA0C-4528-92BC-5B8BE1D02D6D}"
-EndProject
-Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "zlib", "..\..\..\..\zlib\build\windows10\zlib\zlib.vcxproj", "{A34F450D-392D-4660-9618-810BD695B3B0}"
-EndProject
-Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "sqlite", "..\..\..\..\build\sqlite\sqlite.vcxproj", "{74CAD9D0-D8AE-4896-B71B-B2D9B48F30AA}"
-EndProject
-Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "liblinphone-tester-runtime-component", "liblinphone-tester-runtime-component\liblinphone-tester-runtime-component.vcxproj", "{1CE10F06-8FAD-437F-B3D7-3B7A8909A190}"
-EndProject
-Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "cunit", "..\..\..\..\cunit\build\windows10\cunit\cunit.vcxproj", "{025E28A8-9DFB-4015-AD56-19896AA6CC9B}"
-EndProject
-Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libmsamr", "..\..\..\..\msamr\build\windows10\libmsamr\libmsamr.vcxproj", "{8C1BC968-C5C8-4D4B-9EF3-D6A065FC7C97}"
-EndProject
-Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libmsilbc", "..\..\..\..\msilbc\build\windows10\libmsilbc\libmsilbc.vcxproj", "{6A18BBB9-08D1-41A8-BE57-17FC992CC36F}"
-EndProject
-Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libmssilk", "..\..\..\..\mssilk\build\windows10\libmssilk\libmssilk.vcxproj", "{B84D5C3B-6DE5-49C8-B3DD-5EB67B01A527}"
-EndProject
-Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libmswasapi", "..\..\..\..\mswasapi\windows10\libmswasapi\libmswasapi.vcxproj", "{266B769A-C04E-424C-9033-7209F0425BC0}"
-EndProject
-Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libmswebrtc", "..\..\..\..\mswebrtc\build\windows10\libmswebrtc\libmswebrtc.vcxproj", "{878CF9D3-9761-479E-A715-A1DE9F99CB78}"
-EndProject
-Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ilbc", "..\..\..\..\libilbc-rfc3951\build\windows10\ilbc\ilbc.vcxproj", "{995B01AF-C568-453E-9E5F-8AE81FB79B4B}"
-EndProject
-Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "opencore_amrnb", "..\..\..\..\msamr\build\windows10\opencore_amrnb\opencore_amrnb.vcxproj", "{71A5F1C8-F76D-4297-95AA-75E1C967DC79}"
-EndProject
-Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "opencore_amrwb", "..\..\..\..\msamr\build\windows10\opencore_amrwb\opencore_amrwb.vcxproj", "{3CC91899-3E98-49FD-BED5-FA290A9A5C8E}"
-EndProject
-Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "vo-amrwbenc", "..\..\..\..\msamr\build\windows10\vo-amrwbenc\vo-amrwbenc.vcxproj", "{D829672F-3775-4718-A991-1ABC42CBA67C}"
-EndProject
-Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "webrtc", "..\..\..\..\mswebrtc\webrtc\build\windows10\webrtc\webrtc.vcxproj", "{C5895B75-BDCF-406C-B803-9CB954E90F0C}"
-EndProject
-Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libmsbcg729", "..\..\..\..\bcg729\build\windows10\libmsbcg729\libmsbcg729.vcxproj", "{ACF5EA95-D647-4D0C-8F97-2CD9AAE8A2E0}"
-EndProject
-Global
- GlobalSection(SolutionConfigurationPlatforms) = preSolution
- Debug|ARM = Debug|ARM
- Debug|x64 = Debug|x64
- Debug|x86 = Debug|x86
- Release|ARM = Release|ARM
- Release|x64 = Release|x64
- Release|x86 = Release|x86
- EndGlobalSection
- GlobalSection(ProjectConfigurationPlatforms) = postSolution
- {EC78E1D3-6FD8-4CAF-8D3F-6F4F97093BE5}.Debug|ARM.ActiveCfg = Debug|ARM
- {EC78E1D3-6FD8-4CAF-8D3F-6F4F97093BE5}.Debug|ARM.Build.0 = Debug|ARM
- {EC78E1D3-6FD8-4CAF-8D3F-6F4F97093BE5}.Debug|ARM.Deploy.0 = Debug|ARM
- {EC78E1D3-6FD8-4CAF-8D3F-6F4F97093BE5}.Debug|x64.ActiveCfg = Debug|x64
- {EC78E1D3-6FD8-4CAF-8D3F-6F4F97093BE5}.Debug|x64.Build.0 = Debug|x64
- {EC78E1D3-6FD8-4CAF-8D3F-6F4F97093BE5}.Debug|x64.Deploy.0 = Debug|x64
- {EC78E1D3-6FD8-4CAF-8D3F-6F4F97093BE5}.Debug|x86.ActiveCfg = Debug|x86
- {EC78E1D3-6FD8-4CAF-8D3F-6F4F97093BE5}.Debug|x86.Build.0 = Debug|x86
- {EC78E1D3-6FD8-4CAF-8D3F-6F4F97093BE5}.Debug|x86.Deploy.0 = Debug|x86
- {EC78E1D3-6FD8-4CAF-8D3F-6F4F97093BE5}.Release|ARM.ActiveCfg = Release|ARM
- {EC78E1D3-6FD8-4CAF-8D3F-6F4F97093BE5}.Release|ARM.Build.0 = Release|ARM
- {EC78E1D3-6FD8-4CAF-8D3F-6F4F97093BE5}.Release|ARM.Deploy.0 = Release|ARM
- {EC78E1D3-6FD8-4CAF-8D3F-6F4F97093BE5}.Release|x64.ActiveCfg = Release|x64
- {EC78E1D3-6FD8-4CAF-8D3F-6F4F97093BE5}.Release|x64.Build.0 = Release|x64
- {EC78E1D3-6FD8-4CAF-8D3F-6F4F97093BE5}.Release|x64.Deploy.0 = Release|x64
- {EC78E1D3-6FD8-4CAF-8D3F-6F4F97093BE5}.Release|x86.ActiveCfg = Release|x86
- {EC78E1D3-6FD8-4CAF-8D3F-6F4F97093BE5}.Release|x86.Build.0 = Release|x86
- {EC78E1D3-6FD8-4CAF-8D3F-6F4F97093BE5}.Release|x86.Deploy.0 = Release|x86
- {9EB3FE8D-2D91-4D29-A3BB-98DDB51D45B7}.Debug|ARM.ActiveCfg = Debug|ARM
- {9EB3FE8D-2D91-4D29-A3BB-98DDB51D45B7}.Debug|ARM.Build.0 = Debug|ARM
- {9EB3FE8D-2D91-4D29-A3BB-98DDB51D45B7}.Debug|x64.ActiveCfg = Debug|x64
- {9EB3FE8D-2D91-4D29-A3BB-98DDB51D45B7}.Debug|x64.Build.0 = Debug|x64
- {9EB3FE8D-2D91-4D29-A3BB-98DDB51D45B7}.Debug|x86.ActiveCfg = Debug|Win32
- {9EB3FE8D-2D91-4D29-A3BB-98DDB51D45B7}.Debug|x86.Build.0 = Debug|Win32
- {9EB3FE8D-2D91-4D29-A3BB-98DDB51D45B7}.Release|ARM.ActiveCfg = Release|ARM
- {9EB3FE8D-2D91-4D29-A3BB-98DDB51D45B7}.Release|ARM.Build.0 = Release|ARM
- {9EB3FE8D-2D91-4D29-A3BB-98DDB51D45B7}.Release|x64.ActiveCfg = Release|x64
- {9EB3FE8D-2D91-4D29-A3BB-98DDB51D45B7}.Release|x64.Build.0 = Release|x64
- {9EB3FE8D-2D91-4D29-A3BB-98DDB51D45B7}.Release|x86.ActiveCfg = Release|Win32
- {9EB3FE8D-2D91-4D29-A3BB-98DDB51D45B7}.Release|x86.Build.0 = Release|Win32
- {C7139899-D8BC-48A3-A437-6844A8BAABEF}.Debug|ARM.ActiveCfg = Debug|ARM
- {C7139899-D8BC-48A3-A437-6844A8BAABEF}.Debug|ARM.Build.0 = Debug|ARM
- {C7139899-D8BC-48A3-A437-6844A8BAABEF}.Debug|x64.ActiveCfg = Debug|x64
- {C7139899-D8BC-48A3-A437-6844A8BAABEF}.Debug|x64.Build.0 = Debug|x64
- {C7139899-D8BC-48A3-A437-6844A8BAABEF}.Debug|x86.ActiveCfg = Debug|Win32
- {C7139899-D8BC-48A3-A437-6844A8BAABEF}.Debug|x86.Build.0 = Debug|Win32
- {C7139899-D8BC-48A3-A437-6844A8BAABEF}.Release|ARM.ActiveCfg = Release|ARM
- {C7139899-D8BC-48A3-A437-6844A8BAABEF}.Release|ARM.Build.0 = Release|ARM
- {C7139899-D8BC-48A3-A437-6844A8BAABEF}.Release|x64.ActiveCfg = Release|x64
- {C7139899-D8BC-48A3-A437-6844A8BAABEF}.Release|x64.Build.0 = Release|x64
- {C7139899-D8BC-48A3-A437-6844A8BAABEF}.Release|x86.ActiveCfg = Release|Win32
- {C7139899-D8BC-48A3-A437-6844A8BAABEF}.Release|x86.Build.0 = Release|Win32
- {88E3C241-EB6F-4C84-80DC-89B8961DAF80}.Debug|ARM.ActiveCfg = Debug|ARM
- {88E3C241-EB6F-4C84-80DC-89B8961DAF80}.Debug|ARM.Build.0 = Debug|ARM
- {88E3C241-EB6F-4C84-80DC-89B8961DAF80}.Debug|x64.ActiveCfg = Debug|x64
- {88E3C241-EB6F-4C84-80DC-89B8961DAF80}.Debug|x64.Build.0 = Debug|x64
- {88E3C241-EB6F-4C84-80DC-89B8961DAF80}.Debug|x86.ActiveCfg = Debug|Win32
- {88E3C241-EB6F-4C84-80DC-89B8961DAF80}.Debug|x86.Build.0 = Debug|Win32
- {88E3C241-EB6F-4C84-80DC-89B8961DAF80}.Release|ARM.ActiveCfg = Release|ARM
- {88E3C241-EB6F-4C84-80DC-89B8961DAF80}.Release|ARM.Build.0 = Release|ARM
- {88E3C241-EB6F-4C84-80DC-89B8961DAF80}.Release|x64.ActiveCfg = Release|x64
- {88E3C241-EB6F-4C84-80DC-89B8961DAF80}.Release|x64.Build.0 = Release|x64
- {88E3C241-EB6F-4C84-80DC-89B8961DAF80}.Release|x86.ActiveCfg = Release|Win32
- {88E3C241-EB6F-4C84-80DC-89B8961DAF80}.Release|x86.Build.0 = Release|Win32
- {2E56B851-9D8D-40E5-84BB-E4EE63B71D25}.Debug|ARM.ActiveCfg = Debug|ARM
- {2E56B851-9D8D-40E5-84BB-E4EE63B71D25}.Debug|ARM.Build.0 = Debug|ARM
- {2E56B851-9D8D-40E5-84BB-E4EE63B71D25}.Debug|x64.ActiveCfg = Debug|x64
- {2E56B851-9D8D-40E5-84BB-E4EE63B71D25}.Debug|x64.Build.0 = Debug|x64
- {2E56B851-9D8D-40E5-84BB-E4EE63B71D25}.Debug|x86.ActiveCfg = Debug|Win32
- {2E56B851-9D8D-40E5-84BB-E4EE63B71D25}.Debug|x86.Build.0 = Debug|Win32
- {2E56B851-9D8D-40E5-84BB-E4EE63B71D25}.Release|ARM.ActiveCfg = Release|ARM
- {2E56B851-9D8D-40E5-84BB-E4EE63B71D25}.Release|ARM.Build.0 = Release|ARM
- {2E56B851-9D8D-40E5-84BB-E4EE63B71D25}.Release|x64.ActiveCfg = Release|x64
- {2E56B851-9D8D-40E5-84BB-E4EE63B71D25}.Release|x64.Build.0 = Release|x64
- {2E56B851-9D8D-40E5-84BB-E4EE63B71D25}.Release|x86.ActiveCfg = Release|Win32
- {2E56B851-9D8D-40E5-84BB-E4EE63B71D25}.Release|x86.Build.0 = Release|Win32
- {59104E4F-A087-442E-ABD4-BCD2A1F0B0FE}.Debug|ARM.ActiveCfg = Debug|ARM
- {59104E4F-A087-442E-ABD4-BCD2A1F0B0FE}.Debug|ARM.Build.0 = Debug|ARM
- {59104E4F-A087-442E-ABD4-BCD2A1F0B0FE}.Debug|x64.ActiveCfg = Debug|x64
- {59104E4F-A087-442E-ABD4-BCD2A1F0B0FE}.Debug|x64.Build.0 = Debug|x64
- {59104E4F-A087-442E-ABD4-BCD2A1F0B0FE}.Debug|x86.ActiveCfg = Debug|Win32
- {59104E4F-A087-442E-ABD4-BCD2A1F0B0FE}.Debug|x86.Build.0 = Debug|Win32
- {59104E4F-A087-442E-ABD4-BCD2A1F0B0FE}.Release|ARM.ActiveCfg = Release|ARM
- {59104E4F-A087-442E-ABD4-BCD2A1F0B0FE}.Release|ARM.Build.0 = Release|ARM
- {59104E4F-A087-442E-ABD4-BCD2A1F0B0FE}.Release|x64.ActiveCfg = Release|x64
- {59104E4F-A087-442E-ABD4-BCD2A1F0B0FE}.Release|x64.Build.0 = Release|x64
- {59104E4F-A087-442E-ABD4-BCD2A1F0B0FE}.Release|x86.ActiveCfg = Release|Win32
- {59104E4F-A087-442E-ABD4-BCD2A1F0B0FE}.Release|x86.Build.0 = Release|Win32
- {2B04DE79-4D33-4405-AC01-C89E0593A71D}.Debug|ARM.ActiveCfg = Debug|ARM
- {2B04DE79-4D33-4405-AC01-C89E0593A71D}.Debug|ARM.Build.0 = Debug|ARM
- {2B04DE79-4D33-4405-AC01-C89E0593A71D}.Debug|x64.ActiveCfg = Debug|x64
- {2B04DE79-4D33-4405-AC01-C89E0593A71D}.Debug|x64.Build.0 = Debug|x64
- {2B04DE79-4D33-4405-AC01-C89E0593A71D}.Debug|x86.ActiveCfg = Debug|Win32
- {2B04DE79-4D33-4405-AC01-C89E0593A71D}.Debug|x86.Build.0 = Debug|Win32
- {2B04DE79-4D33-4405-AC01-C89E0593A71D}.Release|ARM.ActiveCfg = Release|ARM
- {2B04DE79-4D33-4405-AC01-C89E0593A71D}.Release|ARM.Build.0 = Release|ARM
- {2B04DE79-4D33-4405-AC01-C89E0593A71D}.Release|x64.ActiveCfg = Release|x64
- {2B04DE79-4D33-4405-AC01-C89E0593A71D}.Release|x64.Build.0 = Release|x64
- {2B04DE79-4D33-4405-AC01-C89E0593A71D}.Release|x86.ActiveCfg = Release|Win32
- {2B04DE79-4D33-4405-AC01-C89E0593A71D}.Release|x86.Build.0 = Release|Win32
- {88768DD9-5110-4AC8-8B0E-41CD7713E1A2}.Debug|ARM.ActiveCfg = Debug|ARM
- {88768DD9-5110-4AC8-8B0E-41CD7713E1A2}.Debug|ARM.Build.0 = Debug|ARM
- {88768DD9-5110-4AC8-8B0E-41CD7713E1A2}.Debug|x64.ActiveCfg = Debug|x64
- {88768DD9-5110-4AC8-8B0E-41CD7713E1A2}.Debug|x64.Build.0 = Debug|x64
- {88768DD9-5110-4AC8-8B0E-41CD7713E1A2}.Debug|x86.ActiveCfg = Debug|Win32
- {88768DD9-5110-4AC8-8B0E-41CD7713E1A2}.Debug|x86.Build.0 = Debug|Win32
- {88768DD9-5110-4AC8-8B0E-41CD7713E1A2}.Release|ARM.ActiveCfg = Release|ARM
- {88768DD9-5110-4AC8-8B0E-41CD7713E1A2}.Release|ARM.Build.0 = Release|ARM
- {88768DD9-5110-4AC8-8B0E-41CD7713E1A2}.Release|x64.ActiveCfg = Release|x64
- {88768DD9-5110-4AC8-8B0E-41CD7713E1A2}.Release|x64.Build.0 = Release|x64
- {88768DD9-5110-4AC8-8B0E-41CD7713E1A2}.Release|x86.ActiveCfg = Release|Win32
- {88768DD9-5110-4AC8-8B0E-41CD7713E1A2}.Release|x86.Build.0 = Release|Win32
- {971DD379-1C2D-44D2-9285-FDA556C48176}.Debug|ARM.ActiveCfg = Debug|ARM
- {971DD379-1C2D-44D2-9285-FDA556C48176}.Debug|ARM.Build.0 = Debug|ARM
- {971DD379-1C2D-44D2-9285-FDA556C48176}.Debug|x64.ActiveCfg = Debug|x64
- {971DD379-1C2D-44D2-9285-FDA556C48176}.Debug|x64.Build.0 = Debug|x64
- {971DD379-1C2D-44D2-9285-FDA556C48176}.Debug|x86.ActiveCfg = Debug|Win32
- {971DD379-1C2D-44D2-9285-FDA556C48176}.Debug|x86.Build.0 = Debug|Win32
- {971DD379-1C2D-44D2-9285-FDA556C48176}.Release|ARM.ActiveCfg = Release|ARM
- {971DD379-1C2D-44D2-9285-FDA556C48176}.Release|ARM.Build.0 = Release|ARM
- {971DD379-1C2D-44D2-9285-FDA556C48176}.Release|x64.ActiveCfg = Release|x64
- {971DD379-1C2D-44D2-9285-FDA556C48176}.Release|x64.Build.0 = Release|x64
- {971DD379-1C2D-44D2-9285-FDA556C48176}.Release|x86.ActiveCfg = Release|Win32
- {971DD379-1C2D-44D2-9285-FDA556C48176}.Release|x86.Build.0 = Release|Win32
- {104BF91B-8314-4328-A996-90B8DF6052AF}.Debug|ARM.ActiveCfg = Debug|ARM
- {104BF91B-8314-4328-A996-90B8DF6052AF}.Debug|ARM.Build.0 = Debug|ARM
- {104BF91B-8314-4328-A996-90B8DF6052AF}.Debug|x64.ActiveCfg = Debug|x64
- {104BF91B-8314-4328-A996-90B8DF6052AF}.Debug|x64.Build.0 = Debug|x64
- {104BF91B-8314-4328-A996-90B8DF6052AF}.Debug|x86.ActiveCfg = Debug|Win32
- {104BF91B-8314-4328-A996-90B8DF6052AF}.Debug|x86.Build.0 = Debug|Win32
- {104BF91B-8314-4328-A996-90B8DF6052AF}.Release|ARM.ActiveCfg = Release|ARM
- {104BF91B-8314-4328-A996-90B8DF6052AF}.Release|ARM.Build.0 = Release|ARM
- {104BF91B-8314-4328-A996-90B8DF6052AF}.Release|x64.ActiveCfg = Release|x64
- {104BF91B-8314-4328-A996-90B8DF6052AF}.Release|x64.Build.0 = Release|x64
- {104BF91B-8314-4328-A996-90B8DF6052AF}.Release|x86.ActiveCfg = Release|Win32
- {104BF91B-8314-4328-A996-90B8DF6052AF}.Release|x86.Build.0 = Release|Win32
- {81AF1025-E0EE-4AD6-988D-2EF162778693}.Debug|ARM.ActiveCfg = Debug|ARM
- {81AF1025-E0EE-4AD6-988D-2EF162778693}.Debug|ARM.Build.0 = Debug|ARM
- {81AF1025-E0EE-4AD6-988D-2EF162778693}.Debug|x64.ActiveCfg = Debug|x64
- {81AF1025-E0EE-4AD6-988D-2EF162778693}.Debug|x64.Build.0 = Debug|x64
- {81AF1025-E0EE-4AD6-988D-2EF162778693}.Debug|x86.ActiveCfg = Debug|Win32
- {81AF1025-E0EE-4AD6-988D-2EF162778693}.Debug|x86.Build.0 = Debug|Win32
- {81AF1025-E0EE-4AD6-988D-2EF162778693}.Release|ARM.ActiveCfg = Release|ARM
- {81AF1025-E0EE-4AD6-988D-2EF162778693}.Release|ARM.Build.0 = Release|ARM
- {81AF1025-E0EE-4AD6-988D-2EF162778693}.Release|x64.ActiveCfg = Release|x64
- {81AF1025-E0EE-4AD6-988D-2EF162778693}.Release|x64.Build.0 = Release|x64
- {81AF1025-E0EE-4AD6-988D-2EF162778693}.Release|x86.ActiveCfg = Release|Win32
- {81AF1025-E0EE-4AD6-988D-2EF162778693}.Release|x86.Build.0 = Release|Win32
- {45C7723D-3107-4906-9633-F43ABE8A7147}.Debug|ARM.ActiveCfg = Debug|ARM
- {45C7723D-3107-4906-9633-F43ABE8A7147}.Debug|ARM.Build.0 = Debug|ARM
- {45C7723D-3107-4906-9633-F43ABE8A7147}.Debug|x64.ActiveCfg = Debug|x64
- {45C7723D-3107-4906-9633-F43ABE8A7147}.Debug|x64.Build.0 = Debug|x64
- {45C7723D-3107-4906-9633-F43ABE8A7147}.Debug|x86.ActiveCfg = Debug|Win32
- {45C7723D-3107-4906-9633-F43ABE8A7147}.Debug|x86.Build.0 = Debug|Win32
- {45C7723D-3107-4906-9633-F43ABE8A7147}.Release|ARM.ActiveCfg = Release|ARM
- {45C7723D-3107-4906-9633-F43ABE8A7147}.Release|ARM.Build.0 = Release|ARM
- {45C7723D-3107-4906-9633-F43ABE8A7147}.Release|x64.ActiveCfg = Release|x64
- {45C7723D-3107-4906-9633-F43ABE8A7147}.Release|x64.Build.0 = Release|x64
- {45C7723D-3107-4906-9633-F43ABE8A7147}.Release|x86.ActiveCfg = Release|Win32
- {45C7723D-3107-4906-9633-F43ABE8A7147}.Release|x86.Build.0 = Release|Win32
- {EF1103C7-8AAC-464B-BA31-86B87246FA72}.Debug|ARM.ActiveCfg = Debug|ARM
- {EF1103C7-8AAC-464B-BA31-86B87246FA72}.Debug|ARM.Build.0 = Debug|ARM
- {EF1103C7-8AAC-464B-BA31-86B87246FA72}.Debug|x64.ActiveCfg = Debug|x64
- {EF1103C7-8AAC-464B-BA31-86B87246FA72}.Debug|x64.Build.0 = Debug|x64
- {EF1103C7-8AAC-464B-BA31-86B87246FA72}.Debug|x86.ActiveCfg = Debug|Win32
- {EF1103C7-8AAC-464B-BA31-86B87246FA72}.Debug|x86.Build.0 = Debug|Win32
- {EF1103C7-8AAC-464B-BA31-86B87246FA72}.Release|ARM.ActiveCfg = Release|ARM
- {EF1103C7-8AAC-464B-BA31-86B87246FA72}.Release|ARM.Build.0 = Release|ARM
- {EF1103C7-8AAC-464B-BA31-86B87246FA72}.Release|x64.ActiveCfg = Release|x64
- {EF1103C7-8AAC-464B-BA31-86B87246FA72}.Release|x64.Build.0 = Release|x64
- {EF1103C7-8AAC-464B-BA31-86B87246FA72}.Release|x86.ActiveCfg = Release|Win32
- {EF1103C7-8AAC-464B-BA31-86B87246FA72}.Release|x86.Build.0 = Release|Win32
- {B6CDF482-7DA3-43D4-9B12-70150106C191}.Debug|ARM.ActiveCfg = Debug|ARM
- {B6CDF482-7DA3-43D4-9B12-70150106C191}.Debug|ARM.Build.0 = Debug|ARM
- {B6CDF482-7DA3-43D4-9B12-70150106C191}.Debug|x64.ActiveCfg = Debug|x64
- {B6CDF482-7DA3-43D4-9B12-70150106C191}.Debug|x64.Build.0 = Debug|x64
- {B6CDF482-7DA3-43D4-9B12-70150106C191}.Debug|x86.ActiveCfg = Debug|Win32
- {B6CDF482-7DA3-43D4-9B12-70150106C191}.Debug|x86.Build.0 = Debug|Win32
- {B6CDF482-7DA3-43D4-9B12-70150106C191}.Release|ARM.ActiveCfg = Release|ARM
- {B6CDF482-7DA3-43D4-9B12-70150106C191}.Release|ARM.Build.0 = Release|ARM
- {B6CDF482-7DA3-43D4-9B12-70150106C191}.Release|x64.ActiveCfg = Release|x64
- {B6CDF482-7DA3-43D4-9B12-70150106C191}.Release|x64.Build.0 = Release|x64
- {B6CDF482-7DA3-43D4-9B12-70150106C191}.Release|x86.ActiveCfg = Release|Win32
- {B6CDF482-7DA3-43D4-9B12-70150106C191}.Release|x86.Build.0 = Release|Win32
- {01CCCCC9-CA0C-4528-92BC-5B8BE1D02D6D}.Debug|ARM.ActiveCfg = Debug|ARM
- {01CCCCC9-CA0C-4528-92BC-5B8BE1D02D6D}.Debug|ARM.Build.0 = Debug|ARM
- {01CCCCC9-CA0C-4528-92BC-5B8BE1D02D6D}.Debug|x64.ActiveCfg = Debug|x64
- {01CCCCC9-CA0C-4528-92BC-5B8BE1D02D6D}.Debug|x64.Build.0 = Debug|x64
- {01CCCCC9-CA0C-4528-92BC-5B8BE1D02D6D}.Debug|x86.ActiveCfg = Debug|Win32
- {01CCCCC9-CA0C-4528-92BC-5B8BE1D02D6D}.Debug|x86.Build.0 = Debug|Win32
- {01CCCCC9-CA0C-4528-92BC-5B8BE1D02D6D}.Release|ARM.ActiveCfg = Release|ARM
- {01CCCCC9-CA0C-4528-92BC-5B8BE1D02D6D}.Release|ARM.Build.0 = Release|ARM
- {01CCCCC9-CA0C-4528-92BC-5B8BE1D02D6D}.Release|x64.ActiveCfg = Release|x64
- {01CCCCC9-CA0C-4528-92BC-5B8BE1D02D6D}.Release|x64.Build.0 = Release|x64
- {01CCCCC9-CA0C-4528-92BC-5B8BE1D02D6D}.Release|x86.ActiveCfg = Release|Win32
- {01CCCCC9-CA0C-4528-92BC-5B8BE1D02D6D}.Release|x86.Build.0 = Release|Win32
- {A34F450D-392D-4660-9618-810BD695B3B0}.Debug|ARM.ActiveCfg = Debug|ARM
- {A34F450D-392D-4660-9618-810BD695B3B0}.Debug|ARM.Build.0 = Debug|ARM
- {A34F450D-392D-4660-9618-810BD695B3B0}.Debug|x64.ActiveCfg = Debug|x64
- {A34F450D-392D-4660-9618-810BD695B3B0}.Debug|x64.Build.0 = Debug|x64
- {A34F450D-392D-4660-9618-810BD695B3B0}.Debug|x86.ActiveCfg = Debug|Win32
- {A34F450D-392D-4660-9618-810BD695B3B0}.Debug|x86.Build.0 = Debug|Win32
- {A34F450D-392D-4660-9618-810BD695B3B0}.Release|ARM.ActiveCfg = Release|ARM
- {A34F450D-392D-4660-9618-810BD695B3B0}.Release|ARM.Build.0 = Release|ARM
- {A34F450D-392D-4660-9618-810BD695B3B0}.Release|x64.ActiveCfg = Release|x64
- {A34F450D-392D-4660-9618-810BD695B3B0}.Release|x64.Build.0 = Release|x64
- {A34F450D-392D-4660-9618-810BD695B3B0}.Release|x86.ActiveCfg = Release|Win32
- {A34F450D-392D-4660-9618-810BD695B3B0}.Release|x86.Build.0 = Release|Win32
- {74CAD9D0-D8AE-4896-B71B-B2D9B48F30AA}.Debug|ARM.ActiveCfg = Debug|ARM
- {74CAD9D0-D8AE-4896-B71B-B2D9B48F30AA}.Debug|ARM.Build.0 = Debug|ARM
- {74CAD9D0-D8AE-4896-B71B-B2D9B48F30AA}.Debug|x64.ActiveCfg = Debug|x64
- {74CAD9D0-D8AE-4896-B71B-B2D9B48F30AA}.Debug|x64.Build.0 = Debug|x64
- {74CAD9D0-D8AE-4896-B71B-B2D9B48F30AA}.Debug|x86.ActiveCfg = Debug|Win32
- {74CAD9D0-D8AE-4896-B71B-B2D9B48F30AA}.Debug|x86.Build.0 = Debug|Win32
- {74CAD9D0-D8AE-4896-B71B-B2D9B48F30AA}.Release|ARM.ActiveCfg = Release|ARM
- {74CAD9D0-D8AE-4896-B71B-B2D9B48F30AA}.Release|ARM.Build.0 = Release|ARM
- {74CAD9D0-D8AE-4896-B71B-B2D9B48F30AA}.Release|x64.ActiveCfg = Release|x64
- {74CAD9D0-D8AE-4896-B71B-B2D9B48F30AA}.Release|x64.Build.0 = Release|x64
- {74CAD9D0-D8AE-4896-B71B-B2D9B48F30AA}.Release|x86.ActiveCfg = Release|Win32
- {74CAD9D0-D8AE-4896-B71B-B2D9B48F30AA}.Release|x86.Build.0 = Release|Win32
- {1CE10F06-8FAD-437F-B3D7-3B7A8909A190}.Debug|ARM.ActiveCfg = Debug|ARM
- {1CE10F06-8FAD-437F-B3D7-3B7A8909A190}.Debug|ARM.Build.0 = Debug|ARM
- {1CE10F06-8FAD-437F-B3D7-3B7A8909A190}.Debug|x64.ActiveCfg = Debug|x64
- {1CE10F06-8FAD-437F-B3D7-3B7A8909A190}.Debug|x64.Build.0 = Debug|x64
- {1CE10F06-8FAD-437F-B3D7-3B7A8909A190}.Debug|x86.ActiveCfg = Debug|Win32
- {1CE10F06-8FAD-437F-B3D7-3B7A8909A190}.Debug|x86.Build.0 = Debug|Win32
- {1CE10F06-8FAD-437F-B3D7-3B7A8909A190}.Release|ARM.ActiveCfg = Release|ARM
- {1CE10F06-8FAD-437F-B3D7-3B7A8909A190}.Release|ARM.Build.0 = Release|ARM
- {1CE10F06-8FAD-437F-B3D7-3B7A8909A190}.Release|x64.ActiveCfg = Release|x64
- {1CE10F06-8FAD-437F-B3D7-3B7A8909A190}.Release|x64.Build.0 = Release|x64
- {1CE10F06-8FAD-437F-B3D7-3B7A8909A190}.Release|x86.ActiveCfg = Release|Win32
- {1CE10F06-8FAD-437F-B3D7-3B7A8909A190}.Release|x86.Build.0 = Release|Win32
- {025E28A8-9DFB-4015-AD56-19896AA6CC9B}.Debug|ARM.ActiveCfg = Debug|ARM
- {025E28A8-9DFB-4015-AD56-19896AA6CC9B}.Debug|ARM.Build.0 = Debug|ARM
- {025E28A8-9DFB-4015-AD56-19896AA6CC9B}.Debug|x64.ActiveCfg = Debug|x64
- {025E28A8-9DFB-4015-AD56-19896AA6CC9B}.Debug|x64.Build.0 = Debug|x64
- {025E28A8-9DFB-4015-AD56-19896AA6CC9B}.Debug|x86.ActiveCfg = Debug|Win32
- {025E28A8-9DFB-4015-AD56-19896AA6CC9B}.Debug|x86.Build.0 = Debug|Win32
- {025E28A8-9DFB-4015-AD56-19896AA6CC9B}.Release|ARM.ActiveCfg = Release|ARM
- {025E28A8-9DFB-4015-AD56-19896AA6CC9B}.Release|ARM.Build.0 = Release|ARM
- {025E28A8-9DFB-4015-AD56-19896AA6CC9B}.Release|x64.ActiveCfg = Release|x64
- {025E28A8-9DFB-4015-AD56-19896AA6CC9B}.Release|x64.Build.0 = Release|x64
- {025E28A8-9DFB-4015-AD56-19896AA6CC9B}.Release|x86.ActiveCfg = Release|Win32
- {025E28A8-9DFB-4015-AD56-19896AA6CC9B}.Release|x86.Build.0 = Release|Win32
- {8C1BC968-C5C8-4D4B-9EF3-D6A065FC7C97}.Debug|ARM.ActiveCfg = Debug|ARM
- {8C1BC968-C5C8-4D4B-9EF3-D6A065FC7C97}.Debug|ARM.Build.0 = Debug|ARM
- {8C1BC968-C5C8-4D4B-9EF3-D6A065FC7C97}.Debug|x64.ActiveCfg = Debug|x64
- {8C1BC968-C5C8-4D4B-9EF3-D6A065FC7C97}.Debug|x64.Build.0 = Debug|x64
- {8C1BC968-C5C8-4D4B-9EF3-D6A065FC7C97}.Debug|x86.ActiveCfg = Debug|Win32
- {8C1BC968-C5C8-4D4B-9EF3-D6A065FC7C97}.Debug|x86.Build.0 = Debug|Win32
- {8C1BC968-C5C8-4D4B-9EF3-D6A065FC7C97}.Release|ARM.ActiveCfg = Release|ARM
- {8C1BC968-C5C8-4D4B-9EF3-D6A065FC7C97}.Release|ARM.Build.0 = Release|ARM
- {8C1BC968-C5C8-4D4B-9EF3-D6A065FC7C97}.Release|x64.ActiveCfg = Release|x64
- {8C1BC968-C5C8-4D4B-9EF3-D6A065FC7C97}.Release|x64.Build.0 = Release|x64
- {8C1BC968-C5C8-4D4B-9EF3-D6A065FC7C97}.Release|x86.ActiveCfg = Release|Win32
- {8C1BC968-C5C8-4D4B-9EF3-D6A065FC7C97}.Release|x86.Build.0 = Release|Win32
- {6A18BBB9-08D1-41A8-BE57-17FC992CC36F}.Debug|ARM.ActiveCfg = Debug|ARM
- {6A18BBB9-08D1-41A8-BE57-17FC992CC36F}.Debug|ARM.Build.0 = Debug|ARM
- {6A18BBB9-08D1-41A8-BE57-17FC992CC36F}.Debug|x64.ActiveCfg = Debug|x64
- {6A18BBB9-08D1-41A8-BE57-17FC992CC36F}.Debug|x64.Build.0 = Debug|x64
- {6A18BBB9-08D1-41A8-BE57-17FC992CC36F}.Debug|x86.ActiveCfg = Debug|Win32
- {6A18BBB9-08D1-41A8-BE57-17FC992CC36F}.Debug|x86.Build.0 = Debug|Win32
- {6A18BBB9-08D1-41A8-BE57-17FC992CC36F}.Release|ARM.ActiveCfg = Release|ARM
- {6A18BBB9-08D1-41A8-BE57-17FC992CC36F}.Release|ARM.Build.0 = Release|ARM
- {6A18BBB9-08D1-41A8-BE57-17FC992CC36F}.Release|x64.ActiveCfg = Release|x64
- {6A18BBB9-08D1-41A8-BE57-17FC992CC36F}.Release|x64.Build.0 = Release|x64
- {6A18BBB9-08D1-41A8-BE57-17FC992CC36F}.Release|x86.ActiveCfg = Release|Win32
- {6A18BBB9-08D1-41A8-BE57-17FC992CC36F}.Release|x86.Build.0 = Release|Win32
- {B84D5C3B-6DE5-49C8-B3DD-5EB67B01A527}.Debug|ARM.ActiveCfg = Debug|ARM
- {B84D5C3B-6DE5-49C8-B3DD-5EB67B01A527}.Debug|ARM.Build.0 = Debug|ARM
- {B84D5C3B-6DE5-49C8-B3DD-5EB67B01A527}.Debug|x64.ActiveCfg = Debug|x64
- {B84D5C3B-6DE5-49C8-B3DD-5EB67B01A527}.Debug|x64.Build.0 = Debug|x64
- {B84D5C3B-6DE5-49C8-B3DD-5EB67B01A527}.Debug|x86.ActiveCfg = Debug|Win32
- {B84D5C3B-6DE5-49C8-B3DD-5EB67B01A527}.Debug|x86.Build.0 = Debug|Win32
- {B84D5C3B-6DE5-49C8-B3DD-5EB67B01A527}.Release|ARM.ActiveCfg = Release|ARM
- {B84D5C3B-6DE5-49C8-B3DD-5EB67B01A527}.Release|ARM.Build.0 = Release|ARM
- {B84D5C3B-6DE5-49C8-B3DD-5EB67B01A527}.Release|x64.ActiveCfg = Release|x64
- {B84D5C3B-6DE5-49C8-B3DD-5EB67B01A527}.Release|x64.Build.0 = Release|x64
- {B84D5C3B-6DE5-49C8-B3DD-5EB67B01A527}.Release|x86.ActiveCfg = Release|Win32
- {B84D5C3B-6DE5-49C8-B3DD-5EB67B01A527}.Release|x86.Build.0 = Release|Win32
- {266B769A-C04E-424C-9033-7209F0425BC0}.Debug|ARM.ActiveCfg = Debug|ARM
- {266B769A-C04E-424C-9033-7209F0425BC0}.Debug|ARM.Build.0 = Debug|ARM
- {266B769A-C04E-424C-9033-7209F0425BC0}.Debug|x64.ActiveCfg = Debug|x64
- {266B769A-C04E-424C-9033-7209F0425BC0}.Debug|x64.Build.0 = Debug|x64
- {266B769A-C04E-424C-9033-7209F0425BC0}.Debug|x86.ActiveCfg = Debug|Win32
- {266B769A-C04E-424C-9033-7209F0425BC0}.Debug|x86.Build.0 = Debug|Win32
- {266B769A-C04E-424C-9033-7209F0425BC0}.Release|ARM.ActiveCfg = Release|ARM
- {266B769A-C04E-424C-9033-7209F0425BC0}.Release|ARM.Build.0 = Release|ARM
- {266B769A-C04E-424C-9033-7209F0425BC0}.Release|x64.ActiveCfg = Release|x64
- {266B769A-C04E-424C-9033-7209F0425BC0}.Release|x64.Build.0 = Release|x64
- {266B769A-C04E-424C-9033-7209F0425BC0}.Release|x86.ActiveCfg = Release|Win32
- {266B769A-C04E-424C-9033-7209F0425BC0}.Release|x86.Build.0 = Release|Win32
- {878CF9D3-9761-479E-A715-A1DE9F99CB78}.Debug|ARM.ActiveCfg = Debug|ARM
- {878CF9D3-9761-479E-A715-A1DE9F99CB78}.Debug|ARM.Build.0 = Debug|ARM
- {878CF9D3-9761-479E-A715-A1DE9F99CB78}.Debug|x64.ActiveCfg = Debug|x64
- {878CF9D3-9761-479E-A715-A1DE9F99CB78}.Debug|x64.Build.0 = Debug|x64
- {878CF9D3-9761-479E-A715-A1DE9F99CB78}.Debug|x86.ActiveCfg = Debug|Win32
- {878CF9D3-9761-479E-A715-A1DE9F99CB78}.Debug|x86.Build.0 = Debug|Win32
- {878CF9D3-9761-479E-A715-A1DE9F99CB78}.Release|ARM.ActiveCfg = Release|ARM
- {878CF9D3-9761-479E-A715-A1DE9F99CB78}.Release|ARM.Build.0 = Release|ARM
- {878CF9D3-9761-479E-A715-A1DE9F99CB78}.Release|x64.ActiveCfg = Release|x64
- {878CF9D3-9761-479E-A715-A1DE9F99CB78}.Release|x64.Build.0 = Release|x64
- {878CF9D3-9761-479E-A715-A1DE9F99CB78}.Release|x86.ActiveCfg = Release|Win32
- {878CF9D3-9761-479E-A715-A1DE9F99CB78}.Release|x86.Build.0 = Release|Win32
- {995B01AF-C568-453E-9E5F-8AE81FB79B4B}.Debug|ARM.ActiveCfg = Debug|ARM
- {995B01AF-C568-453E-9E5F-8AE81FB79B4B}.Debug|ARM.Build.0 = Debug|ARM
- {995B01AF-C568-453E-9E5F-8AE81FB79B4B}.Debug|x64.ActiveCfg = Debug|x64
- {995B01AF-C568-453E-9E5F-8AE81FB79B4B}.Debug|x64.Build.0 = Debug|x64
- {995B01AF-C568-453E-9E5F-8AE81FB79B4B}.Debug|x86.ActiveCfg = Debug|Win32
- {995B01AF-C568-453E-9E5F-8AE81FB79B4B}.Debug|x86.Build.0 = Debug|Win32
- {995B01AF-C568-453E-9E5F-8AE81FB79B4B}.Release|ARM.ActiveCfg = Release|ARM
- {995B01AF-C568-453E-9E5F-8AE81FB79B4B}.Release|ARM.Build.0 = Release|ARM
- {995B01AF-C568-453E-9E5F-8AE81FB79B4B}.Release|x64.ActiveCfg = Release|x64
- {995B01AF-C568-453E-9E5F-8AE81FB79B4B}.Release|x64.Build.0 = Release|x64
- {995B01AF-C568-453E-9E5F-8AE81FB79B4B}.Release|x86.ActiveCfg = Release|Win32
- {995B01AF-C568-453E-9E5F-8AE81FB79B4B}.Release|x86.Build.0 = Release|Win32
- {71A5F1C8-F76D-4297-95AA-75E1C967DC79}.Debug|ARM.ActiveCfg = Debug|ARM
- {71A5F1C8-F76D-4297-95AA-75E1C967DC79}.Debug|ARM.Build.0 = Debug|ARM
- {71A5F1C8-F76D-4297-95AA-75E1C967DC79}.Debug|x64.ActiveCfg = Debug|x64
- {71A5F1C8-F76D-4297-95AA-75E1C967DC79}.Debug|x64.Build.0 = Debug|x64
- {71A5F1C8-F76D-4297-95AA-75E1C967DC79}.Debug|x86.ActiveCfg = Debug|Win32
- {71A5F1C8-F76D-4297-95AA-75E1C967DC79}.Debug|x86.Build.0 = Debug|Win32
- {71A5F1C8-F76D-4297-95AA-75E1C967DC79}.Release|ARM.ActiveCfg = Release|ARM
- {71A5F1C8-F76D-4297-95AA-75E1C967DC79}.Release|ARM.Build.0 = Release|ARM
- {71A5F1C8-F76D-4297-95AA-75E1C967DC79}.Release|x64.ActiveCfg = Release|x64
- {71A5F1C8-F76D-4297-95AA-75E1C967DC79}.Release|x64.Build.0 = Release|x64
- {71A5F1C8-F76D-4297-95AA-75E1C967DC79}.Release|x86.ActiveCfg = Release|Win32
- {71A5F1C8-F76D-4297-95AA-75E1C967DC79}.Release|x86.Build.0 = Release|Win32
- {3CC91899-3E98-49FD-BED5-FA290A9A5C8E}.Debug|ARM.ActiveCfg = Debug|ARM
- {3CC91899-3E98-49FD-BED5-FA290A9A5C8E}.Debug|ARM.Build.0 = Debug|ARM
- {3CC91899-3E98-49FD-BED5-FA290A9A5C8E}.Debug|x64.ActiveCfg = Debug|x64
- {3CC91899-3E98-49FD-BED5-FA290A9A5C8E}.Debug|x64.Build.0 = Debug|x64
- {3CC91899-3E98-49FD-BED5-FA290A9A5C8E}.Debug|x86.ActiveCfg = Debug|Win32
- {3CC91899-3E98-49FD-BED5-FA290A9A5C8E}.Debug|x86.Build.0 = Debug|Win32
- {3CC91899-3E98-49FD-BED5-FA290A9A5C8E}.Release|ARM.ActiveCfg = Release|ARM
- {3CC91899-3E98-49FD-BED5-FA290A9A5C8E}.Release|ARM.Build.0 = Release|ARM
- {3CC91899-3E98-49FD-BED5-FA290A9A5C8E}.Release|x64.ActiveCfg = Release|x64
- {3CC91899-3E98-49FD-BED5-FA290A9A5C8E}.Release|x64.Build.0 = Release|x64
- {3CC91899-3E98-49FD-BED5-FA290A9A5C8E}.Release|x86.ActiveCfg = Release|Win32
- {3CC91899-3E98-49FD-BED5-FA290A9A5C8E}.Release|x86.Build.0 = Release|Win32
- {D829672F-3775-4718-A991-1ABC42CBA67C}.Debug|ARM.ActiveCfg = Debug|ARM
- {D829672F-3775-4718-A991-1ABC42CBA67C}.Debug|ARM.Build.0 = Debug|ARM
- {D829672F-3775-4718-A991-1ABC42CBA67C}.Debug|x64.ActiveCfg = Debug|x64
- {D829672F-3775-4718-A991-1ABC42CBA67C}.Debug|x64.Build.0 = Debug|x64
- {D829672F-3775-4718-A991-1ABC42CBA67C}.Debug|x86.ActiveCfg = Debug|Win32
- {D829672F-3775-4718-A991-1ABC42CBA67C}.Debug|x86.Build.0 = Debug|Win32
- {D829672F-3775-4718-A991-1ABC42CBA67C}.Release|ARM.ActiveCfg = Release|ARM
- {D829672F-3775-4718-A991-1ABC42CBA67C}.Release|ARM.Build.0 = Release|ARM
- {D829672F-3775-4718-A991-1ABC42CBA67C}.Release|x64.ActiveCfg = Release|x64
- {D829672F-3775-4718-A991-1ABC42CBA67C}.Release|x64.Build.0 = Release|x64
- {D829672F-3775-4718-A991-1ABC42CBA67C}.Release|x86.ActiveCfg = Release|Win32
- {D829672F-3775-4718-A991-1ABC42CBA67C}.Release|x86.Build.0 = Release|Win32
- {C5895B75-BDCF-406C-B803-9CB954E90F0C}.Debug|ARM.ActiveCfg = Debug|ARM
- {C5895B75-BDCF-406C-B803-9CB954E90F0C}.Debug|ARM.Build.0 = Debug|ARM
- {C5895B75-BDCF-406C-B803-9CB954E90F0C}.Debug|x64.ActiveCfg = Debug|x64
- {C5895B75-BDCF-406C-B803-9CB954E90F0C}.Debug|x64.Build.0 = Debug|x64
- {C5895B75-BDCF-406C-B803-9CB954E90F0C}.Debug|x86.ActiveCfg = Debug|Win32
- {C5895B75-BDCF-406C-B803-9CB954E90F0C}.Debug|x86.Build.0 = Debug|Win32
- {C5895B75-BDCF-406C-B803-9CB954E90F0C}.Release|ARM.ActiveCfg = Release|ARM
- {C5895B75-BDCF-406C-B803-9CB954E90F0C}.Release|ARM.Build.0 = Release|ARM
- {C5895B75-BDCF-406C-B803-9CB954E90F0C}.Release|x64.ActiveCfg = Release|x64
- {C5895B75-BDCF-406C-B803-9CB954E90F0C}.Release|x64.Build.0 = Release|x64
- {C5895B75-BDCF-406C-B803-9CB954E90F0C}.Release|x86.ActiveCfg = Release|Win32
- {C5895B75-BDCF-406C-B803-9CB954E90F0C}.Release|x86.Build.0 = Release|Win32
- {ACF5EA95-D647-4D0C-8F97-2CD9AAE8A2E0}.Debug|ARM.ActiveCfg = Debug|ARM
- {ACF5EA95-D647-4D0C-8F97-2CD9AAE8A2E0}.Debug|ARM.Build.0 = Debug|ARM
- {ACF5EA95-D647-4D0C-8F97-2CD9AAE8A2E0}.Debug|x64.ActiveCfg = Debug|x64
- {ACF5EA95-D647-4D0C-8F97-2CD9AAE8A2E0}.Debug|x64.Build.0 = Debug|x64
- {ACF5EA95-D647-4D0C-8F97-2CD9AAE8A2E0}.Debug|x86.ActiveCfg = Debug|Win32
- {ACF5EA95-D647-4D0C-8F97-2CD9AAE8A2E0}.Debug|x86.Build.0 = Debug|Win32
- {ACF5EA95-D647-4D0C-8F97-2CD9AAE8A2E0}.Release|ARM.ActiveCfg = Release|ARM
- {ACF5EA95-D647-4D0C-8F97-2CD9AAE8A2E0}.Release|ARM.Build.0 = Release|ARM
- {ACF5EA95-D647-4D0C-8F97-2CD9AAE8A2E0}.Release|x64.ActiveCfg = Release|x64
- {ACF5EA95-D647-4D0C-8F97-2CD9AAE8A2E0}.Release|x64.Build.0 = Release|x64
- {ACF5EA95-D647-4D0C-8F97-2CD9AAE8A2E0}.Release|x86.ActiveCfg = Release|Win32
- {ACF5EA95-D647-4D0C-8F97-2CD9AAE8A2E0}.Release|x86.Build.0 = Release|Win32
- EndGlobalSection
- GlobalSection(SolutionProperties) = preSolution
- HideSolutionNode = FALSE
- EndGlobalSection
-EndGlobal
diff --git a/build/windows10/liblinphone-tester/liblinphone-tester_TemporaryKey.pfx b/build/windows10/liblinphone-tester/liblinphone-tester_TemporaryKey.pfx
deleted file mode 100644
index 3aeb9f3b9..000000000
Binary files a/build/windows10/liblinphone-tester/liblinphone-tester_TemporaryKey.pfx and /dev/null differ
diff --git a/build/windows10/liblinphone-tester/project.json b/build/windows10/liblinphone-tester/project.json
deleted file mode 100644
index e3b2dba25..000000000
--- a/build/windows10/liblinphone-tester/project.json
+++ /dev/null
@@ -1,19 +0,0 @@
-{
- "dependencies": {
- "Microsoft.ApplicationInsights": "1.0.0",
- "Microsoft.ApplicationInsights.PersistenceChannel": "1.0.0",
- "Microsoft.ApplicationInsights.WindowsApps": "1.0.0",
- "Microsoft.NETCore.UniversalWindowsPlatform": "5.0.0"
- },
- "frameworks": {
- "uap10.0": {}
- },
- "runtimes": {
- "win10-arm": {},
- "win10-arm-aot": {},
- "win10-x86": {},
- "win10-x86-aot": {},
- "win10-x64": {},
- "win10-x64-aot": {}
- }
-}
\ No newline at end of file
diff --git a/build/windows10/liblinphone/liblinphone.sln b/build/windows10/liblinphone/liblinphone.sln
deleted file mode 100644
index abfa15a85..000000000
--- a/build/windows10/liblinphone/liblinphone.sln
+++ /dev/null
@@ -1,230 +0,0 @@
-
-Microsoft Visual Studio Solution File, Format Version 12.00
-# Visual Studio 14
-VisualStudioVersion = 14.0.22823.1
-MinimumVisualStudioVersion = 10.0.40219.1
-Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "liblinphone", "liblinphone.vcxproj", "{C7139899-D8BC-48A3-A437-6844A8BAABEF}"
-EndProject
-Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "mediastreamer2", "..\..\..\mediastreamer2\build\windows10\mediastreamer2\mediastreamer2.vcxproj", "{88E3C241-EB6F-4C84-80DC-89B8961DAF80}"
-EndProject
-Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ortp", "..\..\..\oRTP\build\windows10\ortp\ortp.vcxproj", "{2E56B851-9D8D-40E5-84BB-E4EE63B71D25}"
-EndProject
-Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "srtp", "..\..\..\..\srtp\build\windows10\srtp\srtp.vcxproj", "{59104E4F-A087-442E-ABD4-BCD2A1F0B0FE}"
-EndProject
-Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "xml2", "..\..\..\..\build\xml2\xml2.vcxproj", "{2B04DE79-4D33-4405-AC01-C89E0593A71D}"
-EndProject
-Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "polarssl", "..\..\..\..\polarssl\build\windows10\polarssl\polarssl.vcxproj", "{88768DD9-5110-4AC8-8B0E-41CD7713E1A2}"
-EndProject
-Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "speex", "..\..\..\..\speex\build\windows10\speex\speex.vcxproj", "{971DD379-1C2D-44D2-9285-FDA556C48176}"
-EndProject
-Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "speexdsp", "..\..\..\..\speex\build\windows10\speexdsp\speexdsp.vcxproj", "{104BF91B-8314-4328-A996-90B8DF6052AF}"
-EndProject
-Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "opus", "..\..\..\..\opus\build\windows10\opus\opus.vcxproj", "{81AF1025-E0EE-4AD6-988D-2EF162778693}"
-EndProject
-Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "bzrtp", "..\..\..\..\bzrtp\build\windows10\bzrtp\bzrtp.vcxproj", "{45C7723D-3107-4906-9633-F43ABE8A7147}"
-EndProject
-Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "gsm", "..\..\..\..\gsm\build\windows10\gsm\gsm.vcxproj", "{EF1103C7-8AAC-464B-BA31-86B87246FA72}"
-EndProject
-Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "belle-sip", "..\..\..\..\belle-sip\build\windows10\belle-sip\belle-sip.vcxproj", "{B6CDF482-7DA3-43D4-9B12-70150106C191}"
-EndProject
-Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "antlr3c", "..\..\..\..\antlr3\runtime\C\build\windows10\antlr3c\antlr3c.vcxproj", "{01CCCCC9-CA0C-4528-92BC-5B8BE1D02D6D}"
-EndProject
-Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "zlib", "..\..\..\..\zlib\build\windows10\zlib\zlib.vcxproj", "{A34F450D-392D-4660-9618-810BD695B3B0}"
-EndProject
-Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "sqlite", "..\..\..\..\build\sqlite\sqlite.vcxproj", "{74CAD9D0-D8AE-4896-B71B-B2D9B48F30AA}"
-EndProject
-Global
- GlobalSection(SolutionConfigurationPlatforms) = preSolution
- Debug|ARM = Debug|ARM
- Debug|x64 = Debug|x64
- Debug|x86 = Debug|x86
- Release|ARM = Release|ARM
- Release|x64 = Release|x64
- Release|x86 = Release|x86
- EndGlobalSection
- GlobalSection(ProjectConfigurationPlatforms) = postSolution
- {C7139899-D8BC-48A3-A437-6844A8BAABEF}.Debug|ARM.ActiveCfg = Debug|ARM
- {C7139899-D8BC-48A3-A437-6844A8BAABEF}.Debug|ARM.Build.0 = Debug|ARM
- {C7139899-D8BC-48A3-A437-6844A8BAABEF}.Debug|x64.ActiveCfg = Debug|x64
- {C7139899-D8BC-48A3-A437-6844A8BAABEF}.Debug|x64.Build.0 = Debug|x64
- {C7139899-D8BC-48A3-A437-6844A8BAABEF}.Debug|x86.ActiveCfg = Debug|Win32
- {C7139899-D8BC-48A3-A437-6844A8BAABEF}.Debug|x86.Build.0 = Debug|Win32
- {C7139899-D8BC-48A3-A437-6844A8BAABEF}.Release|ARM.ActiveCfg = Release|ARM
- {C7139899-D8BC-48A3-A437-6844A8BAABEF}.Release|ARM.Build.0 = Release|ARM
- {C7139899-D8BC-48A3-A437-6844A8BAABEF}.Release|x64.ActiveCfg = Release|x64
- {C7139899-D8BC-48A3-A437-6844A8BAABEF}.Release|x64.Build.0 = Release|x64
- {C7139899-D8BC-48A3-A437-6844A8BAABEF}.Release|x86.ActiveCfg = Release|Win32
- {C7139899-D8BC-48A3-A437-6844A8BAABEF}.Release|x86.Build.0 = Release|Win32
- {88E3C241-EB6F-4C84-80DC-89B8961DAF80}.Debug|ARM.ActiveCfg = Debug|ARM
- {88E3C241-EB6F-4C84-80DC-89B8961DAF80}.Debug|ARM.Build.0 = Debug|ARM
- {88E3C241-EB6F-4C84-80DC-89B8961DAF80}.Debug|x64.ActiveCfg = Debug|x64
- {88E3C241-EB6F-4C84-80DC-89B8961DAF80}.Debug|x64.Build.0 = Debug|x64
- {88E3C241-EB6F-4C84-80DC-89B8961DAF80}.Debug|x86.ActiveCfg = Debug|Win32
- {88E3C241-EB6F-4C84-80DC-89B8961DAF80}.Debug|x86.Build.0 = Debug|Win32
- {88E3C241-EB6F-4C84-80DC-89B8961DAF80}.Release|ARM.ActiveCfg = Release|ARM
- {88E3C241-EB6F-4C84-80DC-89B8961DAF80}.Release|ARM.Build.0 = Release|ARM
- {88E3C241-EB6F-4C84-80DC-89B8961DAF80}.Release|x64.ActiveCfg = Release|x64
- {88E3C241-EB6F-4C84-80DC-89B8961DAF80}.Release|x64.Build.0 = Release|x64
- {88E3C241-EB6F-4C84-80DC-89B8961DAF80}.Release|x86.ActiveCfg = Release|Win32
- {88E3C241-EB6F-4C84-80DC-89B8961DAF80}.Release|x86.Build.0 = Release|Win32
- {2E56B851-9D8D-40E5-84BB-E4EE63B71D25}.Debug|ARM.ActiveCfg = Debug|ARM
- {2E56B851-9D8D-40E5-84BB-E4EE63B71D25}.Debug|ARM.Build.0 = Debug|ARM
- {2E56B851-9D8D-40E5-84BB-E4EE63B71D25}.Debug|x64.ActiveCfg = Debug|x64
- {2E56B851-9D8D-40E5-84BB-E4EE63B71D25}.Debug|x64.Build.0 = Debug|x64
- {2E56B851-9D8D-40E5-84BB-E4EE63B71D25}.Debug|x86.ActiveCfg = Debug|Win32
- {2E56B851-9D8D-40E5-84BB-E4EE63B71D25}.Debug|x86.Build.0 = Debug|Win32
- {2E56B851-9D8D-40E5-84BB-E4EE63B71D25}.Release|ARM.ActiveCfg = Release|ARM
- {2E56B851-9D8D-40E5-84BB-E4EE63B71D25}.Release|ARM.Build.0 = Release|ARM
- {2E56B851-9D8D-40E5-84BB-E4EE63B71D25}.Release|x64.ActiveCfg = Release|x64
- {2E56B851-9D8D-40E5-84BB-E4EE63B71D25}.Release|x64.Build.0 = Release|x64
- {2E56B851-9D8D-40E5-84BB-E4EE63B71D25}.Release|x86.ActiveCfg = Release|Win32
- {2E56B851-9D8D-40E5-84BB-E4EE63B71D25}.Release|x86.Build.0 = Release|Win32
- {59104E4F-A087-442E-ABD4-BCD2A1F0B0FE}.Debug|ARM.ActiveCfg = Debug|ARM
- {59104E4F-A087-442E-ABD4-BCD2A1F0B0FE}.Debug|ARM.Build.0 = Debug|ARM
- {59104E4F-A087-442E-ABD4-BCD2A1F0B0FE}.Debug|x64.ActiveCfg = Debug|x64
- {59104E4F-A087-442E-ABD4-BCD2A1F0B0FE}.Debug|x64.Build.0 = Debug|x64
- {59104E4F-A087-442E-ABD4-BCD2A1F0B0FE}.Debug|x86.ActiveCfg = Debug|Win32
- {59104E4F-A087-442E-ABD4-BCD2A1F0B0FE}.Debug|x86.Build.0 = Debug|Win32
- {59104E4F-A087-442E-ABD4-BCD2A1F0B0FE}.Release|ARM.ActiveCfg = Release|ARM
- {59104E4F-A087-442E-ABD4-BCD2A1F0B0FE}.Release|ARM.Build.0 = Release|ARM
- {59104E4F-A087-442E-ABD4-BCD2A1F0B0FE}.Release|x64.ActiveCfg = Release|x64
- {59104E4F-A087-442E-ABD4-BCD2A1F0B0FE}.Release|x64.Build.0 = Release|x64
- {59104E4F-A087-442E-ABD4-BCD2A1F0B0FE}.Release|x86.ActiveCfg = Release|Win32
- {59104E4F-A087-442E-ABD4-BCD2A1F0B0FE}.Release|x86.Build.0 = Release|Win32
- {2B04DE79-4D33-4405-AC01-C89E0593A71D}.Debug|ARM.ActiveCfg = Debug|ARM
- {2B04DE79-4D33-4405-AC01-C89E0593A71D}.Debug|ARM.Build.0 = Debug|ARM
- {2B04DE79-4D33-4405-AC01-C89E0593A71D}.Debug|x64.ActiveCfg = Debug|x64
- {2B04DE79-4D33-4405-AC01-C89E0593A71D}.Debug|x64.Build.0 = Debug|x64
- {2B04DE79-4D33-4405-AC01-C89E0593A71D}.Debug|x86.ActiveCfg = Debug|Win32
- {2B04DE79-4D33-4405-AC01-C89E0593A71D}.Debug|x86.Build.0 = Debug|Win32
- {2B04DE79-4D33-4405-AC01-C89E0593A71D}.Release|ARM.ActiveCfg = Release|ARM
- {2B04DE79-4D33-4405-AC01-C89E0593A71D}.Release|ARM.Build.0 = Release|ARM
- {2B04DE79-4D33-4405-AC01-C89E0593A71D}.Release|x64.ActiveCfg = Release|x64
- {2B04DE79-4D33-4405-AC01-C89E0593A71D}.Release|x64.Build.0 = Release|x64
- {2B04DE79-4D33-4405-AC01-C89E0593A71D}.Release|x86.ActiveCfg = Release|Win32
- {2B04DE79-4D33-4405-AC01-C89E0593A71D}.Release|x86.Build.0 = Release|Win32
- {88768DD9-5110-4AC8-8B0E-41CD7713E1A2}.Debug|ARM.ActiveCfg = Debug|ARM
- {88768DD9-5110-4AC8-8B0E-41CD7713E1A2}.Debug|ARM.Build.0 = Debug|ARM
- {88768DD9-5110-4AC8-8B0E-41CD7713E1A2}.Debug|x64.ActiveCfg = Debug|x64
- {88768DD9-5110-4AC8-8B0E-41CD7713E1A2}.Debug|x64.Build.0 = Debug|x64
- {88768DD9-5110-4AC8-8B0E-41CD7713E1A2}.Debug|x86.ActiveCfg = Debug|Win32
- {88768DD9-5110-4AC8-8B0E-41CD7713E1A2}.Debug|x86.Build.0 = Debug|Win32
- {88768DD9-5110-4AC8-8B0E-41CD7713E1A2}.Release|ARM.ActiveCfg = Release|ARM
- {88768DD9-5110-4AC8-8B0E-41CD7713E1A2}.Release|ARM.Build.0 = Release|ARM
- {88768DD9-5110-4AC8-8B0E-41CD7713E1A2}.Release|x64.ActiveCfg = Release|x64
- {88768DD9-5110-4AC8-8B0E-41CD7713E1A2}.Release|x64.Build.0 = Release|x64
- {88768DD9-5110-4AC8-8B0E-41CD7713E1A2}.Release|x86.ActiveCfg = Release|Win32
- {88768DD9-5110-4AC8-8B0E-41CD7713E1A2}.Release|x86.Build.0 = Release|Win32
- {971DD379-1C2D-44D2-9285-FDA556C48176}.Debug|ARM.ActiveCfg = Debug|ARM
- {971DD379-1C2D-44D2-9285-FDA556C48176}.Debug|ARM.Build.0 = Debug|ARM
- {971DD379-1C2D-44D2-9285-FDA556C48176}.Debug|x64.ActiveCfg = Debug|x64
- {971DD379-1C2D-44D2-9285-FDA556C48176}.Debug|x64.Build.0 = Debug|x64
- {971DD379-1C2D-44D2-9285-FDA556C48176}.Debug|x86.ActiveCfg = Debug|Win32
- {971DD379-1C2D-44D2-9285-FDA556C48176}.Debug|x86.Build.0 = Debug|Win32
- {971DD379-1C2D-44D2-9285-FDA556C48176}.Release|ARM.ActiveCfg = Release|ARM
- {971DD379-1C2D-44D2-9285-FDA556C48176}.Release|ARM.Build.0 = Release|ARM
- {971DD379-1C2D-44D2-9285-FDA556C48176}.Release|x64.ActiveCfg = Release|x64
- {971DD379-1C2D-44D2-9285-FDA556C48176}.Release|x64.Build.0 = Release|x64
- {971DD379-1C2D-44D2-9285-FDA556C48176}.Release|x86.ActiveCfg = Release|Win32
- {971DD379-1C2D-44D2-9285-FDA556C48176}.Release|x86.Build.0 = Release|Win32
- {104BF91B-8314-4328-A996-90B8DF6052AF}.Debug|ARM.ActiveCfg = Debug|ARM
- {104BF91B-8314-4328-A996-90B8DF6052AF}.Debug|ARM.Build.0 = Debug|ARM
- {104BF91B-8314-4328-A996-90B8DF6052AF}.Debug|x64.ActiveCfg = Debug|x64
- {104BF91B-8314-4328-A996-90B8DF6052AF}.Debug|x64.Build.0 = Debug|x64
- {104BF91B-8314-4328-A996-90B8DF6052AF}.Debug|x86.ActiveCfg = Debug|Win32
- {104BF91B-8314-4328-A996-90B8DF6052AF}.Debug|x86.Build.0 = Debug|Win32
- {104BF91B-8314-4328-A996-90B8DF6052AF}.Release|ARM.ActiveCfg = Release|ARM
- {104BF91B-8314-4328-A996-90B8DF6052AF}.Release|ARM.Build.0 = Release|ARM
- {104BF91B-8314-4328-A996-90B8DF6052AF}.Release|x64.ActiveCfg = Release|x64
- {104BF91B-8314-4328-A996-90B8DF6052AF}.Release|x64.Build.0 = Release|x64
- {104BF91B-8314-4328-A996-90B8DF6052AF}.Release|x86.ActiveCfg = Release|Win32
- {104BF91B-8314-4328-A996-90B8DF6052AF}.Release|x86.Build.0 = Release|Win32
- {81AF1025-E0EE-4AD6-988D-2EF162778693}.Debug|ARM.ActiveCfg = Debug|ARM
- {81AF1025-E0EE-4AD6-988D-2EF162778693}.Debug|ARM.Build.0 = Debug|ARM
- {81AF1025-E0EE-4AD6-988D-2EF162778693}.Debug|x64.ActiveCfg = Debug|x64
- {81AF1025-E0EE-4AD6-988D-2EF162778693}.Debug|x64.Build.0 = Debug|x64
- {81AF1025-E0EE-4AD6-988D-2EF162778693}.Debug|x86.ActiveCfg = Debug|Win32
- {81AF1025-E0EE-4AD6-988D-2EF162778693}.Debug|x86.Build.0 = Debug|Win32
- {81AF1025-E0EE-4AD6-988D-2EF162778693}.Release|ARM.ActiveCfg = Release|ARM
- {81AF1025-E0EE-4AD6-988D-2EF162778693}.Release|ARM.Build.0 = Release|ARM
- {81AF1025-E0EE-4AD6-988D-2EF162778693}.Release|x64.ActiveCfg = Release|x64
- {81AF1025-E0EE-4AD6-988D-2EF162778693}.Release|x64.Build.0 = Release|x64
- {81AF1025-E0EE-4AD6-988D-2EF162778693}.Release|x86.ActiveCfg = Release|Win32
- {81AF1025-E0EE-4AD6-988D-2EF162778693}.Release|x86.Build.0 = Release|Win32
- {45C7723D-3107-4906-9633-F43ABE8A7147}.Debug|ARM.ActiveCfg = Debug|ARM
- {45C7723D-3107-4906-9633-F43ABE8A7147}.Debug|ARM.Build.0 = Debug|ARM
- {45C7723D-3107-4906-9633-F43ABE8A7147}.Debug|x64.ActiveCfg = Debug|x64
- {45C7723D-3107-4906-9633-F43ABE8A7147}.Debug|x64.Build.0 = Debug|x64
- {45C7723D-3107-4906-9633-F43ABE8A7147}.Debug|x86.ActiveCfg = Debug|Win32
- {45C7723D-3107-4906-9633-F43ABE8A7147}.Debug|x86.Build.0 = Debug|Win32
- {45C7723D-3107-4906-9633-F43ABE8A7147}.Release|ARM.ActiveCfg = Release|ARM
- {45C7723D-3107-4906-9633-F43ABE8A7147}.Release|ARM.Build.0 = Release|ARM
- {45C7723D-3107-4906-9633-F43ABE8A7147}.Release|x64.ActiveCfg = Release|x64
- {45C7723D-3107-4906-9633-F43ABE8A7147}.Release|x64.Build.0 = Release|x64
- {45C7723D-3107-4906-9633-F43ABE8A7147}.Release|x86.ActiveCfg = Release|Win32
- {45C7723D-3107-4906-9633-F43ABE8A7147}.Release|x86.Build.0 = Release|Win32
- {EF1103C7-8AAC-464B-BA31-86B87246FA72}.Debug|ARM.ActiveCfg = Debug|ARM
- {EF1103C7-8AAC-464B-BA31-86B87246FA72}.Debug|ARM.Build.0 = Debug|ARM
- {EF1103C7-8AAC-464B-BA31-86B87246FA72}.Debug|x64.ActiveCfg = Debug|x64
- {EF1103C7-8AAC-464B-BA31-86B87246FA72}.Debug|x64.Build.0 = Debug|x64
- {EF1103C7-8AAC-464B-BA31-86B87246FA72}.Debug|x86.ActiveCfg = Debug|Win32
- {EF1103C7-8AAC-464B-BA31-86B87246FA72}.Debug|x86.Build.0 = Debug|Win32
- {EF1103C7-8AAC-464B-BA31-86B87246FA72}.Release|ARM.ActiveCfg = Release|ARM
- {EF1103C7-8AAC-464B-BA31-86B87246FA72}.Release|ARM.Build.0 = Release|ARM
- {EF1103C7-8AAC-464B-BA31-86B87246FA72}.Release|x64.ActiveCfg = Release|x64
- {EF1103C7-8AAC-464B-BA31-86B87246FA72}.Release|x64.Build.0 = Release|x64
- {EF1103C7-8AAC-464B-BA31-86B87246FA72}.Release|x86.ActiveCfg = Release|Win32
- {EF1103C7-8AAC-464B-BA31-86B87246FA72}.Release|x86.Build.0 = Release|Win32
- {B6CDF482-7DA3-43D4-9B12-70150106C191}.Debug|ARM.ActiveCfg = Debug|ARM
- {B6CDF482-7DA3-43D4-9B12-70150106C191}.Debug|ARM.Build.0 = Debug|ARM
- {B6CDF482-7DA3-43D4-9B12-70150106C191}.Debug|x64.ActiveCfg = Debug|x64
- {B6CDF482-7DA3-43D4-9B12-70150106C191}.Debug|x64.Build.0 = Debug|x64
- {B6CDF482-7DA3-43D4-9B12-70150106C191}.Debug|x86.ActiveCfg = Debug|Win32
- {B6CDF482-7DA3-43D4-9B12-70150106C191}.Debug|x86.Build.0 = Debug|Win32
- {B6CDF482-7DA3-43D4-9B12-70150106C191}.Release|ARM.ActiveCfg = Release|ARM
- {B6CDF482-7DA3-43D4-9B12-70150106C191}.Release|ARM.Build.0 = Release|ARM
- {B6CDF482-7DA3-43D4-9B12-70150106C191}.Release|x64.ActiveCfg = Release|x64
- {B6CDF482-7DA3-43D4-9B12-70150106C191}.Release|x64.Build.0 = Release|x64
- {B6CDF482-7DA3-43D4-9B12-70150106C191}.Release|x86.ActiveCfg = Release|Win32
- {B6CDF482-7DA3-43D4-9B12-70150106C191}.Release|x86.Build.0 = Release|Win32
- {01CCCCC9-CA0C-4528-92BC-5B8BE1D02D6D}.Debug|ARM.ActiveCfg = Debug|ARM
- {01CCCCC9-CA0C-4528-92BC-5B8BE1D02D6D}.Debug|ARM.Build.0 = Debug|ARM
- {01CCCCC9-CA0C-4528-92BC-5B8BE1D02D6D}.Debug|x64.ActiveCfg = Debug|x64
- {01CCCCC9-CA0C-4528-92BC-5B8BE1D02D6D}.Debug|x64.Build.0 = Debug|x64
- {01CCCCC9-CA0C-4528-92BC-5B8BE1D02D6D}.Debug|x86.ActiveCfg = Debug|Win32
- {01CCCCC9-CA0C-4528-92BC-5B8BE1D02D6D}.Debug|x86.Build.0 = Debug|Win32
- {01CCCCC9-CA0C-4528-92BC-5B8BE1D02D6D}.Release|ARM.ActiveCfg = Release|ARM
- {01CCCCC9-CA0C-4528-92BC-5B8BE1D02D6D}.Release|ARM.Build.0 = Release|ARM
- {01CCCCC9-CA0C-4528-92BC-5B8BE1D02D6D}.Release|x64.ActiveCfg = Release|x64
- {01CCCCC9-CA0C-4528-92BC-5B8BE1D02D6D}.Release|x64.Build.0 = Release|x64
- {01CCCCC9-CA0C-4528-92BC-5B8BE1D02D6D}.Release|x86.ActiveCfg = Release|Win32
- {01CCCCC9-CA0C-4528-92BC-5B8BE1D02D6D}.Release|x86.Build.0 = Release|Win32
- {A34F450D-392D-4660-9618-810BD695B3B0}.Debug|ARM.ActiveCfg = Debug|ARM
- {A34F450D-392D-4660-9618-810BD695B3B0}.Debug|ARM.Build.0 = Debug|ARM
- {A34F450D-392D-4660-9618-810BD695B3B0}.Debug|x64.ActiveCfg = Debug|x64
- {A34F450D-392D-4660-9618-810BD695B3B0}.Debug|x64.Build.0 = Debug|x64
- {A34F450D-392D-4660-9618-810BD695B3B0}.Debug|x86.ActiveCfg = Debug|Win32
- {A34F450D-392D-4660-9618-810BD695B3B0}.Debug|x86.Build.0 = Debug|Win32
- {A34F450D-392D-4660-9618-810BD695B3B0}.Release|ARM.ActiveCfg = Release|ARM
- {A34F450D-392D-4660-9618-810BD695B3B0}.Release|ARM.Build.0 = Release|ARM
- {A34F450D-392D-4660-9618-810BD695B3B0}.Release|x64.ActiveCfg = Release|x64
- {A34F450D-392D-4660-9618-810BD695B3B0}.Release|x64.Build.0 = Release|x64
- {A34F450D-392D-4660-9618-810BD695B3B0}.Release|x86.ActiveCfg = Release|Win32
- {A34F450D-392D-4660-9618-810BD695B3B0}.Release|x86.Build.0 = Release|Win32
- {74CAD9D0-D8AE-4896-B71B-B2D9B48F30AA}.Debug|ARM.ActiveCfg = Debug|ARM
- {74CAD9D0-D8AE-4896-B71B-B2D9B48F30AA}.Debug|ARM.Build.0 = Debug|ARM
- {74CAD9D0-D8AE-4896-B71B-B2D9B48F30AA}.Debug|x64.ActiveCfg = Debug|x64
- {74CAD9D0-D8AE-4896-B71B-B2D9B48F30AA}.Debug|x64.Build.0 = Debug|x64
- {74CAD9D0-D8AE-4896-B71B-B2D9B48F30AA}.Debug|x86.ActiveCfg = Debug|Win32
- {74CAD9D0-D8AE-4896-B71B-B2D9B48F30AA}.Debug|x86.Build.0 = Debug|Win32
- {74CAD9D0-D8AE-4896-B71B-B2D9B48F30AA}.Release|ARM.ActiveCfg = Release|ARM
- {74CAD9D0-D8AE-4896-B71B-B2D9B48F30AA}.Release|ARM.Build.0 = Release|ARM
- {74CAD9D0-D8AE-4896-B71B-B2D9B48F30AA}.Release|x64.ActiveCfg = Release|x64
- {74CAD9D0-D8AE-4896-B71B-B2D9B48F30AA}.Release|x64.Build.0 = Release|x64
- {74CAD9D0-D8AE-4896-B71B-B2D9B48F30AA}.Release|x86.ActiveCfg = Release|Win32
- {74CAD9D0-D8AE-4896-B71B-B2D9B48F30AA}.Release|x86.Build.0 = Release|Win32
- EndGlobalSection
- GlobalSection(SolutionProperties) = preSolution
- HideSolutionNode = FALSE
- EndGlobalSection
-EndGlobal
diff --git a/build/windows10/liblinphone/liblinphone.vcxproj b/build/windows10/liblinphone/liblinphone.vcxproj
deleted file mode 100644
index 2be93aeb0..000000000
--- a/build/windows10/liblinphone/liblinphone.vcxproj
+++ /dev/null
@@ -1,189 +0,0 @@
-
-
-
-
- Debug
- ARM
-
-
- Debug
- Win32
-
-
- Debug
- x64
-
-
- Release
- ARM
-
-
- Release
- Win32
-
-
- Release
- x64
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- {b6cdf482-7da3-43d4-9b12-70150106c191}
-
-
- {74cad9d0-d8ae-4896-b71b-b2d9b48f30aa}
-
-
- {2b04de79-4d33-4405-ac01-c89e0593a71d}
-
-
- {a34f450d-392d-4660-9618-810bd695b3b0}
-
-
- {88e3c241-eb6f-4c84-80dc-89b8961daf80}
-
-
- {2e56b851-9d8d-40e5-84bb-e4ee63b71d25}
-
-
-
- {c7139899-d8bc-48a3-a437-6844a8baabef}
- DynamicLibrary
- liblinphone
- liblinphone
- en-US
- 14.0
- true
- Windows Store
- 10
- 10.0.10240.0
- 10.0.10069.0
-
-
-
- DynamicLibrary
- true
- v140
-
-
- DynamicLibrary
- false
- true
- v140
-
-
-
-
-
-
-
-
-
-
-
- false
- false
-
-
-
- NotUsing
- false
- $(SolutionDir)$(Platform)\$(Configuration)\include;$(ProjectDir)..\..\..\include;$(ProjectDir)..\..\..\coreapi;$(ProjectDir)..\..\..\mediastreamer2\include;$(ProjectDir)..\..\..\..\belle-sip\include;$(ProjectDir)..\..\..\oRTP\include;$(ProjectDir)..\..\..\..\sqlite;$(ProjectDir)..\..\..\..\zlib;%(AdditionalIncludeDirectories)
- HAVE_CONFIG_H;HAVE_ZLIB;MSG_STORAGE_ENABLED;VIDEO_ENABLED;IN_LINPHONE;LINPHONE_PLUGINS_DIR="\\linphone\\plugins";_CRT_SECURE_NO_WARNINGS;_WINSOCK_DEPRECATED_NO_WARNINGS;%(PreprocessorDefinitions)
-
-
- Console
- false
- false
-
-
- version.bat
-
-
- Batch script to get the git version
-
-
-
-
-
-
\ No newline at end of file
diff --git a/build/windows10/liblinphone/version.bat b/build/windows10/liblinphone/version.bat
deleted file mode 100644
index c015636e2..000000000
--- a/build/windows10/liblinphone/version.bat
+++ /dev/null
@@ -1,22 +0,0 @@
-@ECHO off
-
-SET gitlog=
-FOR /f "delims=" %%a IN ('git log -1 "--pretty=format:%%H" ../../../configure.ac') DO SET gitlog=%%a
-
-IF [%gitlog%] == [] GOTO UnknownGitVersion
-
-FOR /f "delims=" %%a IN ('git describe --always') DO SET gitdescribe=%%a
-GOTO End
-
-:UnknownGitVersion
-SET gitdescribe=unknown
-
-:End
-ECHO #define LIBLINPHONE_GIT_VERSION "%gitdescribe%" > liblinphone_gitversion.h
-
-
-FOR /F "delims=" %%a IN ('findstr /B AC_INIT ..\..\..\configure.ac') DO (
- FOR /F "tokens=1,2,3 delims=[,]" %%1 IN ("%%a") DO (
- ECHO #define LIBLINPHONE_VERSION "%%3" > config.h
- )
-)
diff --git a/build/wp8/LibLinphoneTester-native/LibLinphoneTester-native.vcxproj b/build/wp8/LibLinphoneTester-native/LibLinphoneTester-native.vcxproj
index 6bc245f9f..e60a5a6dc 100644
--- a/build/wp8/LibLinphoneTester-native/LibLinphoneTester-native.vcxproj
+++ b/build/wp8/LibLinphoneTester-native/LibLinphoneTester-native.vcxproj
@@ -50,7 +50,7 @@
Level4
- $(ProjectDir)..\..\..\..\belle-sip\include;$(ProjectDir)..\..\..\oRTP\include;$(ProjectDir)..\..\..\mediastreamer2\include;$(ProjectDir)..\..\..\tester;$(ProjectDir)..\..\..\coreapi;$(ProjectDir)..\..\..\include;$(ProjectDir)..\..\..\..\cunit\build\wp8\cunit\$(Platform)\$(Configuration);$(SolutionDir)$(Platform)\$(Configuration)\include;%(AdditionalIncludeDirectories)
+ $(ProjectDir)..\..\..\..\belle-sip\include;$(ProjectDir)..\..\..\oRTP\include;$(ProjectDir)..\..\..\mediastreamer2\include;$(ProjectDir)..\..\..\tester;$(ProjectDir)..\..\..\coreapi;$(ProjectDir)..\..\..\include;$(ProjectDir)..\..\..\..\bcunit\build\wp8\bcunit\$(Platform)\$(Configuration);$(SolutionDir)$(Platform)\$(Configuration)\include;%(AdditionalIncludeDirectories)
WIN32;_WINDOWS;_WINRT_DLL;_CRT_SECURE_NO_WARNINGS;HAVE_CU_GET_SUITE;IN_LINPHONE;%(PreprocessorDefinitions)
Default
NotUsing
@@ -124,7 +124,7 @@
{4c225a82-800b-427b-ba7b-61686a9b347f}
-
+
{902daf1d-ebf1-4d03-b598-143500a50ab4}
diff --git a/build/wp8/LibLinphoneTester-native/linphone-tester-native.cpp b/build/wp8/LibLinphoneTester-native/linphone-tester-native.cpp
index 941b50916..3c511f0fd 100644
--- a/build/wp8/LibLinphoneTester-native/linphone-tester-native.cpp
+++ b/build/wp8/LibLinphoneTester-native/linphone-tester-native.cpp
@@ -2,7 +2,7 @@
#include "linphone-tester-native.h"
#include "ortp/logging.h"
-#include "cunit/Util.h"
+#include "bcunit/Util.h"
using namespace linphone_tester_native;
@@ -52,7 +52,7 @@ static void LinphoneNativeVerboseOutputTraceHandler(OrtpLogLevel lev, const char
LinphoneNativeGenericOutputTraceHandler(lev, fmt, args);
}
-static void CUnitNativeOutputTraceHandler(int lev, const char *fmt, va_list args)
+static void BCUnitNativeOutputTraceHandler(int lev, const char *fmt, va_list args)
{
nativeOutputTraceHandler(Raw, fmt, args);
}
@@ -87,7 +87,7 @@ void LinphoneTesterNative::run(Platform::String^ suiteName, Platform::String^ ca
} else {
linphone_core_enable_logs_with_cb(LinphoneNativeOutputTraceHandler);
}
- CU_set_trace_handler(CUnitNativeOutputTraceHandler);
+ CU_set_trace_handler(BCUnitNativeOutputTraceHandler);
liblinphone_tester_run_tests(wssuitename == all ? 0 : csuitename, wscasename == all ? 0 : ccasename);
}
diff --git a/build/wp8/LibLinphoneTester-wp8/LibLinphoneTester-wp8.sln b/build/wp8/LibLinphoneTester-wp8/LibLinphoneTester-wp8.sln
index cf0d4a086..42a854fb1 100644
--- a/build/wp8/LibLinphoneTester-wp8/LibLinphoneTester-wp8.sln
+++ b/build/wp8/LibLinphoneTester-wp8/LibLinphoneTester-wp8.sln
@@ -39,7 +39,7 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "speex", "..\..\..\..\speex\
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "speexdsp", "..\..\..\..\speex\build\wp8\speex\speexdsp.vcxproj", "{6BD78980-9C71-4341-8775-AD19E9EC7305}"
EndProject
-Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "cunit", "..\..\..\..\cunit\build\wp8\cunit\cunit.vcxproj", "{902DAF1D-EBF1-4D03-B598-143500A50AB4}"
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "bcunit", "..\..\..\..\bcunit\build\wp8\bcunit\bcunit.vcxproj", "{902DAF1D-EBF1-4D03-B598-143500A50AB4}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libmswasapi", "..\..\..\..\mswasapi\mswasapi\mswasapi.vcxproj", "{D22BD217-D0F8-4274-9B3A-F3F35F46482C}"
ProjectSection(ProjectDependencies) = postProject
diff --git a/cmake/FindCUnit.cmake b/cmake/FindGtkMacIntegration.cmake
similarity index 52%
rename from cmake/FindCUnit.cmake
rename to cmake/FindGtkMacIntegration.cmake
index 91bc67fbd..17c8efc8c 100644
--- a/cmake/FindCUnit.cmake
+++ b/cmake/FindGtkMacIntegration.cmake
@@ -1,5 +1,5 @@
############################################################################
-# FindCUnit.txt
+# FindGtkMacIntegration.txt
# Copyright (C) 2015 Belledonne Communications, Grenoble France
#
############################################################################
@@ -20,39 +20,35 @@
#
############################################################################
#
-# - Find the CUnit include file and library
+# - Find the libgtkmacintegration include file and library
#
-# CUNIT_FOUND - system has CUnit
-# CUNIT_INCLUDE_DIRS - the CUnit include directory
-# CUNIT_LIBRARIES - The libraries needed to use CUnit
+# GTKMACINTEGRATION_FOUND - system has libgtkmacintegration
+# GTKMACINTEGRATION_INCLUDE_DIRS - the libgtkmacintegration include directory
+# GTKMACINTEGRATION_LIBRARIES - The libraries needed to use libgtkmacintegration
+# GTKMACINTEGRATION_CPPFLAGS - The cflags needed to use libgtkmacintegration
-include(CheckIncludeFile)
-include(CheckLibraryExists)
-
-set(_CUNIT_ROOT_PATHS
+set(_GTKMACINTEGRATION_ROOT_PATHS
${CMAKE_INSTALL_PREFIX}
)
-find_path(CUNIT_INCLUDE_DIRS
- NAMES CUnit/CUnit.h
- HINTS _CUNIT_ROOT_PATHS
- PATH_SUFFIXES include
+find_path(GTKMACINTEGRATION_INCLUDE_DIRS
+ NAMES gtkosxapplication.h
+ HINTS _GTKMACINTEGRATION_ROOT_PATHS
+ PATH_SUFFIXES include/gtkmacintegration-gtk2 include/gtkmacintegration
)
-if(CUNIT_INCLUDE_DIRS)
- set(HAVE_CUNIT_CUNIT_H 1)
-endif()
-
-find_library(CUNIT_LIBRARIES
- NAMES cunit
- HINTS ${_CUNIT_ROOT_PATHS}
+find_library(GTKMACINTEGRATION_LIBRARIES
+ NAMES gtkmacintegration-gtk2 gtkmacintegration
+ HINTS ${_GTKMACINTEGRATION_ROOT_PATHS}
PATH_SUFFIXES bin lib
)
+set(GTKMACINTEGRATION_CPPFLAGS "-DMAC_INTEGRATION")
+
include(FindPackageHandleStandardArgs)
-find_package_handle_standard_args(CUnit
+find_package_handle_standard_args(GTKMACINTEGRATION
DEFAULT_MSG
- CUNIT_INCLUDE_DIRS CUNIT_LIBRARIES
+ GTKMACINTEGRATION_INCLUDE_DIRS GTKMACINTEGRATION_LIBRARIES GTKMACINTEGRATION_CPPFLAGS
)
-mark_as_advanced(CUNIT_INCLUDE_DIRS CUNIT_LIBRARIES)
+mark_as_advanced(GTKMACINTEGRATION_INCLUDE_DIRS GTKMACINTEGRATION_LIBRARIES GTKMACINTEGRATION_CPPFLAGS)
diff --git a/cmake/FindIconv.cmake b/cmake/FindIconv.cmake
index 8497e9f58..d238426c7 100644
--- a/cmake/FindIconv.cmake
+++ b/cmake/FindIconv.cmake
@@ -26,13 +26,16 @@
# ICONV_INCLUDE_DIRS - the libiconv include directory
# ICONV_LIBRARIES - The libraries needed to use libiconv
-set(_ICONV_ROOT_PATHS
- ${CMAKE_INSTALL_PREFIX}
-)
+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_ROOT_PATHS
+ HINTS "${ICONV_HINTS}"
PATH_SUFFIXES include
)
@@ -42,8 +45,7 @@ endif()
find_library(ICONV_LIBRARIES
NAMES iconv
- HINTS ${_ICONV_ROOT_PATHS}
- PATH_SUFFIXES bin lib
+ HINTS "${ICONV_LIBRARIES_HINTS}"
)
include(FindPackageHandleStandardArgs)
diff --git a/cmake/FindSqlite3.cmake b/cmake/FindSqlite3.cmake
index e634b0813..0dd14d183 100644
--- a/cmake/FindSqlite3.cmake
+++ b/cmake/FindSqlite3.cmake
@@ -26,13 +26,16 @@
# SQLITE3_INCLUDE_DIRS - the sqlite3 include directory
# SQLITE3_LIBRARIES - The libraries needed to use sqlite3
-set(_SQLITE3_ROOT_PATHS
- ${CMAKE_INSTALL_PREFIX}
-)
+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_ROOT_PATHS
+ HINTS "${SQLITE3_HINTS}"
PATH_SUFFIXES include
)
@@ -42,8 +45,7 @@ endif()
find_library(SQLITE3_LIBRARIES
NAMES sqlite3
- HINTS ${_SQLITE3_ROOT_PATHS}
- PATH_SUFFIXES bin lib
+ HINTS "${SQLITE3_LIBRARIES_HINTS}"
)
include(FindPackageHandleStandardArgs)
diff --git a/cmake/FindXML2.cmake b/cmake/FindXML2.cmake
index 3f35ab497..da801bcd0 100644
--- a/cmake/FindXML2.cmake
+++ b/cmake/FindXML2.cmake
@@ -26,13 +26,16 @@
# XML2_INCLUDE_DIRS - the libxml2 include directory
# XML2_LIBRARIES - The libraries needed to use libxml2
-set(_XML2_ROOT_PATHS
- ${CMAKE_INSTALL_PREFIX}
-)
+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_ROOT_PATHS
+ HINTS "${XML2_HINTS}"
PATH_SUFFIXES include/libxml2
)
@@ -42,8 +45,7 @@ endif()
find_library(XML2_LIBRARIES
NAMES xml2
- HINTS ${_XML2_ROOT_PATHS}
- PATH_SUFFIXES bin lib
+ HINTS "${XML2_LIBRARIES_HINTS}"
)
include(FindPackageHandleStandardArgs)
diff --git a/cmake/FindZlib.cmake b/cmake/FindZlib.cmake
index 1f42aaf81..8f7e23430 100644
--- a/cmake/FindZlib.cmake
+++ b/cmake/FindZlib.cmake
@@ -26,13 +26,16 @@
# ZLIB_INCLUDE_DIRS - the zlib include directory
# ZLIB_LIBRARIES - The libraries needed to use zlib
-set(_ZLIB_ROOT_PATHS
- ${CMAKE_INSTALL_PREFIX}
-)
+if(APPLE AND NOT IOS)
+ set(ZLIB_HINTS "/usr")
+endif()
+if(ZLIB_HINTS)
+ set(ZLIB_LIBRARIES_HINTS "${ZLIB_HINTS}/lib")
+endif()
find_path(ZLIB_INCLUDE_DIRS
NAMES zlib.h
- HINTS _ZLIB_ROOT_PATHS
+ HINTS "${ZLIB_HINTS}"
PATH_SUFFIXES include
)
@@ -41,21 +44,14 @@ if(ZLIB_INCLUDE_DIRS)
endif()
if(ENABLE_STATIC)
- if(IOS)
- set(_ZLIB_STATIC_NAMES z)
- else()
- set(_ZLIB_STATIC_NAMES zstatic zlibstatic zlibstaticd)
- endif()
find_library(ZLIB_LIBRARIES
- NAMES ${_ZLIB_STATIC_NAMES}
- HINTS ${_ZLIB_ROOT_PATHS}
- PATH_SUFFIXES bin lib
+ NAMES zstatic zlibstatic zlibstaticd z
+ HINTS "${ZLIB_LIBRARIES_HINTS}"
)
else()
find_library(ZLIB_LIBRARIES
NAMES z zlib zlibd
- HINTS ${_ZLIB_ROOT_PATHS}
- PATH_SUFFIXES bin lib
+ HINTS "${ZLIB_LIBRARIES_HINTS}"
)
endif()
diff --git a/cmake/LinphoneConfig.cmake.in b/cmake/LinphoneConfig.cmake.in
index 62d0fa74e..2bbdf4118 100644
--- a/cmake/LinphoneConfig.cmake.in
+++ b/cmake/LinphoneConfig.cmake.in
@@ -29,17 +29,34 @@
# LINPHONE_CPPFLAGS - The compilation flags needed to use linphone
# LINPHONE_LDFLAGS - The linking flags needed to use linphone
-include("${CMAKE_CURRENT_LIST_DIR}/LinphoneTargets.cmake")
+if(NOT LINPHONE_BUILDER_GROUP_EXTERNAL_SOURCE_PATH_BUILDERS)
+ include("${CMAKE_CURRENT_LIST_DIR}/LinphoneTargets.cmake")
+endif()
+
find_package(Mediastreamer2 REQUIRED)
find_package(BelleSIP REQUIRED)
if(@ENABLE_TUNNEL@)
- find_package(Tunnel)
+ if(LINPHONE_BUILDER_GROUP_EXTERNAL_SOURCE_PATH_BUILDERS)
+ include("${EP_tunnel_CONFIG_DIR}/TunnelConfig.cmake")
+ else()
+ find_package(Tunnel)
+ endif()
+endif()
+if(@ENABLE_VCARD@)
+ if(LINPHONE_BUILDER_GROUP_EXTERNAL_SOURCE_PATH_BUILDERS)
+ include("${EP_belcard_CONFIG_DIR}/BelcardConfig.cmake")
+ else()
+ find_package(Belcard)
+ endif()
endif()
get_filename_component(LINPHONE_CMAKE_DIR "${CMAKE_CURRENT_LIST_FILE}" PATH)
set(LINPHONE_INCLUDE_DIRS "${LINPHONE_CMAKE_DIR}/../../../include")
-set(LINPHONE_LIBRARIES BelledonneCommunications::linphone)
-set(LINPHONE_LDFLAGS @LINK_FLAGS@)
+if(@ENABLE_SHARED@)
+ set(LINPHONE_LIBRARIES linphone)
+else()
+ set(LINPHONE_LIBRARIES linphone-static)
+endif()
list(APPEND LINPHONE_INCLUDE_DIRS ${MEDIASTREAMER2_INCLUDE_DIRS} ${BELLESIP_INCLUDE_DIRS})
list(APPEND LINPHONE_LIBRARIES ${MEDIASTREAMER2_LIBRARIES} ${BELLESIP_LIBRARIES})
set(LINPHONE_CPPFLAGS "${MEDIASTREAMER2_CPPFLAGS}")
@@ -48,4 +65,8 @@ if(TUNNEL_FOUND)
list(APPEND LINPHONE_INCLUDE_DIRS ${TUNNEL_INCLUDE_DIRS})
list(APPEND LINPHONE_LIBRARIES ${TUNNEL_LIBRARIES})
endif()
+if(BELCARD_FOUND)
+ list(APPEND LINPHONE_INCLUDE_DIRS ${BELCARD_INCLUDE_DIRS})
+ list(APPEND LINPHONE_LIBRARIES ${BELCARD_LIBRARIES})
+endif()
set(LINPHONE_FOUND 1)
diff --git a/config.h.cmake b/config.h.cmake
index 6e8fd92dd..c608fb77c 100644
--- a/config.h.cmake
+++ b/config.h.cmake
@@ -38,9 +38,11 @@
#define PACKAGE_SOUND_DIR "${PACKAGE_SOUND_DIR}"
#cmakedefine BUILD_WIZARD
+#cmakedefine HAVE_GTK_OSX 1
#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 ENABLE_NLS 1
diff --git a/configure.ac b/configure.ac
index 191c7fd7d..80c7c7b4d 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1,6 +1,6 @@
dnl Process this file with autoconf to produce a configure script.
-AC_INIT([linphone],[3.8.5],[linphone-developers@nongnu.org])
+AC_INIT([linphone],[3.10.2],[linphone-developers@nongnu.org])
AC_CANONICAL_SYSTEM
AC_CONFIG_SRCDIR([coreapi/linphonecore.c])
@@ -17,7 +17,7 @@ if test "$LINPHONE_EXTRA_VERSION" != "" ;then
LINPHONE_VERSION=$LINPHONE_VERSION.${LINPHONE_EXTRA_VERSION}
fi
-LIBLINPHONE_SO_CURRENT=7 dnl increment this number when you add/change/remove an interface
+LIBLINPHONE_SO_CURRENT=9 dnl increment this number when you add/change/remove an interface
LIBLINPHONE_SO_REVISION=0 dnl increment this number when you change source code, without changing interfaces; set to 0 when incrementing CURRENT
LIBLINPHONE_SO_AGE=0 dnl increment this number when you add an interface, set to 0 if you remove an interface
@@ -39,6 +39,7 @@ AC_CONFIG_MACRO_DIR([m4])
dnl do not put anythingelse before AC_PROG_CC unless checking if macro still work for clang
AC_PROG_CXX(["xcrun clang++" g++])
AC_PROG_CC(["xcrun clang" gcc])
+AC_PROG_OBJC(["xcrun clang" gcc])
gl_LD_OUTPUT_DEF
@@ -132,7 +133,7 @@ AC_CONFIG_COMMANDS([libtool-hacking],
dnl Add the languages which your application supports here.
PKG_PROG_PKG_CONFIG
-ALL_LINGUAS=$(cd po && echo *.po | sed 's/\.po//g')
+ALL_LINGUAS=$(cd $srcdir/po && echo *.po | sed 's/\.po//g')
AC_SUBST(ALL_LINGUAS)
AC_DEFINE_UNQUOTED(LINPHONE_ALL_LANGS, "$ALL_LINGUAS", [All supported languages])
@@ -157,6 +158,11 @@ AC_SUBST([GETTEXT_PACKAGE])
AC_DEFINE_UNQUOTED(GETTEXT_PACKAGE,"$GETTEXT_PACKAGE",[The name of the gettext package name])
dnl AC_CHECK_LIB(intl,libintl_gettext)
+PKG_CHECK_MODULES(BCTOOLBOX, bctoolbox, [found_bctoolbox=yes],[found_bctoolbox=no])
+if test "x$found_bctoolbox" != "xyes" ; then
+ AC_MSG_ERROR(["Could not find bctoolbox (required dependency)"])
+fi
+
AC_CHECK_FUNCS([get_current_dir_name strndup stpcpy] )
AC_ARG_ENABLE(x11,
@@ -595,6 +601,8 @@ AC_ARG_WITH(ffmpeg,
[ ffmpegdir=/usr ]
)
+AM_CONDITIONAL([BUILD_MACOS], [test "x$build_macos" = "xyes"])
+
if test "$video" = "true"; then
if test "$enable_x11" = "true"; then
@@ -768,12 +776,15 @@ AC_ARG_ENABLE(strict,
)
STRICT_OPTIONS="-Wall -Wuninitialized"
-STRICT_OPTIONS_CC="-Wdeclaration-after-statement "
+STRICT_OPTIONS_CC="-Wdeclaration-after-statement -Wstrict-prototypes"
STRICT_OPTIONS_CXX=""
#for clang
case $CC in
+ *gcc*)
+ STRICT_OPTIONS="$STRICT_OPTIONS -fno-inline-small-functions"
+ ;;
*clang*)
STRICT_OPTIONS="$STRICT_OPTIONS -Qunused-arguments "
#disabled due to wrong optimization false positive with small string
@@ -791,7 +802,7 @@ case "$target_os" in
;;
esac
if test "$strictness" = "yes" ; then
- STRICT_OPTIONS="$STRICT_OPTIONS -Werror"
+ STRICT_OPTIONS="$STRICT_OPTIONS -Werror -Wextra -Wno-unused-parameter -Wno-error=deprecated-declarations -Wno-error=strict-prototypes -Wno-missing-field-initializers"
CFLAGS="$CFLAGS -fno-strict-aliasing"
fi
@@ -873,46 +884,81 @@ AC_ARG_ENABLE(tunnel,
)
AM_CONDITIONAL(BUILD_TUNNEL, test x$enable_tunnel = xtrue)
if test x$enable_tunnel = xtrue; then
- PKG_CHECK_MODULES(TUNNEL, tunnel >= 0.3.3)
+ PKG_CHECK_MODULES(TUNNEL, tunnel >= 0.6.0)
AC_DEFINE(TUNNEL_ENABLED,1,[Tells tunnel extension is built-in])
fi
-AC_ARG_ENABLE(msg-storage,
- [AS_HELP_STRING([--enable-msg-storage=[yes/no]], [Turn on compilation of message storage (default=auto)])],
+AC_ARG_ENABLE(vcard,
+ [AS_HELP_STRING([--enable-vcard=[yes/no]], [Turn on compilation of vcard (default=auto)])],
[case "${enableval}" in
- yes) enable_msg_storage=true ;;
- no) enable_msg_storage=false ;;
- *) AC_MSG_ERROR(bad value ${enableval} for --enable-msg-storage) ;;
+ yes) enable_vcard=true ;;
+ no) enable_vcard=false ;;
+ *) AC_MSG_ERROR(bad value ${enableval} for --enable-vcard) ;;
esac],
- [enable_msg_storage=auto]
+ [enable_vcard=auto]
)
-if test x$enable_msg_storage != xfalse; then
+if test x$enable_vcard != xfalse; then
+ PKG_CHECK_MODULES(BELCARD, belcard, [found_vcard=yes],[found_vcard=no])
+ if test "$found_vcard" = "no"; then
+ dnl Check the lib presence in case the PKG-CONFIG version is not found
+ AC_LANG_CPLUSPLUS
+ AC_CHECK_LIB(belcard, main, [BELCARD_LIBS+=" -lbelr -lbelcard"; found_vcard=yes], [foo=bar])
+ AC_LANG_C
+ fi
+ if test "$found_vcard" = "yes"; then
+ BELCARD_CFLAGS+=" -DVCARD_ENABLED"
+ enable_vcard=true
+ else
+ if test x$enable_vcard = xtrue; then
+ AC_MSG_ERROR([belcard, required for vcard support, not found])
+ fi
+ enable_vcard=false
+ fi
+
+ AC_SUBST(BELCARD_CFLAGS)
+ AC_SUBST(BELCARD_LIBS)
+fi
+
+AM_CONDITIONAL(BUILD_VCARD, test x$enable_vcard = xtrue)
+
+AC_ARG_ENABLE(sqlite-storage,
+ [AS_HELP_STRING([--sqlite-msg-storage=[yes/no]], [Turn on compilation of sqlite storage for call history, messages, friends (default=auto)])],
+ [case "${enableval}" in
+ yes) enable_sqlite_storage=true ;;
+ no) enable_sqlite_storage=false ;;
+ *) AC_MSG_ERROR(bad value ${enableval} for --enable-sqlite-storage) ;;
+ esac],
+ [enable_sqlite_storage=auto]
+)
+
+if test x$enable_sqlite_storage != xfalse; then
PKG_CHECK_MODULES(SQLITE3,[sqlite3 >= 3.6.0],[found_sqlite=yes],[found_sqlite=no])
if test "$found_sqlite" = "no"; then
dnl Check the lib presence in case the PKG-CONFIG version is not found
AC_CHECK_LIB(sqlite3, sqlite3_open, [SQLITE3_LIBS+=" -lsqlite3 "; found_sqlite=yes], [foo=bar])
fi
if test "$found_sqlite" = "yes"; then
- SQLITE3_CFLAGS+="-DMSG_STORAGE_ENABLED"
+ SQLITE3_CFLAGS+=" -DSQLITE_STORAGE_ENABLED"
if test "$build_macos" = "yes" -o "$ios_found" = "yes"; then
SQLITE3_LIBS+=" -liconv"
fi
- enable_msg_storage=true
+ enable_sqlite_storage=true
else
- if test x$enable_msg_storage = xtrue; then
- AC_MSG_ERROR([sqlite3, required for message storage, not found])
+ if test x$enable_sqlite_storage = xtrue; then
+ AC_MSG_ERROR([sqlite3, required for storage, not found])
fi
- enable_msg_storage=false
+ enable_sqlite_storage=false
fi
AC_SUBST(SQLITE3_CFLAGS)
AC_SUBST(SQLITE3_LIBS)
fi
-AM_CONDITIONAL(BUILD_MSG_STORAGE, test x$enable_msg_storage = xtrue)
+AM_CONDITIONAL(BUILD_SQLITE_STORAGE, test x$enable_sqlite_storage = xtrue)
-PKG_CHECK_MODULES(BELLESIP, [belle-sip >= 1.4.0])
+
+PKG_CHECK_MODULES(BELLESIP, [belle-sip >= 1.4.2])
SIPSTACK_CFLAGS="$BELLESIP_CFLAGS"
SIPSTACK_LIBS="$BELLESIP_LIBS"
@@ -962,44 +1008,13 @@ AC_ARG_ENABLE(tests,
)
AM_CONDITIONAL(ENABLE_TESTS, test x$tests_enabled = xyes)
-PKG_CHECK_MODULES(CUNIT, cunit, [found_cunit=yes],[found_cunit=no])
+PKG_CHECK_MODULES(BCTOOLBOXTESTER, bctoolbox-tester, [found_pkg_config_bctoolboxtester=yes],[found_pkg_config_bctoolboxtester=no])
-if test "$found_cunit" = "no" ; then
- AC_CHECK_HEADERS(CUnit/CUnit.h,
- [
- AC_CHECK_LIB(cunit,CU_add_suite,[
- found_cunit=yes
- CUNIT_LIBS+=" -lcunit"
- ])
-
- ])
+AM_CONDITIONAL([ENABLE_TESTS], [test x$found_pkg_config_bctoolboxtester = xyes && test x$tests_enabled != xfalse])
+if test "$found_pkg_config_bctoolboxtester" = "no" ; then
+ AC_MSG_WARN([Could not find bctoolbox-tester wrapper, tests are not compiled.])
fi
-case "$target_os" in
- *darwin*)
- #hack for macport
- CUNIT_LIBS+=" -lncurses"
- ;;
-esac
-AM_CONDITIONAL([BUILD_CUNIT_TESTS], [test x$found_cunit = xyes && test x$tests_enabled != xfalse])
-if test "$found_cunit" = "no" ; then
- AC_MSG_WARN([Could not find cunit framework, tests are not compiled.])
-else
- AC_CHECK_LIB(cunit,CU_get_suite,[
- AC_DEFINE(HAVE_CU_GET_SUITE,1,[defined when CU_get_suite is available])
- ],[foo=bar],[$CUNIT_LIBS])
-
- AC_CHECK_LIB(cunit,CU_curses_run_tests,[
- AC_DEFINE(HAVE_CU_CURSES,1,[defined when CU_curses_run_tests is available])
- ],[foo=bar],[$CUNIT_LIBS])
-fi
-
-case "$target_os" in
- *linux*)
- # Eliminate -lstdc++ addition to postdeps for cross compiles.
- postdeps_CXX=`echo " $postdeps_CXX " | sed 's, -lstdc++ ,,g'`
- ;;
-esac
dnl ##################################################
@@ -1022,6 +1037,11 @@ else
fi
AM_CONDITIONAL(HAVE_DOXYGEN, test "$DOXYGEN" != "false")
+AC_PATH_PROG([SIPP],[sipp],[false])
+if test "x$SIPP" != "xfalse" ; then
+ AC_DEFINE(HAVE_SIPP,1,[defined when SIPP is available])
+ AC_DEFINE_UNQUOTED(SIPP_COMMAND,"$SIPP",[defined when SIPP is available])
+fi
AC_CONFIG_FILES([
Makefile
@@ -1039,6 +1059,7 @@ AC_CONFIG_FILES([
tester/Makefile
gtk/Makefile
console/Makefile
+ daemon/Makefile
share/Makefile
share/C/Makefile
share/fr/Makefile
@@ -1064,7 +1085,8 @@ printf "* %-30s %s\n" "GTK interface" $gtk_ui
printf "* %-30s %s\n" "Account assistant" $build_wizard
printf "* %-30s %s\n" "Console interface" $console_ui
printf "* %-30s %s\n" "Tools" $build_tools
-printf "* %-30s %s\n" "Message storage" $enable_msg_storage
+printf "* %-30s %s\n" "Sqlite storage" $enable_sqlite_storage
+printf "* %-30s %s\n" "VCard support" $enable_vcard
printf "* %-30s %s\n" "IM encryption" $lime
printf "* %-30s %s\n" "uPnP support" $build_upnp
printf "* %-30s %s\n" "LDAP support" $enable_ldap
diff --git a/console/CMakeLists.txt b/console/CMakeLists.txt
index 696d33a43..d4dc9d259 100644
--- a/console/CMakeLists.txt
+++ b/console/CMakeLists.txt
@@ -29,16 +29,32 @@ set(LINPHONECSH_SOURCE_FILES
shell.c
)
+apply_compile_flags(LINPHONEC_SOURCE_FILES "CPP" "C")
+if(MSVC)
+ get_source_file_property(COMMANDS_C_COMPILE_FLAGS commands.c COMPILE_FLAGS)
+ set(COMMANDS_C_COMPILE_FLAGS "${COMMANDS_C_COMPILE_FLAGS} /wd4996") # Disable "was declared deprecated" warnings
+ set_source_files_properties(commands.c PROPERTY COMPILE_FLAGS "${COMMANDS_C_COMPILE_FLAGS}")
+endif()
+
add_executable(linphonec ${LINPHONEC_SOURCE_FILES})
-target_link_libraries(linphonec linphone)
+target_link_libraries(linphonec ${LINPHONE_LIBS_FOR_TOOLS} ${BCTOOLBOX_CORE_LIBRARIES} ${ORTP_LIBRARIES} ${MEDIASTREAMER2_LIBRARIES})
+set_target_properties(linphonec PROPERTIES LINK_FLAGS "${LINPHONE_LDFLAGS}")
+
+if(INTL_FOUND)
+ target_link_libraries(linphonec ${INTL_LIBRARIES})
+endif()
if(WIN32)
add_executable(linphoned WIN32 ${LINPHONEC_SOURCE_FILES})
- target_link_libraries(linphoned linphone)
+ target_link_libraries(linphoned ${LINPHONE_LIBS_FOR_TOOLS} ${BCTOOLBOX_CORE_LIBRARIES} ${ORTP_LIBRARIES} ${MEDIASTREAMER2_LIBRARIES})
+ if(INTL_FOUND)
+ target_link_libraries(linphoned ${INTL_LIBRARIES})
+ endif()
endif()
add_executable(linphonecsh ${LINPHONECSH_SOURCE_FILES})
-target_link_libraries(linphonecsh linphone)
+target_link_libraries(linphonecsh ${LINPHONE_LIBS_FOR_TOOLS} ${ORTP_LIBRARIES})
+set_target_properties(linphonecsh PROPERTIES LINK_FLAGS "${LINPHONE_LDFLAGS}")
set(INSTALL_TARGETS linphonec linphonecsh)
if(WIN32)
@@ -46,8 +62,8 @@ if(WIN32)
endif()
install(TARGETS ${INSTALL_TARGETS}
- RUNTIME DESTINATION bin
- LIBRARY DESTINATION lib
- ARCHIVE DESTINATION lib
+ RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+ LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+ ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE
)
diff --git a/console/Makefile.am b/console/Makefile.am
index 482948329..c43570e6f 100644
--- a/console/Makefile.am
+++ b/console/Makefile.am
@@ -15,7 +15,8 @@ COMMON_CFLAGS=\
$(VIDEO_CFLAGS) \
$(READLINE_CFLAGS) \
$(SQLITE3_CFLAGS) \
- $(LIBXML2_CFLAGS)
+ $(LIBXML2_CFLAGS) \
+ $(BCTOOLBOX_CFLAGS)
if BUILD_CONSOLE
@@ -32,7 +33,8 @@ linphonec_LDADD=$(top_builddir)/coreapi/liblinphone.la \
$(SQLITE3_LIBS) \
$(X11_LIBS) \
$(BELLESIP_LIBS) \
- $(LIBXML2_LIBS)
+ $(LIBXML2_LIBS) \
+ $(BCTOOLBOX_LIBS)
if BUILD_WIN32
#special build of linphonec to detach from the windows console
diff --git a/console/commands.c b/console/commands.c
index 75dbbb688..72510bd65 100644
--- a/console/commands.c
+++ b/console/commands.c
@@ -35,7 +35,7 @@
#include "linphonec.h"
#include "lpconfig.h"
-#ifndef WIN32
+#ifndef _WIN32
#include
#include
#endif
@@ -118,7 +118,7 @@ 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
+#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);
@@ -133,8 +133,8 @@ static LPC_COMMAND *lpc_find_command(const char *name);
void linphonec_out(const char *fmt,...);
-VideoParams lpc_video_params={-1,-1,-1,-1,0,TRUE};
-VideoParams lpc_preview_params={-1,-1,-1,-1,0,TRUE};
+VideoParams lpc_video_params={-1,-1,-1,-1,NULL,TRUE,FALSE};
+VideoParams lpc_preview_params={-1,-1,-1,-1,NULL,TRUE,FALSE};
/***************************************************************************
*
@@ -212,7 +212,8 @@ static LPC_COMMAND commands[] = {
"'ipv6 disable' : do not use ipv6 network."
},
{ "mute", lpc_cmd_mute_mic,
- "Mute microphone and suspend voice transmission."
+ "Mute microphone and suspend voice transmission.",
+ NULL
},
{ "nat", lpc_cmd_nat, "Set nat address",
"'nat' : show nat settings.\n"
@@ -228,7 +229,8 @@ static LPC_COMMAND commands[] = {
"'play ' : play a wav file."
},
{ "playbackgain", lpc_cmd_playback_gain,
- "Adjust playback gain."
+ "Adjust playback gain.",
+ NULL
},
{ "proxy", lpc_cmd_proxy, "Manage proxies",
"'proxy list' : list all proxy setups.\n"
@@ -269,7 +271,8 @@ static LPC_COMMAND commands[] = {
"'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."
+ "Unmute microphone and resume voice transmission.",
+ NULL
},
{ "webcam", lpc_cmd_webcam, "Manage webcams",
"'webcam list' : list all known devices.\n"
@@ -287,10 +290,10 @@ static LPC_COMMAND advanced_commands[] = {
"'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" },
+ { "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"
@@ -327,8 +330,9 @@ static LPC_COMMAND advanced_commands[] = {
{ "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"
-},
+ { "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"
@@ -367,7 +371,8 @@ static LPC_COMMAND advanced_commands[] = {
"'ringback disable'\t: Disable playing of ringback tone to callers\n"
},
{ "redirect", lpc_cmd_redirect, "Redirect an incoming call",
- "'redirect '\t: Redirect all pending incoming calls to the \n"
+ "'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"
@@ -414,7 +419,8 @@ linphonec_parse_command_line(LinphoneCore *lc, char *cl)
{
while ( isdigit(*cl) || *cl == '#' || *cl == '*' )
{
- linphone_core_send_dtmf(lc, *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;
@@ -564,7 +570,7 @@ lpc_cmd_call(LinphoneCore *lc, char *args)
}
{
LinphoneCall *call;
- LinphoneCallParams *cp=linphone_core_create_default_call_parameters (lc);
+ LinphoneCallParams *cp=linphone_core_create_call_params (lc, NULL);
char *opt1,*opt2;
if ( linphone_core_in_call(lc) )
{
@@ -598,7 +604,7 @@ lpc_cmd_call(LinphoneCore *lc, char *args)
static int
lpc_cmd_calls(LinphoneCore *lc, char *args){
- const MSList *calls = linphone_core_get_calls(lc);
+ const bctbx_list_t *calls = linphone_core_get_calls(lc);
if(calls)
{
lpc_display_call_states(lc);
@@ -638,11 +644,11 @@ lpc_cmd_chat(LinphoneCore *lc, char *args)
return 1;
}
-const char *linphonec_get_callee(){
+const char *linphonec_get_callee(void){
return callee_name;
}
-const char *linphonec_get_caller(){
+const char *linphonec_get_caller(void){
return caller_name;
}
@@ -663,7 +669,7 @@ lpc_cmd_transfer(LinphoneCore *lc, char *args)
int n=sscanf(args,"%255s %265s %li",arg1,arg2,&id2);
if (n==1 || isalpha(*arg1)){
call=linphone_core_get_current_call(lc);
- if (call==NULL && ms_list_size(linphone_core_get_calls(lc))==1){
+ if (call==NULL && bctbx_list_size(linphone_core_get_calls(lc))==1){
call=(LinphoneCall*)linphone_core_get_calls(lc)->data;
}
refer_to=args;
@@ -732,24 +738,44 @@ lpc_cmd_terminate(LinphoneCore *lc, char *args)
static int
lpc_cmd_redirect(LinphoneCore *lc, char *args){
- const MSList *elem;
+ 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;
}
- while(elem!=NULL){
- LinphoneCall *call=(LinphoneCall*)elem->data;
- if (linphone_call_get_state(call)==LinphoneCallIncomingReceived){
- linphone_core_redirect_call(lc,call,args);
- 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.");
+ if (strncmp(args, "all ", 4) == 0) {
+ while(elem!=NULL){
+ LinphoneCall *call=(LinphoneCall*)elem->data;
+ if (linphone_call_get_state(call)==LinphoneCallIncomingReceived){
+ if (linphone_core_redirect_call(lc,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;
+ long id;
+ int charRead;
+ if ( sscanf(args, "%li%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_core_redirect_call(lc,call,args+charRead) != 0) {
+ linphonec_out("Could not redirect call.\n");
+ }
+ }
+ } else return 0;
}
return 1;
}
@@ -758,7 +784,7 @@ static int
lpc_cmd_answer(LinphoneCore *lc, char *args){
if (!args)
{
- int nb=ms_list_size(linphone_core_get_calls(lc));
+ int nb=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) )
@@ -927,7 +953,7 @@ lpc_cmd_firewall(LinphoneCore *lc, char *args)
return 1;
}
-#ifndef WIN32
+#ifndef _WIN32
/* Helper function for processing freind names */
static int
lpc_friend_name(char **args, char **name)
@@ -1012,7 +1038,7 @@ lpc_cmd_friend(LinphoneCore *lc, char *args)
}
else if ( !strncmp(args, "add", 3) )
{
-#ifndef WIN32
+#ifndef _WIN32
char *name;
char addr[80];
char *addr_p = addr;
@@ -1040,7 +1066,7 @@ lpc_cmd_friend(LinphoneCore *lc, char *args)
linphonec_friend_add(lc, name, addr);
#else
LinphoneFriend *new_friend;
- new_friend = linphone_friend_new_with_address(args);
+ new_friend = linphone_core_create_friend_with_address(lc, args);
linphone_core_add_friend(lc, new_friend);
#endif
return 1;
@@ -1154,8 +1180,8 @@ lpc_cmd_proxy(LinphoneCore *lc, char *args)
static int
lpc_cmd_call_logs(LinphoneCore *lc, char *args)
{
- const MSList *elem=linphone_core_get_call_logs(lc);
- for (;elem!=NULL;elem=ms_list_next(elem))
+ 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);
@@ -1242,12 +1268,16 @@ static int lpc_cmd_soundcard(LinphoneCore *lc, char *args)
if (strcmp(arg1, "show")==0)
{
- 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));
+ 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;
}
@@ -1260,6 +1290,8 @@ static int lpc_cmd_soundcard(LinphoneCore *lc, char *args)
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++)
@@ -1275,6 +1307,7 @@ static int lpc_cmd_soundcard(LinphoneCore *lc, char *args)
linphonec_out("No such sound device\n");
return 1;
}
+
if (strcmp(arg1, "capture")==0)
{
const char *devname=linphone_core_get_capture_device(lc);
@@ -1403,7 +1436,7 @@ lpc_cmd_staticpic(LinphoneCore *lc, char *args)
if (strcmp(arg1, "fps")==0) {
if (arg2) {
- float fps = atof(arg2); /* FIXME: Handle not-a-float */
+ float fps = (float)atof(arg2); /* FIXME: Handle not-a-float */
linphone_core_set_static_picture_fps(lc, fps);
return 1;
} else {
@@ -1451,8 +1484,8 @@ static int lpc_cmd_resume(LinphoneCore *lc, char *args){
}
else
{
- const MSList *calls = linphone_core_get_calls(lc);
- int nbcalls=ms_list_size(calls);
+ const bctbx_list_t *calls = linphone_core_get_calls(lc);
+ int nbcalls=bctbx_list_size(calls);
if( nbcalls == 1)
{
if(linphone_core_resume_call(lc,calls->data) < 0)
@@ -1737,7 +1770,7 @@ linphonec_proxy_display(LinphoneProxyConfig *cfg)
static void linphonec_proxy_show(LinphoneCore *lc, int index)
{
- const MSList *elem;
+ 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){
@@ -1752,12 +1785,12 @@ static void linphonec_proxy_show(LinphoneCore *lc, int index)
static void
linphonec_proxy_list(LinphoneCore *lc)
{
- const MSList *proxies;
+ 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=ms_list_next(proxies),n++){
+ 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
@@ -1770,10 +1803,10 @@ linphonec_proxy_list(LinphoneCore *lc)
static void
linphonec_proxy_remove(LinphoneCore *lc, int index)
{
- const MSList *proxies;
+ const bctbx_list_t *proxies;
LinphoneProxyConfig *cfg;
proxies=linphone_core_get_proxy_config_list(lc);
- cfg=(LinphoneProxyConfig*)ms_list_nth_data(proxies,index);
+ cfg=(LinphoneProxyConfig*)bctbx_list_nth_data(proxies,index);
if (cfg==NULL){
linphonec_out("No such proxy.\n");
return;
@@ -1785,10 +1818,10 @@ linphonec_proxy_remove(LinphoneCore *lc, int index)
static int
linphonec_proxy_use(LinphoneCore *lc, int index)
{
- const MSList *proxies;
+ const bctbx_list_t *proxies;
LinphoneProxyConfig *cfg;
proxies=linphone_core_get_proxy_config_list(lc);
- cfg=(LinphoneProxyConfig*)ms_list_nth_data(proxies,index);
+ cfg=(LinphoneProxyConfig*)bctbx_list_nth_data(proxies,index);
if (cfg==NULL){
linphonec_out("No such proxy (try 'proxy list').");
return 0;
@@ -1800,19 +1833,19 @@ linphonec_proxy_use(LinphoneCore *lc, int index)
static void
linphonec_friend_display(LinphoneFriend *fr)
{
- LinphoneAddress *uri=linphone_address_clone(linphone_friend_get_address(fr));
- char *str;
+ const LinphoneAddress *addr = linphone_friend_get_address(fr);
+ char *str = NULL;
- linphonec_out("name: %s\n", linphone_address_get_display_name(uri));
- linphone_address_set_display_name(uri,NULL);
- str=linphone_address_as_string(uri);
+ 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 MSList *friend;
+ const bctbx_list_t *friend;
int n;
if (pat) {
@@ -1821,12 +1854,11 @@ linphonec_friend_list(LinphoneCore *lc, char *pat)
}
friend = linphone_core_get_friend_list(lc);
- for(n=0; friend!=NULL; friend=ms_list_next(friend), ++n )
+ for(n=0; friend!=NULL; friend=bctbx_list_next(friend), ++n )
{
if ( pat ) {
- const char *name = linphone_address_get_display_name(
- linphone_friend_get_address((LinphoneFriend*)friend->data));
- if (name && ! strstr(name, pat) ) continue;
+ 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);
@@ -1838,26 +1870,31 @@ linphonec_friend_list(LinphoneCore *lc, char *pat)
static int
linphonec_friend_call(LinphoneCore *lc, unsigned int num)
{
- const MSList *friend = linphone_core_get_friend_list(lc);
+ const bctbx_list_t *friend = linphone_core_get_friend_list(lc);
unsigned int n;
- char *addr;
+ char *addr_str;
- for(n=0; friend!=NULL; friend=ms_list_next(friend), ++n )
+ for(n=0; friend!=NULL; friend=bctbx_list_next(friend), ++n )
{
if ( n == num )
{
int ret;
- addr = linphone_address_as_string(linphone_friend_get_address((LinphoneFriend*)friend->data));
- ret=lpc_cmd_call(lc, addr);
- ms_free(addr);
- return 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
+#ifndef _WIN32
static int
linphonec_friend_add(LinphoneCore *lc, const char *name, const char *addr)
{
@@ -1866,7 +1903,7 @@ linphonec_friend_add(LinphoneCore *lc, const char *name, const char *addr)
char url[PATH_MAX];
snprintf(url, PATH_MAX, "%s <%s>", name, addr);
- newFriend = linphone_friend_new_with_address(url);
+ newFriend = linphone_core_create_friend_with_address(lc, url);
linphone_core_add_friend(lc, newFriend);
return 0;
}
@@ -1875,10 +1912,10 @@ linphonec_friend_add(LinphoneCore *lc, const char *name, const char *addr)
static int
linphonec_friend_delete(LinphoneCore *lc, int num)
{
- const MSList *friend = linphone_core_get_friend_list(lc);
- unsigned int n;
+ const bctbx_list_t *friend = linphone_core_get_friend_list(lc);
+ int n;
- for(n=0; friend!=NULL; friend=ms_list_next(friend), ++n )
+ for(n=0; friend!=NULL; friend=bctbx_list_next(friend), ++n )
{
if ( n == num )
{
@@ -1889,13 +1926,13 @@ linphonec_friend_delete(LinphoneCore *lc, int num)
if (-1 == num)
{
- unsigned int i;
+ int i;
for (i = 0 ; i < n ; i++)
linphonec_friend_delete(lc, 0);
return 0;
}
- linphonec_out("No such friend %u\n", num);
+ linphonec_out("No such friend %i\n", num);
return 1;
}
@@ -1912,13 +1949,12 @@ static int lpc_cmd_register(LinphoneCore *lc, char *args){
char proxy[512];
char passwd[512];
LinphoneProxyConfig *cfg;
- const MSList *elem;
+ const bctbx_list_t *elem;
if (!args)
{
/* it means that you want to register the default proxy */
- LinphoneProxyConfig *cfg=NULL;
- linphone_core_get_default_proxy(lc,&cfg);
+ LinphoneProxyConfig *cfg = linphone_core_get_default_proxy_config(lc);
if (cfg)
{
if(!linphone_proxy_config_is_registered(cfg)) {
@@ -1965,8 +2001,7 @@ static int lpc_cmd_register(LinphoneCore *lc, char *args){
}
static int lpc_cmd_unregister(LinphoneCore *lc, char *args){
- LinphoneProxyConfig *cfg=NULL;
- linphone_core_get_default_proxy(lc,&cfg);
+ 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);
@@ -1979,7 +2014,7 @@ static int lpc_cmd_unregister(LinphoneCore *lc, char *args){
static int lpc_cmd_duration(LinphoneCore *lc, char *args){
LinphoneCallLog *cl;
- const MSList *elem=linphone_core_get_call_logs(lc);
+ 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;
@@ -1994,7 +2029,7 @@ static int lpc_cmd_status(LinphoneCore *lc, char *args)
LinphoneProxyConfig *cfg;
if ( ! args ) return 0;
- linphone_core_get_default_proxy(lc,&cfg);
+ cfg = linphone_core_get_default_proxy_config(lc);
if (strstr(args,"register"))
{
if (cfg)
@@ -2110,7 +2145,7 @@ static int lpc_cmd_param(LinphoneCore *lc, char *args)
}
static int lpc_cmd_speak(LinphoneCore *lc, char *args){
-#ifndef WIN32
+#ifndef _WIN32
char voice[64];
char *sentence;
char cl[128];
@@ -2207,7 +2242,7 @@ static int lpc_cmd_codec(int type, LinphoneCore *lc, char *args){
static void linphonec_codec_list(int type, LinphoneCore *lc){
PayloadType *pt;
int index=0;
- const MSList *node=NULL;
+ const bctbx_list_t *node=NULL;
if (type == AUDIO) {
node=linphone_core_get_audio_codecs(lc);
@@ -2215,7 +2250,7 @@ static void linphonec_codec_list(int type, LinphoneCore *lc){
node=linphone_core_get_video_codecs(lc);
}
- for(;node!=NULL;node=ms_list_next(node)){
+ 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");
@@ -2226,7 +2261,7 @@ static void linphonec_codec_list(int type, LinphoneCore *lc){
static void linphonec_codec_enable(int type, LinphoneCore *lc, int sel_index){
PayloadType *pt;
int index=0;
- const MSList *node=NULL;
+ const bctbx_list_t *node=NULL;
if (type == AUDIO) {
node=linphone_core_get_audio_codecs(lc);
@@ -2234,7 +2269,7 @@ static void linphonec_codec_enable(int type, LinphoneCore *lc, int sel_index){
node=linphone_core_get_video_codecs(lc);
}
- for(;node!=NULL;node=ms_list_next(node)){
+ 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);
@@ -2247,7 +2282,7 @@ static void linphonec_codec_enable(int type, LinphoneCore *lc, int sel_index){
static void linphonec_codec_disable(int type, LinphoneCore *lc, int sel_index){
PayloadType *pt;
int index=0;
- const MSList *node=NULL;
+ const bctbx_list_t *node=NULL;
if (type == AUDIO) {
node=linphone_core_get_audio_codecs(lc);
@@ -2255,7 +2290,7 @@ static void linphonec_codec_disable(int type, LinphoneCore *lc, int sel_index){
node=linphone_core_get_video_codecs(lc);
}
- for(;node!=NULL;node=ms_list_next(node)){
+ 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);
@@ -2336,19 +2371,19 @@ static int lpc_cmd_echolimiter(LinphoneCore *lc, char *args){
static int lpc_cmd_mute_mic(LinphoneCore *lc, char *args)
{
- linphone_core_mute_mic(lc, 1);
+ linphone_core_enable_mic(lc, 0);
return 1;
}
static int lpc_cmd_unmute_mic(LinphoneCore *lc, char *args){
- linphone_core_mute_mic(lc, 0);
+ 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, atof(args));
+ linphone_core_set_playback_gain_db(lc, (float)atof(args));
return 1;
}
return 0;
@@ -2439,7 +2474,7 @@ static void lpc_display_global_state(LinphoneCore *lc){
static void lpc_display_call_states(LinphoneCore *lc){
LinphoneCall *call;
- const MSList *elem;
+ const bctbx_list_t *elem;
char *tmp;
linphonec_out("Call states\n"
"Id | Destination | State | Flags |\n"
@@ -2452,7 +2487,7 @@ static void lpc_display_call_states(LinphoneCore *lc){
const char *flag;
bool_t in_conference;
call=(LinphoneCall*)elem->data;
- in_conference=linphone_call_params_get_local_conference_mode(linphone_call_get_current_params(call));
+ 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;
@@ -2464,7 +2499,7 @@ static void lpc_display_call_states(LinphoneCore *lc){
}
static void lpc_display_proxy_states(LinphoneCore *lc){
- const MSList *elem;
+ const bctbx_list_t *elem;
linphonec_out("Proxy registration states\n"
" Identity | State\n"
"------------------------------------------------------------\n");
diff --git a/console/linphonec.c b/console/linphonec.c
index 1ad119ed5..248f419dd 100644
--- a/console/linphonec.c
+++ b/console/linphonec.c
@@ -37,8 +37,9 @@
#include
#include "linphonec.h"
+#include
-#ifdef WIN32
+#ifdef _WIN32
#include
#include
#ifndef _WIN32_WCE
@@ -102,7 +103,7 @@ 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();
+static int linphonec_finish_readline(void);
static char **linephonec_readline_completion(const char *text,
int start, int end);
#endif
@@ -290,9 +291,12 @@ linphonec_transfer_state_changed(LinphoneCore *lc, LinphoneCall *call, LinphoneC
static void
linphonec_notify_presence_received(LinphoneCore *lc,LinphoneFriend *fid)
{
- char *tmp=linphone_address_as_string(linphone_friend_get_address(fid));
- printf("Friend %s is %s\n", tmp, linphone_online_status_to_string(linphone_friend_get_status(fid)));
- ms_free(tmp);
+ 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)
}
@@ -359,7 +363,7 @@ static void linphonec_call_state_changed(LinphoneCore *lc, LinphoneCall *call, L
if ( auto_answer) {
answer_call=TRUE;
} else if (real_early_media_sending) {
- LinphoneCallParams* callparams = linphone_core_create_default_call_parameters(lc);
+ 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);
@@ -439,7 +443,7 @@ static void start_prompt_reader(void){
#if !defined(_WIN32_WCE)
static ortp_pipe_t create_server_socket(void){
char path[128];
-#ifndef WIN32
+#ifndef _WIN32
snprintf(path,sizeof(path)-1,"linphonec-%i",getuid());
#else
{
@@ -459,7 +463,7 @@ static void *pipe_thread(void*p){
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
+#ifndef _WIN32
usleep(20000);
#else
Sleep(20);
@@ -537,7 +541,7 @@ char *linphonec_readline(char *prompt){
}
ms_mutex_unlock(&prompt_mutex);
linphonec_idle_call();
-#ifdef WIN32
+#ifdef _WIN32
{
MSG msg;
Sleep(20);
@@ -593,7 +597,7 @@ void linphonec_set_autoanswer(bool_t enabled){
auto_answer=enabled;
}
-bool_t linphonec_get_autoanswer(){
+bool_t linphonec_get_autoanswer(void){
return auto_answer;
}
@@ -965,7 +969,7 @@ static void x11_apply_video_params(VideoParams *params, Window window){
#endif
-static void lpc_apply_video_params(){
+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);
@@ -1206,7 +1210,7 @@ linphonec_parse_cmdline(int argc, char **argv)
if (strcmp(argv[arg_num], "NUL") != 0) {
#endif
#if !defined(_WIN32_WCE)
- if (access(argv[arg_num], F_OK) != 0)
+ if (bctbx_file_exist(argv[arg_num]) != 0)
{
fprintf(stderr,
"Cannot open config file %s.\n",
@@ -1223,7 +1227,7 @@ linphonec_parse_cmdline(int argc, char **argv)
{
if ( ++arg_num >= argc ) print_usage(EXIT_FAILURE);
#if !defined(_WIN32_WCE)
- if (access(argv[arg_num],F_OK)!=0 )
+ if (bctbx_file_exist(argv[arg_num])!=0 )
{
fprintf (stderr,
"Cannot open config file %s.\n",
@@ -1331,7 +1335,7 @@ handle_configfile_migration()
* If the *NEW* configuration already exists
* do nothing.
*/
- if (access(new_cfg,F_OK)==0)
+ if (bctbx_file_exist(new_cfg)==0)
{
free(new_cfg);
return 0;
@@ -1343,7 +1347,7 @@ handle_configfile_migration()
* If the *OLD* CLI configurations exist copy it to
* the new file and make it a symlink.
*/
- if (access(old_cfg_cli, F_OK)==0)
+ if (bctbx_file_exist(old_cfg_cli)==0)
{
if ( ! copy_file(old_cfg_cli, new_cfg) )
{
@@ -1364,7 +1368,7 @@ handle_configfile_migration()
* If the *OLD* GUI configurations exist copy it to
* the new file and make it a symlink.
*/
- if (access(old_cfg_gui, F_OK)==0)
+ if (bctbx_file_exist(old_cfg_gui)==0)
{
if ( ! copy_file(old_cfg_gui, new_cfg) )
{
diff --git a/console/linphonec.h b/console/linphonec.h
index 23c0ee5a0..975fcee90 100644
--- a/console/linphonec.h
+++ b/console/linphonec.h
@@ -116,11 +116,11 @@ extern VideoParams lpc_preview_params;
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 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();
+bool_t linphonec_get_autoanswer(void);
void linphonec_command_finished(void);
void linphonec_set_caller(const char *caller);
LinphoneCall *linphonec_get_call(long id);
diff --git a/console/shell.c b/console/shell.c
index 49b1b5d45..f80a15378 100644
--- a/console/shell.c
+++ b/console/shell.c
@@ -25,7 +25,7 @@
#include
#include
-#ifdef WIN32
+#ifdef _WIN32
#include
#include
#include
@@ -39,6 +39,7 @@
#endif
#include "ortp/ortp.h"
+#include
#define DEFAULT_REPLY_SIZE 4096
@@ -82,7 +83,7 @@ static int send_command(const char *command, char *reply, int reply_len, int pri
int i;
int err;
char path[128];
-#ifndef WIN32
+#ifndef _WIN32
snprintf(path,sizeof(path)-1,"linphonec-%i",getuid());
#else
{
@@ -130,7 +131,7 @@ static void print_usage(void){
exit(-1);
}
-#ifdef WIN32
+#ifdef _WIN32
static char *argv_to_line(int argc, char *argv[]) {
int i;
int line_length;
@@ -157,7 +158,7 @@ static char *argv_to_line(int argc, char *argv[]) {
#define MAX_ARGS 10
-#ifndef WIN32
+#ifndef _WIN32
static void spawn_linphonec(int argc, char *argv[]){
char * args[MAX_ARGS];
int i,j;
diff --git a/console/sipomatic.c b/console/sipomatic.c
index e7a1c6a88..25afdbdb8 100644
--- a/console/sipomatic.c
+++ b/console/sipomatic.c
@@ -76,7 +76,7 @@ void call_accept(Call *call)
sdp_context_t *ctx;
PayloadType *payload;
char *hellofile;
- static int call_count=0;
+ static int call_count=0;
char record_file[250];
osip_message_t *msg=NULL;
sprintf(record_file,"/tmp/sipomatic%i.wav",call_count);
@@ -105,7 +105,7 @@ void call_accept(Call *call)
#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,
+ 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
@@ -124,7 +124,7 @@ PayloadType * sipomatic_payload_is_supported(sdp_payload_t *payload,RtpProfile *
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 */
@@ -160,7 +160,7 @@ int sipomatic_accept_audio_offer(sdp_context_t *ctx,sdp_payload_t *payload)
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) {
@@ -191,7 +191,7 @@ int sipomatic_accept_video_offer(sdp_context_t *ctx,sdp_payload_t *payload)
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) {
@@ -221,9 +221,9 @@ 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){
@@ -237,7 +237,7 @@ void sipomatic_init(Sipomatic *obj, char *url, bool_t ipv6)
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);
@@ -271,8 +271,8 @@ void sipomatic_uninit(Sipomatic *obj)
void sipomatic_iterate(Sipomatic *obj)
{
- MSList *elem;
- MSList *to_be_destroyed=NULL;
+ bctbx_list_t *elem;
+ bctbx_list_t *to_be_destroyed=NULL;
Call *call;
double elapsed;
eXosip_event_t *ev;
@@ -293,13 +293,13 @@ void sipomatic_iterate(Sipomatic *obj)
case CALL_STATE_RUNNING:
if (elapsed>obj->max_call_time || call->eof){
call_release(call);
- to_be_destroyed=ms_list_append(to_be_destroyed,call);
+ to_be_destroyed=bctbx_list_append(to_be_destroyed,call);
}
break;
}
- elem=ms_list_next(elem);
+ elem=bctbx_list_next(elem);
}
- for(;to_be_destroyed!=NULL; to_be_destroyed=ms_list_next(to_be_destroyed)){
+ for(;to_be_destroyed!=NULL; to_be_destroyed=bctbx_list_next(to_be_destroyed)){
call_destroy((Call*)to_be_destroyed->data);
}
}
@@ -307,9 +307,9 @@ void sipomatic_iterate(Sipomatic *obj)
Call* sipomatic_find_call(Sipomatic *obj,int did)
{
- MSList *it;
+ bctbx_list_t *it;
Call *call=NULL;
- for (it=obj->calls;it!=NULL;it=ms_list_next(it)){
+ for (it=obj->calls;it!=NULL;it=bctbx_list_next(it)){
call=(Call*)it->data;
if ( call->did==did) return call;
}
@@ -324,7 +324,7 @@ Call * call_new(Sipomatic *root, eXosip_event_t *ev)
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);
@@ -334,7 +334,7 @@ Call * call_new(Sipomatic *root, eXosip_event_t *ev)
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);
@@ -351,7 +351,7 @@ Call * call_new(Sipomatic *root, eXosip_event_t *ev)
obj->state=CALL_STATE_INIT;
obj->eof=0;
obj->root=root;
- root->calls=ms_list_append(root->calls,obj);
+ root->calls=bctbx_list_append(root->calls,obj);
return obj;
}
@@ -367,7 +367,7 @@ void call_release(Call *call)
void call_destroy(Call *obj)
{
- obj->root->calls=ms_list_remove(obj->root->calls,obj);
+ obj->root->calls=bctbx_list_remove(obj->root->calls,obj);
rtp_profile_destroy(obj->profile);
sdp_context_free(obj->sdpc);
ms_free(obj);
@@ -409,7 +409,7 @@ int main(int argc, char *argv[])
char *url=NULL;
bool_t ipv6=FALSE;
int i;
-
+
for(i=1;iaddServer(ip,port);
@@ -47,13 +47,17 @@ void TunnelManager::addServer(const char *ip, int port) {
void TunnelManager::cleanServers() {
mServerAddrs.clear();
-
+ if (mLongRunningTaskId > 0) {
+ sal_end_background_task(mLongRunningTaskId);
+ mLongRunningTaskId = 0;
+ }
UdpMirrorClientList::iterator it;
for (it = mUdpMirrorClients.begin(); it != mUdpMirrorClients.end();) {
UdpMirrorClient& s=*it++;
s.stop();
}
mUdpMirrorClients.clear();
+ mCurrentUdpMirrorClient = mUdpMirrorClients.end();
if (mTunnelClient) mTunnelClient->cleanServers();
}
@@ -62,8 +66,8 @@ void TunnelManager::reconnect(){
mTunnelClient->reconnect();
}
-static void sCloseRtpTransport(RtpTransport *t, void *userData){
- TunnelSocket *s=(TunnelSocket*)userData;
+static void sCloseRtpTransport(RtpTransport *t){
+ TunnelSocket *s=(TunnelSocket*)t->data;
TunnelManager *manager=(TunnelManager*)s->getUserPointer();
manager->closeRtpTransport(t, s);
}
@@ -89,21 +93,50 @@ RtpTransport *TunnelManager::createRtpTransport(int port){
t->t_close=sCloseRtpTransport;
t->t_destroy=sDestroyRtpTransport;
t->data=socket;
+ ms_message("Creating tunnel RTP transport for local virtual port %i", port);
return t;
}
void TunnelManager::startClient() {
ms_message("TunnelManager: Starting tunnel client");
- mTunnelClient = new TunnelClient(TRUE);
- mTunnelClient->setCallback((TunnelClientController::StateCallback)tunnelCallback,this);
+ if (!mTunnelClient){
+ mTunnelClient = new TunnelClient(TRUE);
+ sal_set_tunnel(mCore->sal, mTunnelClient);
+ mTunnelClient->setCallback(tunnelCallback,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();
list::iterator it;
for(it=mServerAddrs.begin();it!=mServerAddrs.end();++it){
const ServerAddr &addr=*it;
mTunnelClient->addServer(addr.mAddr.c_str(), addr.mPort);
}
mTunnelClient->setHttpProxy(mHttpProxyHost.c_str(), mHttpProxyPort, mHttpUserName.c_str(), mHttpPasswd.c_str());
- mTunnelClient->start();
- sal_set_tunnel(mCore->sal, mTunnelClient);
+ if (!mTunnelClient->isStarted())
+ mTunnelClient->start();
+ else
+ mTunnelClient->reconnect(); /*force a reconnection to take into account new parameters*/
+
+}
+
+void TunnelManager::stopClient(){
+ if (linphone_core_get_calls_nb(mCore) == 0){
+ /*if no calls are running, we can decide to stop the client completely, so that the connection to the tunnel server is terminated.*/
+ if (mTunnelClient) {
+ ms_message("TunnelManager: stoppping tunnel client");
+ mTunnelClient->stop();
+ }
+ /*otherwise, it doesn't really matter if the tunnel connection is kept alive even if it is not used anymore by the liblinphone.*/
+ }
}
bool TunnelManager::isConnected() const {
@@ -120,9 +153,11 @@ int TunnelManager::customSendto(struct _RtpTransport *t, mblk_t *msg , int flags
int TunnelManager::customRecvfrom(struct _RtpTransport *t, mblk_t *msg, int flags, struct sockaddr *from, socklen_t *fromlen){
memset(&msg->recv_addr,0,sizeof(msg->recv_addr));
- int err=((TunnelSocket*)t->data)->recvfrom(msg->b_wptr,msg->b_datap->db_lim-msg->b_datap->db_base,from,*fromlen);
+ int err=((TunnelSocket*)t->data)->recvfrom(msg->b_wptr,dblk_lim(msg->b_datap)-dblk_base(msg->b_datap),from,*fromlen);
//to make ice happy
inet_aton(((TunnelManager*)((TunnelSocket*)t->data)->getUserPointer())->mLocalAddr,&msg->recv_addr.addr.ipi_addr);
+ msg->recv_addr.family = AF_INET;
+ msg->recv_addr.port = htons((unsigned short)((TunnelSocket*)t->data)->getPort());
if (err>0) return err;
return 0;
}
@@ -130,11 +165,11 @@ int TunnelManager::customRecvfrom(struct _RtpTransport *t, mblk_t *msg, int flag
TunnelManager::TunnelManager(LinphoneCore* lc) :
mCore(lc),
mMode(LinphoneTunnelModeDisable),
- mState(disabled),
- mTunnelizeSipPackets(true),
mTunnelClient(NULL),
mHttpProxyPort(0),
- mVTable(NULL)
+ mVTable(NULL),
+ mLongRunningTaskId(0),
+ mSimulateUdpLoss(false)
{
linphone_core_add_iterate_hook(mCore,(LinphoneCoreIterateHook)sOnIterate,this);
mTransportFactories.audio_rtcp_func=sCreateRtpTransport;
@@ -149,20 +184,34 @@ TunnelManager::TunnelManager(LinphoneCore* lc) :
mVTable->network_reachable = networkReachableCb;
linphone_core_add_listener(mCore, mVTable);
linphone_core_get_local_ip_for(AF_INET, NULL, mLocalAddr);
+ mAutodetectionRunning = false;
+ mState = Off;
+ mTargetState = Off;
+ mStarted = false;
+ mTunnelizeSipPackets = true;
}
TunnelManager::~TunnelManager(){
+ if (mLongRunningTaskId > 0) {
+ sal_end_background_task(mLongRunningTaskId);
+ mLongRunningTaskId = 0;
+ }
for(UdpMirrorClientList::iterator udpMirror = mUdpMirrorClients.begin(); udpMirror != mUdpMirrorClients.end(); udpMirror++) {
udpMirror->stop();
}
- if(mTunnelClient) delete mTunnelClient;
+ stopClient();
+ if (mTunnelClient) {
+ mTunnelClient->stop();
+ delete mTunnelClient;
+ }
+ sal_set_tunnel(mCore->sal,NULL);
linphone_core_remove_listener(mCore, mVTable);
linphone_core_v_table_destroy(mVTable);
}
void TunnelManager::doRegistration(){
LinphoneProxyConfig* lProxy;
- linphone_core_get_default_proxy(mCore, &lProxy);
+ lProxy = linphone_core_get_default_proxy_config(mCore);
if (lProxy) {
ms_message("TunnelManager: New registration");
lProxy->commit = TRUE;
@@ -171,82 +220,105 @@ void TunnelManager::doRegistration(){
void TunnelManager::doUnregistration() {
LinphoneProxyConfig *lProxy;
- linphone_core_get_default_proxy(mCore, &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");
- if(mState == connecting) {
- linphone_core_set_rtp_transport_factories(mCore,&mTransportFactories);
- mState = ready;
- if(mTunnelizeSipPackets) {
- doUnregistration();
- _linphone_core_apply_transports(mCore);
- doRegistration();
- }
-
- }
+ applyState();
} else {
ms_error("TunnelManager: tunnel has been disconnected");
}
}
-void TunnelManager::setMode(LinphoneTunnelMode mode) {
- if(mMode == mode) return;
- if((mode==LinphoneTunnelModeDisable && mState==disabled)
- || (mode==LinphoneTunnelModeEnable && mState==ready)) {
- return;
- }
- ms_message("TunnelManager: switching mode from %s to %s",
- linphone_tunnel_mode_to_string(mMode),
- linphone_tunnel_mode_to_string(mode));
- switch(mode) {
+void TunnelManager::applyMode() {
+ switch(mMode) {
case LinphoneTunnelModeEnable:
- if(mState == disabled) {
- startClient();
- mState = connecting;
- mMode = mode;
- } else {
- ms_error("TunnelManager: could not change mode. Bad state");
- }
+ stopAutoDetection();
+ setState(On);
break;
case LinphoneTunnelModeDisable:
- if(mState == ready) {
- linphone_core_set_rtp_transport_factories(mCore,NULL);
- mState = disabled;
- mMode = mode;
- if(mTunnelizeSipPackets) {
- doUnregistration();
- _linphone_core_apply_transports(mCore);
- }
- sal_set_tunnel(mCore->sal,NULL);
- delete mTunnelClient;
- mTunnelClient=NULL;
- } else {
- ms_error("TunnelManager: could not change mode. Bad state");
- }
+ stopAutoDetection();
+ setState(Off);
break;
case LinphoneTunnelModeAuto:
- if(mState == disabled || mState == ready) {
- if(startAutoDetection()) {
- mState = autodetecting;
- mMode = mode;
- }
- } else {
- ms_error("TunnelManager: could not change mode. Bad state");
- }
+ if (linphone_core_is_network_reachable(mCore)) startAutoDetection();
break;
default:
- ms_error("TunnelManager::setMode(): invalid mode (%d)", mode);
+ ms_error("TunnelManager::setMode(): invalid mode (%d)", (int)mMode);
}
}
-void TunnelManager::tunnelCallback(bool connected, TunnelManager *zis){
+
+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);
@@ -318,33 +390,30 @@ LinphoneTunnelMode TunnelManager::getMode() const {
}
void TunnelManager::processUdpMirrorEvent(const Event &ev){
- if(mState != autodetecting) return;
- if (ev.mData.mHaveUdp) {
- ms_message("TunnelManager: UDP mirror test succeed");
- if(mTunnelClient) {
- if(mTunnelizeSipPackets) doUnregistration();
- delete mTunnelClient;
- mTunnelClient = NULL;
- if(mTunnelizeSipPackets) doRegistration();
+ 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);
}
- mState = disabled;
- } else {
- ms_message("TunnelManager: UDP mirror test failed");
mCurrentUdpMirrorClient++;
if (mCurrentUdpMirrorClient !=mUdpMirrorClients.end()) {
- ms_message("TunnelManager: trying another UDP mirror");
+ 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");
- if(mTunnelClient==NULL) {
- startClient();
- mState = connecting;
- } else {
- mState = ready;
- }
+ 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){
@@ -363,11 +432,32 @@ void TunnelManager::sUdpMirrorClientCallback(bool isUdpAvailable, void* data) {
void TunnelManager::networkReachableCb(LinphoneCore *lc, bool_t reachable) {
TunnelManager *tunnel = bcTunnel(linphone_core_get_tunnel(lc));
- if(reachable && tunnel->getMode() == LinphoneTunnelModeAuto && tunnel->mState != connecting && tunnel->mState != autodetecting) {
- tunnel->startAutoDetection();
- tunnel->mState = autodetecting;
+
+ if (reachable) {
+ 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) {
+ // if network is no more reachable, cancel autodetection if any
+ tunnel->stopAutoDetection();
+ //turn off the tunnel connection
+ tunnel->stopClient();
+ tunnel->untunnelizeLiblinphone();
+ }
+}
+
+void TunnelManager::stopAutoDetection(){
+ if (mAutodetectionRunning){
+ for(UdpMirrorClientList::iterator udpMirror = mUdpMirrorClients.begin(); udpMirror != mUdpMirrorClients.end(); udpMirror++) {
+ udpMirror->stop();
+ }
+ mAutodetectionRunning = false;
+ stopLongRunningTask();
}
- linphone_core_get_local_ip_for(AF_INET, NULL,tunnel->mLocalAddr);
}
bool TunnelManager::startAutoDetection() {
@@ -377,21 +467,16 @@ bool TunnelManager::startAutoDetection() {
}
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{
- switch(getMode()){
- case LinphoneTunnelModeAuto:
- return !(mState==disabled);
- case LinphoneTunnelModeDisable:
- return false;
- case LinphoneTunnelModeEnable:
- return true;
- }
- return false;
+ return mState == On;
}
void TunnelManager::setHttpProxyAuthInfo(const char* username,const char* passwd) {
@@ -408,6 +493,14 @@ 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:"";
@@ -419,3 +512,7 @@ void TunnelManager::setHttpProxy(const char *host,int port, const char *username
LinphoneCore *TunnelManager::getLinphoneCore() const{
return mCore;
}
+
+void TunnelManager::simulateUdpLoss(bool enabled) {
+ mSimulateUdpLoss = enabled;
+}
diff --git a/coreapi/TunnelManager.hh b/coreapi/TunnelManager.hh
index f8002ee5d..48fb2ec23 100644
--- a/coreapi/TunnelManager.hh
+++ b/coreapi/TunnelManager.hh
@@ -110,6 +110,18 @@ namespace belledonnecomm {
* @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.
@@ -143,14 +155,10 @@ namespace belledonnecomm {
bool isConnected() const;
bool isActivated() const;
- private:
- enum State {
- disabled,
- connecting,
- ready,
- autodetecting
- };
+ void simulateUdpLoss(bool enabled);
+
+ private:
enum EventType{
UdpMirrorClientEvent,
TunnelEvent,
@@ -165,15 +173,16 @@ namespace belledonnecomm {
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 int eXosipSendto(int fd,const void *buf, size_t len, int flags, const struct sockaddr *to, socklen_t tolen,void* userdata);
- static int eXosipRecvfrom(int fd, void *buf, size_t len, int flags, struct sockaddr *from, socklen_t *fromlen,void* userdata);
- static int eXosipSelect(int nfds, fd_set *s1, fd_set *s2, fd_set *s3, struct timeval *tv,void* userdata);
- static void tunnelCallback(bool connected, TunnelManager *zis);
+ static void tunnelCallback(bool connected, void *zis);
static void sOnIterate(TunnelManager *zis);
static void sUdpMirrorClientCallback(bool result, void* data);
static void networkReachableCb(LinphoneCore *lc, bool_t reachable);
private:
+ enum State{
+ Off, /*no tunneling */
+ On /*tunneling activated*/
+ };
void onIterate();
void doRegistration();
void doUnregistration();
@@ -182,12 +191,18 @@ namespace belledonnecomm {
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();
private:
+
LinphoneCore* mCore;
LinphoneTunnelMode mMode;
- State mState;
- bool mTunnelizeSipPackets;
TunnelClient* mTunnelClient;
std::string mHttpUserName;
std::string mHttpPasswd;
@@ -201,6 +216,14 @@ namespace belledonnecomm {
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;
};
/**
diff --git a/coreapi/account_creator.c b/coreapi/account_creator.c
index b7c809df6..e26f6157c 100644
--- a/coreapi/account_creator.c
+++ b/coreapi/account_creator.c
@@ -17,9 +17,13 @@ along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-#include "linphonecore.h"
+#include "account_creator.h"
#include "private.h"
+#if !_WIN32
+#include "regex.h"
+#endif
+#include
BELLE_SIP_DECLARE_NO_IMPLEMENTED_INTERFACES(LinphoneAccountCreatorCbs);
@@ -30,6 +34,12 @@ BELLE_SIP_INSTANCIATE_VPTR(LinphoneAccountCreatorCbs, belle_sip_object_t,
FALSE
);
+static const char* ha1_for_passwd(const char* username, const char* realm, const char* passwd) {
+ static char ha1[33];
+ sal_auth_compute_ha1(username, realm, passwd, ha1);
+ return ha1;
+}
+
static LinphoneAccountCreatorCbs * linphone_account_creator_cbs_new(void) {
return belle_sip_object_new(LinphoneAccountCreatorCbs);
}
@@ -51,41 +61,92 @@ void linphone_account_creator_cbs_set_user_data(LinphoneAccountCreatorCbs *cbs,
cbs->user_data = ud;
}
-LinphoneAccountCreatorCbsExistenceTestedCb linphone_account_creator_cbs_get_existence_tested(const LinphoneAccountCreatorCbs *cbs) {
- return cbs->existence_tested;
+LinphoneAccountCreatorCbsStatusCb linphone_account_creator_cbs_get_is_account_used(const LinphoneAccountCreatorCbs *cbs) {
+ return cbs->is_account_used;
}
-void linphone_account_creator_cbs_set_existence_tested(LinphoneAccountCreatorCbs *cbs, LinphoneAccountCreatorCbsExistenceTestedCb cb) {
- cbs->existence_tested = cb;
+void linphone_account_creator_cbs_set_is_account_used(LinphoneAccountCreatorCbs *cbs, LinphoneAccountCreatorCbsStatusCb cb) {
+ cbs->is_account_used = cb;
}
-LinphoneAccountCreatorCbsValidationTestedCb linphone_account_creator_cbs_get_validation_tested(const LinphoneAccountCreatorCbs *cbs) {
- return cbs->validation_tested;
+LinphoneAccountCreatorCbsStatusCb linphone_account_creator_cbs_get_create_account(const LinphoneAccountCreatorCbs *cbs) {
+ return cbs->create_account;
}
-void linphone_account_creator_cbs_set_validation_tested(LinphoneAccountCreatorCbs *cbs, LinphoneAccountCreatorCbsValidationTestedCb cb) {
- cbs->validation_tested = cb;
+void linphone_account_creator_cbs_set_create_account(LinphoneAccountCreatorCbs *cbs, LinphoneAccountCreatorCbsStatusCb cb) {
+ cbs->create_account = cb;
}
-LinphoneAccountCreatorCbsValidatedCb linphone_account_creator_cbs_get_validated(const LinphoneAccountCreatorCbs *cbs) {
- return cbs->validated;
+LinphoneAccountCreatorCbsStatusCb linphone_account_creator_cbs_get_activate_account(const LinphoneAccountCreatorCbs *cbs) {
+ return cbs->activate_account;
}
-void linphone_account_creator_cbs_set_validated(LinphoneAccountCreatorCbs *cbs, LinphoneAccountCreatorCbsValidatedCb cb) {
- cbs->validated = cb;
+void linphone_account_creator_cbs_set_activate_account(LinphoneAccountCreatorCbs *cbs, LinphoneAccountCreatorCbsStatusCb cb) {
+ cbs->activate_account = cb;
+}
+
+LinphoneAccountCreatorCbsStatusCb linphone_account_creator_cbs_get_link_phone_number_with_account(const LinphoneAccountCreatorCbs *cbs) {
+ return cbs->link_phone_number_with_account;
+}
+
+void linphone_account_creator_cbs_set_link_phone_number_with_account(LinphoneAccountCreatorCbs *cbs, LinphoneAccountCreatorCbsStatusCb cb) {
+ cbs->link_phone_number_with_account = cb;
+}
+
+LinphoneAccountCreatorCbsStatusCb linphone_account_creator_cbs_get_activate_phone_number_link(const LinphoneAccountCreatorCbs *cbs) {
+ return cbs->activate_phone_number_link;
+}
+
+void linphone_account_creator_cbs_set_activate_phone_number_link(LinphoneAccountCreatorCbs *cbs, LinphoneAccountCreatorCbsStatusCb cb) {
+ cbs->activate_phone_number_link = cb;
+}
+
+void linphone_account_creator_cbs_set_is_account_linked(LinphoneAccountCreatorCbs *cbs, LinphoneAccountCreatorCbsStatusCb cb) {
+ cbs->is_account_linked = cb;
+}
+
+LinphoneAccountCreatorCbsStatusCb linphone_account_creator_cbs_get_is_account_linked(const LinphoneAccountCreatorCbs *cbs) {
+ return cbs->is_account_linked;
+}
+
+LinphoneAccountCreatorCbsStatusCb linphone_account_creator_cbs_get_is_account_activated(const LinphoneAccountCreatorCbs *cbs) {
+ return cbs->is_account_activated;
+}
+
+void linphone_account_creator_cbs_set_is_account_activated(LinphoneAccountCreatorCbs *cbs, LinphoneAccountCreatorCbsStatusCb cb) {
+ cbs->is_account_activated = cb;
+}
+
+LinphoneAccountCreatorCbsStatusCb linphone_account_creator_cbs_get_recover_phone_account(const LinphoneAccountCreatorCbs *cbs) {
+ return cbs->recover_phone_account;
+}
+
+void linphone_account_creator_cbs_set_recover_phone_account(LinphoneAccountCreatorCbs *cbs, LinphoneAccountCreatorCbsStatusCb cb) {
+ cbs->recover_phone_account = cb;
+}
+
+LinphoneAccountCreatorCbsStatusCb linphone_account_creator_cbs_get_is_phone_number_used(const LinphoneAccountCreatorCbs *cbs) {
+ return cbs->is_phone_number_used;
+}
+
+void linphone_account_creator_cbs_set_is_phone_number_used(LinphoneAccountCreatorCbs *cbs, LinphoneAccountCreatorCbsStatusCb cb) {
+ cbs->is_phone_number_used = cb;
}
static void _linphone_account_creator_destroy(LinphoneAccountCreator *creator) {
- linphone_xml_rpc_session_unref(creator->xmlrpc_session);
+ linphone_xml_rpc_session_release(creator->xmlrpc_session); /*this will drop all pending requests if any*/
linphone_account_creator_cbs_unref(creator->callbacks);
if (creator->username) ms_free(creator->username);
if (creator->password) ms_free(creator->password);
+ if (creator->ha1) ms_free(creator->ha1);
if (creator->domain) ms_free(creator->domain);
if (creator->route) ms_free(creator->route);
if (creator->email) ms_free(creator->email);
if (creator->display_name) ms_free(creator->display_name);
- ms_free(creator);
+ if (creator->phone_country_code) ms_free(creator->phone_country_code);
+ if (creator->activation_code) ms_free(creator->activation_code);
+ if (creator->language) ms_free(creator->language);
}
BELLE_SIP_DECLARE_NO_IMPLEMENTED_INTERFACES(LinphoneAccountCreator);
@@ -100,10 +161,14 @@ BELLE_SIP_INSTANCIATE_VPTR(LinphoneAccountCreator, belle_sip_object_t,
LinphoneAccountCreator * linphone_account_creator_new(LinphoneCore *core, const char *xmlrpc_url) {
LinphoneAccountCreator *creator;
+ const char* domain = lp_config_get_string(core->config, "assistant", "domain", NULL);
creator = belle_sip_object_new(LinphoneAccountCreator);
creator->callbacks = linphone_account_creator_cbs_new();
creator->core = core;
creator->xmlrpc_session = linphone_xml_rpc_session_new(core, xmlrpc_url);
+ if (domain) {
+ linphone_account_creator_set_domain(creator, domain);
+ }
return creator;
}
@@ -124,174 +189,299 @@ void linphone_account_creator_set_user_data(LinphoneAccountCreator *creator, voi
creator->user_data = ud;
}
-void linphone_account_creator_set_username(LinphoneAccountCreator *creator, const char *username) {
- set_string(&creator->username, username);
+static LinphoneAccountCreatorStatus validate_uri(const char* username, const char* domain, const char* route, const char* display_name) {
+ LinphoneAddress* addr;
+ LinphoneAccountCreatorStatus status = LinphoneAccountCreatorOK;
+ LinphoneProxyConfig* proxy = linphone_proxy_config_new();
+ linphone_proxy_config_set_identity(proxy, "sip:userame@domain.com");
+
+ if (route && linphone_proxy_config_set_route(proxy, route) != 0) {
+ status = LinphoneAccountCreatorRouteInvalid;
+ goto end;
+ }
+
+ 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 = LinphoneAccountCreatorUsernameInvalid;
+ goto end;
+ }
+
+ if (domain && linphone_address_set_domain(addr, domain) != 0) {
+ status = LinphoneAccountCreatorDomainInvalid;
+ }
+
+ if (display_name && (!strlen(display_name) || linphone_address_set_display_name(addr, display_name) != 0)) {
+ status = LinphoneAccountCreatorDisplayNameInvalid;
+ }
+ 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) && creator->domain) {
+ //we must escape username
+ LinphoneProxyConfig* proxy = linphone_proxy_config_new();
+ LinphoneAddress* addr;
+ // creator->domain may contain some port or some transport (eg. toto.org:443;transport=tcp),
+ // we will accept that
+ char * tmpidentity = ms_strdup_printf("sip:username@%s", creator->domain);
+ linphone_proxy_config_set_identity(proxy, tmpidentity);
+ ms_free(tmpidentity);
+ addr = linphone_proxy_config_normalize_sip_uri(proxy, creator->username ? creator->username : creator->phone_number);
+
+ identity = linphone_address_as_string(addr);
+ linphone_address_destroy(addr);
+ linphone_proxy_config_destroy(proxy);
+ }
+ return identity;
+}
+
+static bool_t is_matching_regex(const char *entry, const char* regex) {
+#if _WIN32
+ return TRUE;
+#else
+ regex_t regex_pattern;
+ char err_msg[256];
+ int res;
+ res = regcomp(®ex_pattern, regex, REG_EXTENDED | REG_NOSUB);
+ if(res != 0) {
+ regerror(res, ®ex_pattern, err_msg, sizeof(err_msg));
+ ms_error("Could not compile regex '%s: %s", regex, err_msg);
+ return FALSE;
+ }
+ res = regexec(®ex_pattern, entry, 0, NULL, 0);
+ regfree(®ex_pattern);
+ return (res != REG_NOMATCH);
+#endif
+}
+
+LinphoneAccountCreatorStatus linphone_account_creator_set_username(LinphoneAccountCreator *creator, const char *username) {
+ int min_length = lp_config_get_int(creator->core->config, "assistant", "username_min_length", -1);
+ int fixed_length = lp_config_get_int(creator->core->config, "assistant", "username_length", -1);
+ int max_length = lp_config_get_int(creator->core->config, "assistant", "username_max_length", -1);
+ bool_t use_phone_number = lp_config_get_int(creator->core->config, "assistant", "use_phone_number", 0);
+ const char* regex = lp_config_get_string(creator->core->config, "assistant", "username_regex", 0);
+ LinphoneAccountCreatorStatus status = LinphoneAccountCreatorOK;
+ if (!username) {
+ creator->username = NULL;
+ return LinphoneAccountCreatorOK;
+ } else if (min_length > 0 && strlen(username) < (size_t)min_length) {
+ return LinphoneAccountCreatorUsernameTooShort;
+ } else if (max_length > 0 && strlen(username) > (size_t)max_length) {
+ return LinphoneAccountCreatorUsernameTooLong;
+ } else if (fixed_length > 0 && strlen(username) != (size_t)fixed_length) {
+ return LinphoneAccountCreatorUsernameInvalidSize;
+ } else if (use_phone_number && !linphone_proxy_config_is_phone_number(NULL, username)) {
+ return LinphoneAccountCreatorUsernameInvalid;
+ } else if (regex && !is_matching_regex(username, regex)) {
+ return LinphoneAccountCreatorUsernameInvalid;
+ } else if ((status = validate_uri(username, NULL, NULL, NULL)) != LinphoneAccountCreatorOK) {
+ return status;
+ }
+
+ set_string(&creator->username, username, TRUE);
+
+ return LinphoneAccountCreatorOK;
}
const char * linphone_account_creator_get_username(const LinphoneAccountCreator *creator) {
return creator->username;
}
-void linphone_account_creator_set_password(LinphoneAccountCreator *creator, const char *password){
- set_string(&creator->password, password);
+
+LinphoneAccountCreatorStatus linphone_account_creator_set_phone_number(LinphoneAccountCreator *creator, const char *phone_number, const char *country_code) {
+ char *normalized_phone_number;
+ LinphoneAccountCreatorStatus return_status;
+ if (!phone_number || !country_code) {
+ if (!phone_number && !country_code) {
+ creator->phone_number = NULL;
+ creator->phone_country_code = NULL;
+ return LinphoneAccountCreatorOK;
+ } else {
+ return LinphoneAccountCreatorPhoneNumberInvalid;
+ }
+ } else {
+ LinphoneProxyConfig *numCfg = linphone_proxy_config_new();
+ 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);
+ linphone_proxy_config_destroy(numCfg);
+ if (!normalized_phone_number) {
+ return LinphoneAccountCreatorPhoneNumberInvalid;
+ }
+
+ // if phone is valid, we lastly want to check that length is OK
+ {
+ const LinphoneDialPlan* plan = linphone_dial_plan_by_ccc(creator->phone_country_code);
+ int size = (int)strlen(phone_number);
+ if (linphone_dial_plan_is_generic(plan)) {
+ return_status = LinphoneAccountCreatorCountryCodeInvalid;
+ goto end;
+ }
+ if (size < plan->nnl - 1) {
+ return_status = LinphoneAccountCreatorPhoneNumberTooShort;
+ goto end;
+ } else if (size > plan->nnl + 1) {
+ return_status = LinphoneAccountCreatorPhoneNumberTooLong;
+ goto end;
+ }
+ }
+ }
+ set_string(&creator->phone_number, normalized_phone_number, TRUE);
+ return_status = LinphoneAccountCreatorOK;
+
+end:
+ ms_free(normalized_phone_number);
+ return return_status;
+}
+
+const char * linphone_account_creator_get_phone_number(const LinphoneAccountCreator *creator) {
+ return creator->phone_number;
+}
+
+LinphoneAccountCreatorStatus linphone_account_creator_set_password(LinphoneAccountCreator *creator, const char *password){
+ int min_length = lp_config_get_int(creator->core->config, "assistant", "password_min_length", -1);
+ int max_length = lp_config_get_int(creator->core->config, "assistant", "password_max_length", -1);
+ if (!password) {
+ creator->password = NULL;
+ return LinphoneAccountCreatorPasswordTooShort;
+ }
+ if (min_length > 0 && strlen(password) < (size_t)min_length) {
+ return LinphoneAccountCreatorPasswordTooShort;
+ } else if (max_length > 0 && strlen(password) > (size_t)max_length) {
+ return LinphoneAccountCreatorPasswordTooLong;
+ }
+ set_string(&creator->password, password, FALSE);
+ return LinphoneAccountCreatorOK;
}
const char * linphone_account_creator_get_password(const LinphoneAccountCreator *creator) {
return creator->password;
}
-void linphone_account_creator_set_domain(LinphoneAccountCreator *creator, const char *domain){
- set_string(&creator->domain, domain);
+LinphoneAccountCreatorStatus linphone_account_creator_set_ha1(LinphoneAccountCreator *creator, const char *ha1){
+ set_string(&creator->ha1, ha1, FALSE);
+ return LinphoneAccountCreatorOK;
+}
+
+const char * linphone_account_creator_get_ha1(const LinphoneAccountCreator *creator) {
+ return creator->ha1;
+}
+
+LinphoneAccountCreatorStatus linphone_account_creator_set_activation_code(LinphoneAccountCreator *creator, const char *activation_code){
+ set_string(&creator->activation_code, activation_code, FALSE);
+ return LinphoneAccountCreatorOK;
+}
+
+LinphoneAccountCreatorStatus linphone_account_creator_set_language(LinphoneAccountCreator *creator, const char *lang) {
+ set_string(&creator->language, lang, FALSE);
+ return LinphoneAccountCreatorOK;
+}
+
+LinphoneAccountCreatorStatus linphone_account_creator_set_transport(LinphoneAccountCreator *creator, LinphoneTransportType transport){
+ if (!linphone_core_sip_transport_supported(creator->core, transport)) {
+ return LinphoneAccountCreatorTransportNotSupported;
+ }
+ creator->transport = transport;
+ return LinphoneAccountCreatorOK;
+}
+
+LinphoneTransportType linphone_account_creator_get_transport(const LinphoneAccountCreator *creator) {
+ return creator->transport;
+}
+
+LinphoneAccountCreatorStatus linphone_account_creator_set_domain(LinphoneAccountCreator *creator, const char *domain){
+ if (validate_uri(NULL, domain, NULL, NULL) != 0) {
+ return LinphoneAccountCreatorDomainInvalid;
+ }
+ set_string(&creator->domain, domain, TRUE);
+ return LinphoneAccountCreatorOK;
}
const char * linphone_account_creator_get_domain(const LinphoneAccountCreator *creator) {
return creator->domain;
}
-void linphone_account_creator_set_route(LinphoneAccountCreator *creator, const char *route) {
- set_string(&creator->route, route);
+LinphoneAccountCreatorStatus linphone_account_creator_set_route(LinphoneAccountCreator *creator, const char *route) {
+ if (validate_uri(NULL, NULL, route, NULL) != 0) {
+ return LinphoneAccountCreatorRouteInvalid;
+ }
+ set_string(&creator->route, route, TRUE);
+ return LinphoneAccountCreatorOK;
}
const char * linphone_account_creator_get_route(const LinphoneAccountCreator *creator) {
return creator->route;
}
-void linphone_account_creator_set_display_name(LinphoneAccountCreator *creator, const char *display_name) {
- set_string(&creator->display_name, display_name);
+LinphoneAccountCreatorStatus linphone_account_creator_set_display_name(LinphoneAccountCreator *creator, const char *display_name) {
+ if (validate_uri(NULL, NULL, NULL, display_name) != 0) {
+ return LinphoneAccountCreatorDisplayNameInvalid;
+ }
+ set_string(&creator->display_name, display_name, FALSE);
+ return LinphoneAccountCreatorOK;
}
const char * linphone_account_creator_get_display_name(const LinphoneAccountCreator *creator) {
return creator->display_name;
}
-void linphone_account_creator_set_email(LinphoneAccountCreator *creator, const char *email) {
- set_string(&creator->email, email);
+LinphoneAccountCreatorStatus linphone_account_creator_set_email(LinphoneAccountCreator *creator, const char *email) {
+ if (!is_matching_regex(email, "^.+@.+\\.[A-Za-z]{2}[A-Za-z]*$")) {
+ return LinphoneAccountCreatorEmailInvalid;
+ }
+ set_string(&creator->email, email, TRUE);
+ return LinphoneAccountCreatorOK;
}
const char * linphone_account_creator_get_email(const LinphoneAccountCreator *creator) {
return creator->email;
}
-void linphone_account_creator_enable_newsletter_subscription(LinphoneAccountCreator *creator, bool_t subscribe) {
- creator->subscribe_to_newsletter = subscribe;
-}
-
-bool_t linphone_account_creator_newsletter_subscription_enabled(const LinphoneAccountCreator *creator) {
- return creator->subscribe_to_newsletter;
-}
-
LinphoneAccountCreatorCbs * linphone_account_creator_get_callbacks(const LinphoneAccountCreator *creator) {
return creator->callbacks;
}
-static void _test_existence_cb(LinphoneXmlRpcRequest *request) {
- LinphoneAccountCreator *creator = (LinphoneAccountCreator *)linphone_xml_rpc_request_get_user_data(request);
- if (creator->callbacks->existence_tested != NULL) {
- LinphoneAccountCreatorStatus status = LinphoneAccountCreatorFailed;
- if ((linphone_xml_rpc_request_get_status(request) == LinphoneXmlRpcStatusOk)
- && (linphone_xml_rpc_request_get_int_response(request) == 0)) {
- status = LinphoneAccountCreatorOk;
- }
- creator->callbacks->existence_tested(creator, status);
- }
-}
-
-LinphoneAccountCreatorStatus linphone_account_creator_test_existence(LinphoneAccountCreator *creator) {
- LinphoneXmlRpcRequest *request;
- char *identity;
-
- if (!creator->username || !creator->domain) return LinphoneAccountCreatorFailed;
-
- identity = ms_strdup_printf("%s@%s", creator->username, creator->domain);
- request = linphone_xml_rpc_request_new_with_args("check_account", LinphoneXmlRpcArgInt,
- LinphoneXmlRpcArgString, identity,
- LinphoneXmlRpcArgNone);
- linphone_xml_rpc_request_set_user_data(request, creator);
- linphone_xml_rpc_request_cbs_set_response(linphone_xml_rpc_request_get_callbacks(request), _test_existence_cb);
- linphone_xml_rpc_session_send_request(creator->xmlrpc_session, request);
- linphone_xml_rpc_request_unref(request);
- ms_free(identity);
- return LinphoneAccountCreatorOk;
-}
-
-static void _test_validation_cb(LinphoneXmlRpcRequest *request) {
- LinphoneAccountCreator *creator = (LinphoneAccountCreator *)linphone_xml_rpc_request_get_user_data(request);
- if (creator->callbacks->validation_tested != NULL) {
- LinphoneAccountCreatorStatus status = LinphoneAccountCreatorFailed;
- if ((linphone_xml_rpc_request_get_status(request) == LinphoneXmlRpcStatusOk)
- && (linphone_xml_rpc_request_get_int_response(request) == 1)) {
- status = LinphoneAccountCreatorOk;
- }
- creator->callbacks->validation_tested(creator, status);
- }
-}
-
-LinphoneAccountCreatorStatus linphone_account_creator_test_validation(LinphoneAccountCreator *creator) {
- LinphoneXmlRpcRequest *request;
- char *identity;
-
- if (!creator->username || !creator->domain) return LinphoneAccountCreatorFailed;
-
- identity = ms_strdup_printf("%s@%s", creator->username, creator->domain);
- request = linphone_xml_rpc_request_new_with_args("check_account_validated", LinphoneXmlRpcArgInt,
- LinphoneXmlRpcArgString, identity,
- LinphoneXmlRpcArgNone);
- linphone_xml_rpc_request_set_user_data(request, creator);
- linphone_xml_rpc_request_cbs_set_response(linphone_xml_rpc_request_get_callbacks(request), _test_validation_cb);
- linphone_xml_rpc_session_send_request(creator->xmlrpc_session, request);
- linphone_xml_rpc_request_unref(request);
- ms_free(identity);
- return LinphoneAccountCreatorOk;
-}
-
-static void _validate_cb(LinphoneXmlRpcRequest *request) {
- LinphoneAccountCreator *creator = (LinphoneAccountCreator *)linphone_xml_rpc_request_get_user_data(request);
- if (creator->callbacks->validated != NULL) {
- LinphoneAccountCreatorStatus status = LinphoneAccountCreatorFailed;
- if ((linphone_xml_rpc_request_get_status(request) == LinphoneXmlRpcStatusOk)
- && (linphone_xml_rpc_request_get_int_response(request) == 0)) {
- status = LinphoneAccountCreatorOk;
- }
- creator->callbacks->validated(creator, status);
- }
-}
-
-LinphoneAccountCreatorStatus linphone_account_creator_validate(LinphoneAccountCreator *creator) {
- LinphoneXmlRpcRequest *request;
- char *identity;
-
- if (!creator->username || !creator->domain) return LinphoneAccountCreatorFailed;
-
- identity = ms_strdup_printf("%s@%s", creator->username, creator->domain);
- request = linphone_xml_rpc_request_new_with_args("create_account", LinphoneXmlRpcArgInt,
- LinphoneXmlRpcArgString, identity,
- LinphoneXmlRpcArgString, creator->password,
- LinphoneXmlRpcArgString, creator->email,
- LinphoneXmlRpcArgInt, (creator->subscribe_to_newsletter == TRUE) ? 1 : 0,
- LinphoneXmlRpcArgNone);
- linphone_xml_rpc_request_set_user_data(request, creator);
- linphone_xml_rpc_request_cbs_set_response(linphone_xml_rpc_request_get_callbacks(request), _validate_cb);
- linphone_xml_rpc_session_send_request(creator->xmlrpc_session, request);
- linphone_xml_rpc_request_unref(request);
- ms_free(identity);
- return LinphoneAccountCreatorOk;
-}
-
LinphoneProxyConfig * linphone_account_creator_configure(const LinphoneAccountCreator *creator) {
LinphoneAuthInfo *info;
LinphoneProxyConfig *cfg = linphone_core_create_proxy_config(creator->core);
- char *identity_str = ms_strdup_printf("sip:%s@%s", creator->username, creator->domain);
- LinphoneAddress *identity = linphone_address_new(identity_str);
+ char *identity_str = _get_identity(creator);
+ LinphoneAddress *identity = linphone_address_new(identity_str);
+ char *route = NULL;
+ char *domain = NULL;
+ ms_free(identity_str);
if (creator->display_name) {
linphone_address_set_display_name(identity, creator->display_name);
}
-
- linphone_proxy_config_set_identity(cfg, linphone_address_as_string(identity));
- linphone_proxy_config_set_server_addr(cfg, creator->domain);
- linphone_proxy_config_set_route(cfg, creator->route);
+ if (creator->route) {
+ route = ms_strdup_printf("%s;transport=%s", creator->route, linphone_transport_to_string(creator->transport));
+ }
+ if (creator->domain) {
+ domain = ms_strdup_printf("%s;transport=%s", creator->domain, linphone_transport_to_string(creator->transport));
+ }
+ 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 = linphone_dial_plan_lookup_ccc_from_e164(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)
+ linphone_proxy_config_set_server_addr(cfg, domain);
+ if (linphone_proxy_config_get_route(cfg) == NULL)
+ linphone_proxy_config_set_route(cfg, route);
linphone_proxy_config_enable_publish(cfg, FALSE);
linphone_proxy_config_enable_register(cfg, TRUE);
- ms_free(identity_str);
if (strcmp(creator->domain, "sip.linphone.org") == 0) {
linphone_proxy_config_enable_avpf(cfg, TRUE);
@@ -310,7 +500,13 @@ LinphoneProxyConfig * linphone_account_creator_configure(const LinphoneAccountCr
linphone_core_set_firewall_policy(creator->core, LinphonePolicyUseIce);
}
- info = linphone_auth_info_new(linphone_address_get_username(identity), NULL, creator->password, NULL, NULL, linphone_address_get_domain(identity));
+ info = linphone_auth_info_new(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
+ );
linphone_core_add_auth_info(creator->core, info);
linphone_address_destroy(identity);
@@ -323,3 +519,382 @@ LinphoneProxyConfig * linphone_account_creator_configure(const LinphoneAccountCr
linphone_proxy_config_unref(cfg);
return NULL;
}
+
+/****************** START OF ACCOUNT USED SECTION *****************************/
+static void _is_account_used_cb(LinphoneXmlRpcRequest *request) {
+ LinphoneAccountCreator *creator = (LinphoneAccountCreator *)linphone_xml_rpc_request_get_user_data(request);
+ if (creator->callbacks->is_account_used != NULL) {
+ LinphoneAccountCreatorStatus status = LinphoneAccountCreatorReqFailed;
+ 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) ? LinphoneAccountCreatorAccountNotExist : (
+ (strcmp(resp, "ERROR_ALIAS_DOESNT_EXIST") == 0) ? LinphoneAccountCreatorAccountExist :
+ LinphoneAccountCreatorAccountExistWithAlias);
+ if (status == LinphoneAccountCreatorAccountExistWithAlias) {
+ set_string(&creator->phone_number, resp, FALSE);
+ }
+ }
+ creator->callbacks->is_account_used(creator, status, resp);
+ }
+}
+
+LinphoneAccountCreatorStatus linphone_account_creator_is_account_used(LinphoneAccountCreator *creator) {
+ LinphoneXmlRpcRequest *request;
+ if (!creator->username && !creator->phone_number) {
+ if (creator->callbacks->is_account_used != NULL) {
+ creator->callbacks->is_account_used(creator, LinphoneAccountCreatorReqFailed, "Missing required parameters");
+ }
+ return LinphoneAccountCreatorReqFailed;
+ }
+
+ request = linphone_xml_rpc_request_new_with_args("get_phone_number_for_account", LinphoneXmlRpcArgString,
+ LinphoneXmlRpcArgString, creator->username ? creator->username : creator->phone_number,
+ LinphoneXmlRpcArgString, creator->domain,
+ LinphoneXmlRpcArgNone);
+
+ 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_used_cb);
+ linphone_xml_rpc_session_send_request(creator->xmlrpc_session, request);
+ linphone_xml_rpc_request_unref(request);
+
+ return LinphoneAccountCreatorOK;
+}
+/****************** END OF CREATE ACCOUNT USED SECTION ************************/
+
+/****************** START OF CREATE ACCOUNT SECTION ***************************/
+static void _create_account_cb(LinphoneXmlRpcRequest *request) {
+ LinphoneAccountCreator *creator = (LinphoneAccountCreator *)linphone_xml_rpc_request_get_user_data(request);
+ if (creator->callbacks->create_account != NULL) {
+ LinphoneAccountCreatorStatus status = LinphoneAccountCreatorReqFailed;
+ 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) ? LinphoneAccountCreatorAccountCreated
+ : (strcmp(resp, "ERROR_CANNOT_SEND_SMS") == 0) ? LinphoneAccountCreatorErrorServer
+ : (strcmp(resp, "ERROR_ACCOUNT_ALREADY_IN_USE") == 0) ? LinphoneAccountCreatorAccountExist
+ : (strcmp(resp, "ERROR_ALIAS_ALREADY_IN_USE") == 0) ? LinphoneAccountCreatorAccountExistWithAlias
+ :LinphoneAccountCreatorAccountNotCreated;
+ }
+ creator->callbacks->create_account(creator, status, resp);
+ }
+}
+
+static LinphoneXmlRpcRequest * _create_account_with_phone(LinphoneAccountCreator *creator) {
+ LinphoneXmlRpcRequest *request;
+ if (!creator->phone_number) {
+ return NULL;
+ }
+ request = linphone_xml_rpc_request_new_with_args("create_phone_account", LinphoneXmlRpcArgString,
+ LinphoneXmlRpcArgString, creator->phone_number,
+ LinphoneXmlRpcArgString, creator->username ? creator->username : creator->phone_number,
+ LinphoneXmlRpcArgString, creator->password ? ha1_for_passwd(creator->username ? creator->username : creator->phone_number, creator->domain, creator->password) : "",
+ LinphoneXmlRpcArgString, linphone_core_get_user_agent(creator->core),
+ LinphoneXmlRpcArgString, creator->domain,
+ LinphoneXmlRpcArgString, creator->language,
+ LinphoneXmlRpcArgNone);
+ return request;
+}
+
+static LinphoneXmlRpcRequest * _create_account_with_email(LinphoneAccountCreator *creator) {
+ LinphoneXmlRpcRequest *request;
+ if (!creator->username || !creator->email) {
+ return NULL;
+ }
+ request = linphone_xml_rpc_request_new_with_args("create_email_account", LinphoneXmlRpcArgString,
+ LinphoneXmlRpcArgString, creator->username,
+ LinphoneXmlRpcArgString, creator->email,
+ LinphoneXmlRpcArgString, ha1_for_passwd(creator->username ? creator->username : creator->phone_number, creator->domain, creator->password),
+ LinphoneXmlRpcArgString, linphone_core_get_user_agent(creator->core),
+ LinphoneXmlRpcArgString, creator->domain,
+ LinphoneXmlRpcArgNone);
+ return request;
+}
+
+LinphoneAccountCreatorStatus linphone_account_creator_create_account(LinphoneAccountCreator *creator) {
+ LinphoneXmlRpcRequest *request;
+ char *identity = _get_identity(creator);
+ if (!identity || (!(request = _create_account_with_phone(creator))
+ && !(request = _create_account_with_email(creator)))) {
+ if (creator->callbacks->create_account != NULL) {
+ creator->callbacks->create_account(creator, LinphoneAccountCreatorReqFailed, "Missing required parameters");
+ }
+ if (identity) ms_free(identity);
+ return LinphoneAccountCreatorReqFailed;
+ }
+
+ 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);
+ linphone_xml_rpc_session_send_request(creator->xmlrpc_session, request);
+ linphone_xml_rpc_request_unref(request);
+ ms_free(identity);
+ return LinphoneAccountCreatorOK;
+}
+/****************** END OF CREATE ACCOUNT SECTION *****************************/
+
+/****************** START OF VALIDATE ACCOUNT SECTION *************************/
+static void _activate_account_cb(LinphoneXmlRpcRequest *request) {
+ LinphoneAccountCreator *creator = (LinphoneAccountCreator *)linphone_xml_rpc_request_get_user_data(request);
+ if (creator->callbacks->activate_account != NULL) {
+ LinphoneAccountCreatorStatus status = LinphoneAccountCreatorReqFailed;
+ 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 = LinphoneAccountCreatorAccountAlreadyActivated;
+ } else if (strstr(resp, "ERROR_") == resp) {
+ status = LinphoneAccountCreatorAccountNotActivated;
+ } else {
+ status = LinphoneAccountCreatorAccountActivated;
+ set_string(&creator->ha1, resp, FALSE);
+ }
+ }
+ creator->callbacks->activate_account(creator, status, resp);
+ }
+}
+
+LinphoneAccountCreatorStatus linphone_account_creator_activate_account(LinphoneAccountCreator *creator) {
+ LinphoneXmlRpcRequest *request;
+ char *identity = _get_identity(creator);
+ if (!identity || !creator->activation_code) {
+ if (creator->callbacks->is_account_activated != NULL) {
+ creator->callbacks->is_account_activated(creator, LinphoneAccountCreatorReqFailed, "Missing required parameters");
+ }
+ return LinphoneAccountCreatorReqFailed;
+ }
+
+ if (creator->phone_number) {
+ request = linphone_xml_rpc_request_new_with_args("activate_phone_account", LinphoneXmlRpcArgString,
+ LinphoneXmlRpcArgString, creator->phone_number,
+ LinphoneXmlRpcArgString, creator->username ? creator->username : creator->phone_number,
+ LinphoneXmlRpcArgString, creator->activation_code,
+ LinphoneXmlRpcArgString, creator->domain,
+ LinphoneXmlRpcArgNone);
+ } else {
+ request = linphone_xml_rpc_request_new_with_args("activate_email_account", LinphoneXmlRpcArgString,
+ LinphoneXmlRpcArgString, creator->username,
+ LinphoneXmlRpcArgString, creator->activation_code,
+ LinphoneXmlRpcArgString, creator->domain,
+ LinphoneXmlRpcArgNone);
+ }
+ 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);
+ linphone_xml_rpc_session_send_request(creator->xmlrpc_session, request);
+ linphone_xml_rpc_request_unref(request);
+ ms_free(identity);
+ return LinphoneAccountCreatorOK;
+}
+/****************** END OF CREATE VALIDATE ACCOUNT SECTION ********************/
+
+/****************** START OF ACCOUNT VALIDATED SECTION ************************/
+static void _is_account_activated_cb(LinphoneXmlRpcRequest *request) {
+ LinphoneAccountCreator *creator = (LinphoneAccountCreator *)linphone_xml_rpc_request_get_user_data(request);
+ if (creator->callbacks->is_account_activated != NULL) {
+ LinphoneAccountCreatorStatus status = LinphoneAccountCreatorReqFailed;
+ 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) ? LinphoneAccountCreatorAccountActivated : LinphoneAccountCreatorAccountNotActivated;
+ }
+ creator->callbacks->is_account_activated(creator, status, resp);
+ }
+}
+
+LinphoneAccountCreatorStatus linphone_account_creator_is_account_activated(LinphoneAccountCreator *creator) {
+ LinphoneXmlRpcRequest *request;
+ char *identity = _get_identity(creator);
+ if (!identity) {
+ if (creator->callbacks->is_account_activated != NULL) {
+ creator->callbacks->is_account_activated(creator, LinphoneAccountCreatorReqFailed, "Missing required parameters");
+ }
+ return LinphoneAccountCreatorReqFailed;
+ }
+ request = linphone_xml_rpc_request_new_with_args("is_account_activated", LinphoneXmlRpcArgString,
+ LinphoneXmlRpcArgString, creator->username ? creator->username : creator->phone_number,
+ LinphoneXmlRpcArgString, creator->domain,
+ LinphoneXmlRpcArgNone);
+ 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);
+ linphone_xml_rpc_session_send_request(creator->xmlrpc_session, request);
+ linphone_xml_rpc_request_unref(request);
+ ms_free(identity);
+ return LinphoneAccountCreatorOK;
+}
+/****************** END OF CREATE ACCOUNT VALIDATED SECTION********************/
+
+/****************** START OF PHONE NUMBER VALIDATED SECTION *******************/
+
+static void _is_phone_number_used_cb(LinphoneXmlRpcRequest *request) {
+ LinphoneAccountCreator *creator = (LinphoneAccountCreator *)linphone_xml_rpc_request_get_user_data(request);
+ if (creator->callbacks->is_phone_number_used != NULL) {
+ LinphoneAccountCreatorStatus status = LinphoneAccountCreatorReqFailed;
+ 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) ? LinphoneAccountCreatorPhoneNumberUsedAccount
+ : (strcmp(resp, "OK_ALIAS") == 0) ? LinphoneAccountCreatorPhoneNumberUsedAlias
+ : LinphoneAccountCreatorPhoneNumberNotUsed;
+ }
+ creator->callbacks->is_phone_number_used(creator, status, resp);
+ }
+}
+
+LinphoneAccountCreatorStatus linphone_account_creator_is_phone_number_used(LinphoneAccountCreator *creator) {
+ LinphoneXmlRpcRequest *request;
+ char *identity = _get_identity(creator);
+ if (!identity) {
+ if (creator->callbacks->is_phone_number_used != NULL) {
+ creator->callbacks->is_phone_number_used(creator, LinphoneAccountCreatorReqFailed, "Missing required parameters");
+ }
+ return LinphoneAccountCreatorReqFailed;
+ }
+ request = linphone_xml_rpc_request_new_with_args("is_phone_number_used", LinphoneXmlRpcArgString,
+ LinphoneXmlRpcArgString, creator->phone_number,
+ LinphoneXmlRpcArgString, creator->domain,
+ LinphoneXmlRpcArgNone);
+ 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);
+ linphone_xml_rpc_session_send_request(creator->xmlrpc_session, request);
+ linphone_xml_rpc_request_unref(request);
+ ms_free(identity);
+ return LinphoneAccountCreatorOK;
+}
+
+/****************** END OF PHONE NUMBER VALIDATED SECTION *********************/
+
+/****************** START OF LINK PHONE NUMBER WITH ACCOUNT SECTION ***********/
+static void _link_phone_number_with_account_cb(LinphoneXmlRpcRequest *request) {
+ LinphoneAccountCreator *creator = (LinphoneAccountCreator *)linphone_xml_rpc_request_get_user_data(request);
+ if (creator->callbacks->link_phone_number_with_account != NULL) {
+ LinphoneAccountCreatorStatus status = LinphoneAccountCreatorReqFailed;
+ 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) ? LinphoneAccountCreatorOK : LinphoneAccountCreatorReqFailed;
+ }
+ creator->callbacks->link_phone_number_with_account(creator, status, resp);
+ }
+}
+
+LinphoneAccountCreatorStatus linphone_account_creator_link_phone_number_with_account(LinphoneAccountCreator *creator) {
+ LinphoneXmlRpcRequest *request;
+ if (!creator->phone_number || !creator->username) {
+ if (creator->callbacks->link_phone_number_with_account != NULL) {
+ creator->callbacks->link_phone_number_with_account(creator, LinphoneAccountCreatorReqFailed, "Missing required parameters");
+ }
+ return LinphoneAccountCreatorReqFailed;
+ }
+ request = linphone_xml_rpc_request_new_with_args("link_phone_number_with_account", LinphoneXmlRpcArgString,
+ LinphoneXmlRpcArgString, creator->phone_number,
+ LinphoneXmlRpcArgString, creator->username,
+ LinphoneXmlRpcArgString, creator->domain,
+ LinphoneXmlRpcArgString, creator->language,
+ LinphoneXmlRpcArgNone);
+ 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);
+ linphone_xml_rpc_session_send_request(creator->xmlrpc_session, request);
+ linphone_xml_rpc_request_unref(request);
+ return LinphoneAccountCreatorOK;
+}
+
+static void _get_phone_number_for_account_cb(LinphoneXmlRpcRequest *request) {
+ LinphoneAccountCreator *creator = (LinphoneAccountCreator *)linphone_xml_rpc_request_get_user_data(request);
+ if (creator->callbacks->is_account_linked != NULL) {
+ LinphoneAccountCreatorStatus status = LinphoneAccountCreatorReqFailed;
+ 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) ? LinphoneAccountCreatorAccountNotLinked : LinphoneAccountCreatorAccountLinked;
+ }
+ creator->callbacks->link_phone_number_with_account(creator, status, resp);
+ }
+}
+
+LinphoneAccountCreatorStatus linphone_account_creator_is_account_linked(LinphoneAccountCreator *creator) {
+ LinphoneXmlRpcRequest *request;
+ if (!creator->username || !creator->domain) {
+ return LinphoneAccountCreatorReqFailed;
+ }
+ request = linphone_xml_rpc_request_new_with_args("get_phone_number_for_account",LinphoneXmlRpcArgString,
+ LinphoneXmlRpcArgString, creator->username,
+ LinphoneXmlRpcArgString, creator->domain,
+ LinphoneXmlRpcArgNone);
+ 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);
+ linphone_xml_rpc_session_send_request(creator->xmlrpc_session, request);
+ linphone_xml_rpc_request_unref(request);
+ return LinphoneAccountCreatorOK;
+}
+/****************** END OF LINK PHONE NUMBER WITH ACCOUNT SECTION *************/
+
+/****************** START OF ACTIVE PHONE NUMBER LINK **************************/
+static void _activate_phone_number_link_cb(LinphoneXmlRpcRequest *request) {
+ LinphoneAccountCreator *creator = (LinphoneAccountCreator *)linphone_xml_rpc_request_get_user_data(request);
+ if (creator->callbacks->activate_phone_number_link != NULL) {
+ LinphoneAccountCreatorStatus status = LinphoneAccountCreatorReqFailed;
+ 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) ? LinphoneAccountCreatorReqFailed : LinphoneAccountCreatorOK;
+ }
+ creator->callbacks->activate_phone_number_link(creator, status, resp);
+ }
+}
+
+LinphoneAccountCreatorStatus linphone_account_creator_activate_phone_number_link(LinphoneAccountCreator *creator) {
+ LinphoneXmlRpcRequest *request;
+ if (!creator->phone_number || !creator->username || !creator->activation_code || (!creator->password && !creator->ha1) || !creator->domain) {
+ if (creator->callbacks->activate_phone_number_link != NULL) {
+ creator->callbacks->activate_phone_number_link(creator, LinphoneAccountCreatorReqFailed, "Missing required parameters");
+ }
+ return LinphoneAccountCreatorReqFailed;
+ }
+ request = linphone_xml_rpc_request_new_with_args("activate_phone_number_link", LinphoneXmlRpcArgString,
+ LinphoneXmlRpcArgString, creator->phone_number,
+ LinphoneXmlRpcArgString, creator->username,
+ LinphoneXmlRpcArgString, creator->activation_code,
+ LinphoneXmlRpcArgString, creator->ha1 ? creator->ha1 : ha1_for_passwd(creator->username, creator->domain, creator->password),
+ LinphoneXmlRpcArgString, creator->domain,
+ LinphoneXmlRpcArgNone);
+ 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);
+ linphone_xml_rpc_session_send_request(creator->xmlrpc_session, request);
+ linphone_xml_rpc_request_unref(request);
+ return LinphoneAccountCreatorOK;
+}
+/****************** END OF ACTIVE PHONE NUMBER LINK **************************/
+
+/****************** START OF ACTIVE PHONE NUMBER LINK **************************/
+static void _recover_phone_account_cb(LinphoneXmlRpcRequest *request) {
+ LinphoneAccountCreator *creator = (LinphoneAccountCreator *)linphone_xml_rpc_request_get_user_data(request);
+ if (creator->callbacks->recover_phone_account != NULL) {
+ LinphoneAccountCreatorStatus status = LinphoneAccountCreatorReqFailed;
+ 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 = (strstr(resp, "ERROR_CANNOT_SEND_SMS") == resp) ? LinphoneAccountCreatorErrorServer
+ : (strstr(resp, "ERROR_ACCOUNT_DOESNT_EXIST") == resp) ? LinphoneAccountCreatorAccountNotExist
+ : LinphoneAccountCreatorReqFailed;
+ } else {
+ status = LinphoneAccountCreatorOK;
+ set_string(&creator->username, resp, FALSE);
+ }
+ }
+ creator->callbacks->recover_phone_account(creator, status, resp);
+ }
+}
+
+LinphoneAccountCreatorStatus linphone_account_creator_recover_phone_account(LinphoneAccountCreator *creator) {
+ LinphoneXmlRpcRequest *request;
+ if (!creator->phone_number) {
+ if (creator->callbacks->recover_phone_account != NULL) {
+ creator->callbacks->recover_phone_account(creator, LinphoneAccountCreatorReqFailed, "Missing required parameters");
+ }
+ return LinphoneAccountCreatorReqFailed;
+ }
+ request = linphone_xml_rpc_request_new_with_args("recover_phone_account", LinphoneXmlRpcArgString,
+ LinphoneXmlRpcArgString, creator->phone_number,
+ LinphoneXmlRpcArgString, creator->domain,
+ LinphoneXmlRpcArgString, creator->language,
+ LinphoneXmlRpcArgNone);
+ 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);
+ linphone_xml_rpc_session_send_request(creator->xmlrpc_session, request);
+ linphone_xml_rpc_request_unref(request);
+ return LinphoneAccountCreatorOK;
+}
+/****************** END OF ACTIVE PHONE NUMBER LINK **************************/
diff --git a/coreapi/account_creator.h b/coreapi/account_creator.h
index fbc5d5114..c308ba3df 100644
--- a/coreapi/account_creator.h
+++ b/coreapi/account_creator.h
@@ -20,12 +20,12 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#ifndef LINPHONE_ACCOUNT_CREATOR_H_
#define LINPHONE_ACCOUNT_CREATOR_H_
+#include "linphonecore.h"
#ifdef __cplusplus
extern "C" {
#endif
-
/**
* @addtogroup misc
* @{
@@ -35,8 +35,47 @@ extern "C" {
* Enum describing the status of a LinphoneAccountCreator operation.
**/
typedef enum _LinphoneAccountCreatorStatus {
- LinphoneAccountCreatorOk,
- LinphoneAccountCreatorFailed
+ LinphoneAccountCreatorOK,
+ LinphoneAccountCreatorReqFailed,
+
+ LinphoneAccountCreatorAccountCreated,
+ LinphoneAccountCreatorAccountNotCreated,
+
+ LinphoneAccountCreatorAccountExist,
+ LinphoneAccountCreatorAccountExistWithAlias,
+ LinphoneAccountCreatorAccountNotExist,
+
+ LinphoneAccountCreatorAccountActivated,
+ LinphoneAccountCreatorAccountAlreadyActivated,
+ LinphoneAccountCreatorAccountNotActivated,
+
+ LinphoneAccountCreatorAccountLinked,
+ LinphoneAccountCreatorAccountNotLinked,
+
+ LinphoneAccountCreatorEmailInvalid,
+
+ LinphoneAccountCreatorUsernameInvalid,
+ LinphoneAccountCreatorUsernameTooShort,
+ LinphoneAccountCreatorUsernameTooLong,
+ LinphoneAccountCreatorUsernameInvalidSize,
+
+ LinphoneAccountCreatorPhoneNumberInvalid,
+ LinphoneAccountCreatorPhoneNumberTooShort,
+ LinphoneAccountCreatorPhoneNumberTooLong,
+ LinphoneAccountCreatorPhoneNumberUsedAccount,
+ LinphoneAccountCreatorPhoneNumberUsedAlias,
+ LinphoneAccountCreatorPhoneNumberNotUsed,
+
+ LinphoneAccountCreatorPasswordTooShort,
+ LinphoneAccountCreatorPasswordTooLong,
+
+ LinphoneAccountCreatorDomainInvalid,
+ LinphoneAccountCreatorRouteInvalid,
+ LinphoneAccountCreatorDisplayNameInvalid,
+ LinphoneAccountCreatorTransportNotSupported,
+ LinphoneAccountCreatorCountryCodeInvalid,
+
+ LinphoneAccountCreatorErrorServer,
} LinphoneAccountCreatorStatus;
/**
@@ -50,30 +89,16 @@ typedef struct _LinphoneAccountCreator LinphoneAccountCreator;
typedef struct _LinphoneAccountCreatorCbs LinphoneAccountCreatorCbs;
/**
- * Callback used to notify the end of a LinphoneAccountCreator test existence operation.
+ * Callback to notify a status change of the account creator.
* @param[in] creator LinphoneAccountCreator object
* @param[in] status The status of the LinphoneAccountCreator test existence operation that has just finished
**/
-typedef void (*LinphoneAccountCreatorCbsExistenceTestedCb)(LinphoneAccountCreator *creator, LinphoneAccountCreatorStatus status);
-
-/**
- * Callback used to notify the end of a LinphoneAccountCreator test validation operation.
- * @param[in] creator LinphoneAccountCreator object
- * @param[in] status The status of the LinphoneAccountCreator test validation operation that has just finished
-**/
-typedef void (*LinphoneAccountCreatorCbsValidationTestedCb)(LinphoneAccountCreator *creator, LinphoneAccountCreatorStatus status);
-
-/**
- * Callback used to notify the end of a LinphoneAccountCreator validate operation.
- * @param[in] creator LinphoneAccountCreator object
- * @param[in] status The status of the LinphoneAccountCreator validate operation that has just finished
-**/
-typedef void (*LinphoneAccountCreatorCbsValidatedCb)(LinphoneAccountCreator *creator, LinphoneAccountCreatorStatus status);
+typedef void (*LinphoneAccountCreatorCbsStatusCb)(LinphoneAccountCreator *creator, LinphoneAccountCreatorStatus status, const char* resp);
/**
* Create a LinphoneAccountCreator.
* @param[in] core The LinphoneCore used for the XML-RPC communication
- * @param[in] xmlrpc_url The URL to the XML-RPC server
+ * @param[in] xmlrpc_url The URL to the XML-RPC server. Must be NON NULL.
* @return The new LinphoneAccountCreator object
**/
LINPHONE_PUBLIC LinphoneAccountCreator * linphone_account_creator_new(LinphoneCore *core, const char *xmlrpc_url);
@@ -109,8 +134,9 @@ LINPHONE_PUBLIC void linphone_account_creator_set_user_data(LinphoneAccountCreat
* Set the username.
* @param[in] creator LinphoneAccountCreator object
* @param[in] username The username to set
+ * @return LinphoneAccountCreatorOk if everything is OK, or a specific error otherwise.
**/
-LINPHONE_PUBLIC void linphone_account_creator_set_username(LinphoneAccountCreator *creator, const char *username);
+LINPHONE_PUBLIC LinphoneAccountCreatorStatus linphone_account_creator_set_username(LinphoneAccountCreator *creator, const char *username);
/**
* Get the username.
@@ -119,12 +145,29 @@ LINPHONE_PUBLIC void linphone_account_creator_set_username(LinphoneAccountCreato
**/
LINPHONE_PUBLIC const char * linphone_account_creator_get_username(const LinphoneAccountCreator *creator);
+/**
+ * Set the phone number normalized.
+ * @param[in] creator LinphoneAccountCreator object
+ * @param[in] phone number The phone number to set
+ * @param[in] country code Country code to associate phone number with
+ * @return LinphoneAccountCreatorOk if everything is OK, or a specific error otherwise.
+**/
+LINPHONE_PUBLIC LinphoneAccountCreatorStatus linphone_account_creator_set_phone_number(LinphoneAccountCreator *creator, const char *phone_number, const char *country_code);
+
+/**
+ * Get the RFC 3966 normalized phone number.
+ * @param[in] creator LinphoneAccountCreator object
+ * @return The phone number of the LinphoneAccountCreator
+**/
+LINPHONE_PUBLIC const char * linphone_account_creator_get_phone_number(const LinphoneAccountCreator *creator);
+
/**
* Set the password.
* @param[in] creator LinphoneAccountCreator object
* @param[in] password The password to set
+ * @return LinphoneAccountCreatorOk if everything is OK, or a specific error otherwise.
**/
-LINPHONE_PUBLIC void linphone_account_creator_set_password(LinphoneAccountCreator *creator, const char *password);
+LINPHONE_PUBLIC LinphoneAccountCreatorStatus linphone_account_creator_set_password(LinphoneAccountCreator *creator, const char *password);
/**
* Get the password.
@@ -133,12 +176,57 @@ LINPHONE_PUBLIC void linphone_account_creator_set_password(LinphoneAccountCreato
**/
LINPHONE_PUBLIC const char * linphone_account_creator_get_password(const LinphoneAccountCreator *creator);
+/**
+ * Set the ha1.
+ * @param[in] creator LinphoneAccountCreator object
+ * @param[in] password The ha1 to set
+ * @return LinphoneAccountCreatorOk if everything is OK, or a specific error otherwise.
+**/
+LINPHONE_PUBLIC LinphoneAccountCreatorStatus linphone_account_creator_set_ha1(LinphoneAccountCreator *creator, const char *ha1);
+
+/**
+ * Get the ha1.
+ * @param[in] creator LinphoneAccountCreator object
+ * @return The ha1 of the LinphoneAccountCreator
+**/
+LINPHONE_PUBLIC const char * linphone_account_creator_get_ha1(const LinphoneAccountCreator *creator);
+
+/**
+ * Set the activation code.
+ * @param[in] creator LinphoneAccountCreator object
+ * @param[in] activation_code The activation code to set
+**/
+LINPHONE_PUBLIC LinphoneAccountCreatorStatus linphone_account_creator_set_activation_code(LinphoneAccountCreator *creator, const char *activation_code);
+
+/**
+ * Set the language to use in email or SMS if supported.
+ * @param[in] creator LinphoneAccountCreator object
+ * @param[in] activation_code The language code to use
+**/
+LINPHONE_PUBLIC LinphoneAccountCreatorStatus linphone_account_creator_set_language(LinphoneAccountCreator *creator, const char *lang);
+
+/**
+ * Set the transport.
+ * @param[in] creator LinphoneAccountCreator object
+ * @param[in] transport The transport to set
+ * @return LinphoneAccountCreatorOk if everything is OK, or a specific error if given transport is not supported by linphone core.
+**/
+LINPHONE_PUBLIC LinphoneAccountCreatorStatus linphone_account_creator_set_transport(LinphoneAccountCreator *creator, LinphoneTransportType transport);
+
+/**
+ * Get the transport.
+ * @param[in] creator LinphoneAccountCreator object
+ * @return The transport of the LinphoneAccountCreator
+**/
+LINPHONE_PUBLIC LinphoneTransportType linphone_account_creator_get_transport(const LinphoneAccountCreator *creator);
+
/**
* Set the domain.
* @param[in] creator LinphoneAccountCreator object
* @param[in] domain The domain to set
+ * @return LinphoneAccountCreatorOk if everything is OK, or a specific error otherwise.
**/
-LINPHONE_PUBLIC void linphone_account_creator_set_domain(LinphoneAccountCreator *creator, const char *domain);
+LINPHONE_PUBLIC LinphoneAccountCreatorStatus linphone_account_creator_set_domain(LinphoneAccountCreator *creator, const char *domain);
/**
* Get the domain.
@@ -151,8 +239,9 @@ LINPHONE_PUBLIC const char * linphone_account_creator_get_domain(const LinphoneA
* Set the route.
* @param[in] creator LinphoneAccountCreator object
* @param[in] route The route to set
+ * @return LinphoneAccountCreatorOk if everything is OK, or a specific error otherwise.
**/
-LINPHONE_PUBLIC void linphone_account_creator_set_route(LinphoneAccountCreator *creator, const char *route);
+LINPHONE_PUBLIC LinphoneAccountCreatorStatus linphone_account_creator_set_route(LinphoneAccountCreator *creator, const char *route);
/**
* Get the route.
@@ -165,8 +254,9 @@ LINPHONE_PUBLIC const char * linphone_account_creator_get_route(const LinphoneAc
* Set the email.
* @param[in] creator LinphoneAccountCreator object
* @param[in] display_name The display name to set
+ * @return LinphoneAccountCreatorOk if everything is OK, or a specific error otherwise.
**/
-LINPHONE_PUBLIC void linphone_account_creator_set_display_name(LinphoneAccountCreator *creator, const char *display_name);
+LINPHONE_PUBLIC LinphoneAccountCreatorStatus linphone_account_creator_set_display_name(LinphoneAccountCreator *creator, const char *display_name);
/**
* Get the email.
@@ -179,8 +269,9 @@ LINPHONE_PUBLIC const char * linphone_account_creator_get_display_name(const Lin
* Set the email.
* @param[in] creator LinphoneAccountCreator object
* @param[in] email The email to set
+ * @return LinphoneAccountCreatorOk if everything is OK, or a specific error otherwise.
**/
-LINPHONE_PUBLIC void linphone_account_creator_set_email(LinphoneAccountCreator *creator, const char *email);
+LINPHONE_PUBLIC LinphoneAccountCreatorStatus linphone_account_creator_set_email(LinphoneAccountCreator *creator, const char *email);
/**
* Get the email.
@@ -189,20 +280,6 @@ LINPHONE_PUBLIC void linphone_account_creator_set_email(LinphoneAccountCreator *
**/
LINPHONE_PUBLIC const char * linphone_account_creator_get_email(const LinphoneAccountCreator *creator);
-/**
- * Enable the newsletter subscription.
- * @param[in] creator LinphoneAccountCreator object
- * @param[in] subscribe A boolean telling whether to subscribe to the newsletter or not.
-**/
-LINPHONE_PUBLIC void linphone_account_creator_enable_newsletter_subscription(LinphoneAccountCreator *creator, bool_t subscribe);
-
-/**
- * Tell whether to subscribe to the newsletter or not.
- * @param[in] creator LinphoneAccountCreator object
- * @return A boolean telling whether to subscribe to the newsletter or not.
-**/
-LINPHONE_PUBLIC bool_t linphone_account_creator_newsletter_subscription_enabled(const LinphoneAccountCreator *creator);
-
/**
* Get the LinphoneAccountCreatorCbs object associated with a LinphoneAccountCreator.
* @param[in] creator LinphoneAccountCreator object
@@ -213,23 +290,60 @@ LINPHONE_PUBLIC LinphoneAccountCreatorCbs * linphone_account_creator_get_callbac
/**
* Send an XML-RPC request to test the existence of a Linphone account.
* @param[in] creator LinphoneAccountCreator object
- * @return LinphoneAccountCreatorOk if the request has been sent, LinphoneAccountCreatorFailed otherwise
+ * @return LinphoneAccountCreatorOk if the request has been sent, LinphoneAccountCreatorReqFailed otherwise
**/
-LINPHONE_PUBLIC LinphoneAccountCreatorStatus linphone_account_creator_test_existence(LinphoneAccountCreator *creator);
-
-/**
- * Send an XML-RPC request to test the validation of a Linphone account.
- * @param[in] creator LinphoneAccountCreator object
- * @return LinphoneAccountCreatorOk if the request has been sent, LinphoneAccountCreatorFailed otherwise
-**/
-LINPHONE_PUBLIC LinphoneAccountCreatorStatus linphone_account_creator_test_validation(LinphoneAccountCreator *creator);
+LINPHONE_PUBLIC LinphoneAccountCreatorStatus linphone_account_creator_is_account_used(LinphoneAccountCreator *creator);
/**
* Send an XML-RPC request to create a Linphone account.
* @param[in] creator LinphoneAccountCreator object
- * @return LinphoneAccountCreatorOk if the request has been sent, LinphoneAccountCreatorFailed otherwise
+ * @return LinphoneAccountCreatorOk if the request has been sent, LinphoneAccountCreatorReqFailed otherwise
**/
-LINPHONE_PUBLIC LinphoneAccountCreatorStatus linphone_account_creator_validate(LinphoneAccountCreator *creator);
+LINPHONE_PUBLIC LinphoneAccountCreatorStatus linphone_account_creator_create_account(LinphoneAccountCreator *creator);
+
+/**
+ * Send an XML-RPC request to activate a Linphone account.
+ * @param[in] creator LinphoneAccountCreator object
+ * @return LinphoneAccountCreatorOk if the request has been sent, LinphoneAccountCreatorReqFailed otherwise
+**/
+LINPHONE_PUBLIC LinphoneAccountCreatorStatus linphone_account_creator_activate_account(LinphoneAccountCreator *creator);
+
+/**
+ * Send an XML-RPC request to test the validation of a Linphone account.
+ * @param[in] creator LinphoneAccountCreator object
+ * @return LinphoneAccountCreatorOk if the request has been sent, LinphoneAccountCreatorReqFailed otherwise
+**/
+LINPHONE_PUBLIC LinphoneAccountCreatorStatus linphone_account_creator_is_account_activated(LinphoneAccountCreator *creator);
+
+/**
+ * Send an XML-RPC request to test the existence a phone number with a Linphone account.
+ * @param[in] creator LinphoneAccountCreator object
+ * @return LinphoneAccountCreatorOk if the request has been sent, LinphoneAccountCreatorReqFailed otherwise
+**/
+LINPHONE_PUBLIC LinphoneAccountCreatorStatus linphone_account_creator_is_phone_number_used(LinphoneAccountCreator *creator);
+
+/**
+ * Send an XML-RPC request to link a phone number with a Linphone account.
+ * @param[in] creator LinphoneAccountCreator object
+ * @return LinphoneAccountCreatorOK if the request has been sent, LinphoneAccountCreatorReqFailed otherwise
+**/
+LINPHONE_PUBLIC LinphoneAccountCreatorStatus linphone_account_creator_link_phone_number_with_account(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 LinphoneAccountCreatorOK if the request has been sent, LinphoneAccountCreatorReqFailed otherwise
+**/
+LINPHONE_PUBLIC LinphoneAccountCreatorStatus linphone_account_creator_activate_phone_number_link(LinphoneAccountCreator *creator);
+
+LINPHONE_PUBLIC LinphoneAccountCreatorStatus linphone_account_creator_recover_phone_account(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(LinphoneAccountCreator *creator);
/**
* Configure an account (create a proxy config and authentication info for it).
@@ -238,7 +352,6 @@ LINPHONE_PUBLIC LinphoneAccountCreatorStatus linphone_account_creator_validate(L
**/
LINPHONE_PUBLIC LinphoneProxyConfig * linphone_account_creator_configure(const LinphoneAccountCreator *creator);
-
/**
* Acquire a reference to a LinphoneAccountCreatorCbs object.
* @param[in] cbs LinphoneAccountCreatorCbs object.
@@ -266,47 +379,120 @@ LINPHONE_PUBLIC void *linphone_account_creator_cbs_get_user_data(const LinphoneA
**/
LINPHONE_PUBLIC void linphone_account_creator_cbs_set_user_data(LinphoneAccountCreatorCbs *cbs, void *ud);
+/**
+ * Get the current linked tested callback.
+ * @param[in] cbs LinphoneAccountCreatorCbs object.
+ * @return The current linked tested callback.
+**/
+LINPHONE_PUBLIC LinphoneAccountCreatorCbsStatusCb linphone_account_creator_cbs_get_is_account_linked(const LinphoneAccountCreatorCbs *cbs);
+
+/**
+ * Set the linked tested callback
+ * @param[in] cbs LinphoneAccountCreatorCbs object.
+ * @param[in] cb The existence tested callback to be used.
+**/
+LINPHONE_PUBLIC void linphone_account_creator_cbs_set_is_account_linked(LinphoneAccountCreatorCbs *cbs, LinphoneAccountCreatorCbsStatusCb cb);
+
/**
* Get the existence tested callback.
* @param[in] cbs LinphoneAccountCreatorCbs object.
* @return The current existence tested callback.
**/
-LINPHONE_PUBLIC LinphoneAccountCreatorCbsExistenceTestedCb linphone_account_creator_cbs_get_existence_tested(const LinphoneAccountCreatorCbs *cbs);
+LINPHONE_PUBLIC LinphoneAccountCreatorCbsStatusCb linphone_account_creator_cbs_get_is_account_used(const LinphoneAccountCreatorCbs *cbs);
/**
* Set the existence tested callback.
* @param[in] cbs LinphoneAccountCreatorCbs object.
* @param[in] cb The existence tested callback to be used.
**/
-LINPHONE_PUBLIC void linphone_account_creator_cbs_set_existence_tested(LinphoneAccountCreatorCbs *cbs, LinphoneAccountCreatorCbsExistenceTestedCb cb);
+LINPHONE_PUBLIC void linphone_account_creator_cbs_set_is_account_used(LinphoneAccountCreatorCbs *cbs, LinphoneAccountCreatorCbsStatusCb cb);
+
+/**
+ * Get the create account callback.
+ * @param[in] cbs LinphoneAccountCreatorCbs object.
+ * @return The current create account callback.
+**/
+LINPHONE_PUBLIC LinphoneAccountCreatorCbsStatusCb linphone_account_creator_cbs_get_create_account(const LinphoneAccountCreatorCbs *cbs);
+
+/**
+ * Set the create account callback.
+ * @param[in] cbs LinphoneAccountCreatorCbs object.
+ * @param[in] cb The create account callback to be used.
+**/
+LINPHONE_PUBLIC void linphone_account_creator_cbs_set_create_account(LinphoneAccountCreatorCbs *cbs, LinphoneAccountCreatorCbsStatusCb cb);
+
+/**
+ * Get the activate account callback.
+ * @param[in] cbs LinphoneAccountCreatorCbs object.
+ * @return The current activate account callback.
+**/
+LINPHONE_PUBLIC LinphoneAccountCreatorCbsStatusCb linphone_account_creator_cbs_get_activate_account(const LinphoneAccountCreatorCbs *cbs);
+
+/**
+ * Set the activate account callback.
+ * @param[in] cbs LinphoneAccountCreatorCbs object.
+ * @param[in] cb The activate account callback to be used.
+**/
+LINPHONE_PUBLIC void linphone_account_creator_cbs_set_activate_account(LinphoneAccountCreatorCbs *cbs, LinphoneAccountCreatorCbsStatusCb cb);
+
+/**
+ * Get the link phone number with account callback.
+ * @param[in] cbs LinphoneAccountCreatorCbs object.
+ * @return The current link phone number with account callback.
+**/
+LINPHONE_PUBLIC LinphoneAccountCreatorCbsStatusCb linphone_account_creator_cbs_get_link_phone_number_with_account(const LinphoneAccountCreatorCbs *cbs);
+
+/**
+ * Set the link phone number with account callback.
+ * @param[in] cbs LinphoneAccountCreatorCbs object.
+ * @param[in] cb The link phone number with account callback to be used.
+**/
+LINPHONE_PUBLIC void linphone_account_creator_cbs_set_link_phone_number_with_account(LinphoneAccountCreatorCbs *cbs, LinphoneAccountCreatorCbsStatusCb cb);
+
+/**
+ * Get the activate phone number link callback.
+ * @param[in] cbs LinphoneAccountCreatorCbs object.
+ * @return The current activate phone number link callback.
+**/
+LINPHONE_PUBLIC LinphoneAccountCreatorCbsStatusCb linphone_account_creator_cbs_get_activate_phone_number_link(const LinphoneAccountCreatorCbs *cbs);
+
+/**
+ * Set the activate phone number link callback.
+ * @param[in] cbs LinphoneAccountCreatorCbs object.
+ * @param[in] cb The activate phone number link callback to be used.
+**/
+LINPHONE_PUBLIC void linphone_account_creator_cbs_set_activate_phone_number_link(LinphoneAccountCreatorCbs *cbs, LinphoneAccountCreatorCbsStatusCb cb);
/**
* Get the validation tested callback.
* @param[in] cbs LinphoneAccountCreatorCbs object.
* @return The current validation tested callback.
**/
-LINPHONE_PUBLIC LinphoneAccountCreatorCbsValidationTestedCb linphone_account_creator_cbs_get_validation_tested(const LinphoneAccountCreatorCbs *cbs);
+LINPHONE_PUBLIC LinphoneAccountCreatorCbsStatusCb linphone_account_creator_cbs_get_is_account_activated(const LinphoneAccountCreatorCbs *cbs);
/**
* Set the validation tested callback.
* @param[in] cbs LinphoneAccountCreatorCbs object.
* @param[in] cb The validation tested callback to be used.
**/
-LINPHONE_PUBLIC void linphone_account_creator_cbs_set_validation_tested(LinphoneAccountCreatorCbs *cbs, LinphoneAccountCreatorCbsValidationTestedCb cb);
+LINPHONE_PUBLIC void linphone_account_creator_cbs_set_is_account_activated(LinphoneAccountCreatorCbs *cbs, LinphoneAccountCreatorCbsStatusCb cb);
/**
- * Get the validated callback.
+ * Get the is phone number used callback.
* @param[in] cbs LinphoneAccountCreatorCbs object.
- * @return The current validated callback.
+ * @return The current is phone number used callback
**/
-LINPHONE_PUBLIC LinphoneAccountCreatorCbsValidatedCb linphone_account_creator_cbs_get_validated(const LinphoneAccountCreatorCbs *cbs);
+LINPHONE_PUBLIC LinphoneAccountCreatorCbsStatusCb linphone_account_creator_cbs_get_is_phone_number_used(const LinphoneAccountCreatorCbs *cbs);
/**
- * Set the validated callback.
+ * Set the is phone number used callback.
* @param[in] cbs LinphoneAccountCreatorCbs object.
- * @param[in] cb The validated callback to be used.
+ * @param[in] cb is phone number to be used.
**/
-LINPHONE_PUBLIC void linphone_account_creator_cbs_set_validated(LinphoneAccountCreatorCbs *cbs, LinphoneAccountCreatorCbsValidatedCb cb);
+LINPHONE_PUBLIC void linphone_account_creator_cbs_set_is_phone_number_used(LinphoneAccountCreatorCbs *cbs, LinphoneAccountCreatorCbsStatusCb cb);
+
+LINPHONE_PUBLIC void linphone_account_creator_cbs_set_recover_phone_account(LinphoneAccountCreatorCbs *cbs, LinphoneAccountCreatorCbsStatusCb cb);
+LINPHONE_PUBLIC LinphoneAccountCreatorCbsStatusCb linphone_account_creator_cbs_get_recover_phone_account(const LinphoneAccountCreatorCbs *cbs);
/**
* @}
diff --git a/coreapi/address.c b/coreapi/address.c
index cb2f3cdf2..2b684b5d9 100644
--- a/coreapi/address.c
+++ b/coreapi/address.c
@@ -89,37 +89,42 @@ const char *linphone_address_get_domain(const LinphoneAddress *u){
/**
* Sets the display name.
**/
-void linphone_address_set_display_name(LinphoneAddress *u, const char *display_name){
+int linphone_address_set_display_name(LinphoneAddress *u, const char *display_name){
sal_address_set_display_name(u,display_name);
+ return 0;
}
/**
* Sets the username.
**/
-void linphone_address_set_username(LinphoneAddress *uri, const char *username){
+int linphone_address_set_username(LinphoneAddress *uri, const char *username){
sal_address_set_username(uri,username);
+ return 0;
}
/**
* Sets the domain.
**/
-void linphone_address_set_domain(LinphoneAddress *uri, const char *host){
+int linphone_address_set_domain(LinphoneAddress *uri, const char *host){
sal_address_set_domain(uri,host);
+ return 0;
}
/**
* Sets the port number.
**/
-void linphone_address_set_port(LinphoneAddress *uri, int port){
+int linphone_address_set_port(LinphoneAddress *uri, int port){
sal_address_set_port(uri,port);
+ return 0;
}
/**
* Set a transport.
**/
-void linphone_address_set_transport(LinphoneAddress *uri, LinphoneTransportType tp){
+int linphone_address_set_transport(LinphoneAddress *uri, LinphoneTransportType tp){
sal_address_set_transport(uri,(SalTransport)tp);
+ return 0;
}
/**
@@ -129,6 +134,20 @@ LinphoneTransportType linphone_address_get_transport(const LinphoneAddress *uri)
return (LinphoneTransportType)sal_address_get_transport(uri);
}
+/**
+ * Set the value of the method parameter
+**/
+void linphone_address_set_method_param(LinphoneAddress *addr, const char *method) {
+ sal_address_set_method_param(addr, method);
+}
+
+/**
+ * Get the value of the method parameter
+**/
+const char *linphone_address_get_method_param(const LinphoneAddress *addr) {
+ return sal_address_get_method_param(addr);
+}
+
/**
* Removes address's tags and uri headers so that it is displayable to the user.
**/
@@ -169,7 +188,8 @@ bool_t linphone_address_get_secure(const LinphoneAddress *uri){
/**
* Make the address refer to a secure location (sips scheme)
- * @param enabled TRUE if address is requested to be secure.
+ * @param[in] addr A #LinphoneAddress object
+ * @param[in] enabled TRUE if address is requested to be secure.
**/
void linphone_address_set_secure(LinphoneAddress *addr, bool_t enabled){
sal_address_set_secure(addr, enabled);
@@ -279,6 +299,38 @@ void linphone_address_set_header(LinphoneAddress *addr, const char *header_name,
sal_address_set_header(addr,header_name,header_value);
}
+bool_t linphone_address_has_param(const LinphoneAddress *addr, const char *name) {
+ return sal_address_has_param(addr, name);
+}
+
+const char * linphone_address_get_param(const LinphoneAddress *addr, const char *name) {
+ return sal_address_get_param(addr, name);
+}
+
+void linphone_address_set_param(LinphoneAddress *addr, const char *name, const char *value) {
+ sal_address_set_param(addr, name, value);
+}
+
+void linphone_address_set_params(LinphoneAddress *addr, const char *params) {
+ sal_address_set_params(addr, params);
+}
+
+void linphone_address_set_uri_param(LinphoneAddress *addr, const char *name, const char *value) {
+ sal_address_set_uri_param(addr, name, value);
+}
+
+void linphone_address_set_uri_params(LinphoneAddress *addr, const char *params) {
+ sal_address_set_uri_params(addr, params);
+}
+
+bool_t linphone_address_has_uri_param(const LinphoneAddress *addr, const char *name) {
+ return sal_address_has_uri_param(addr, name);
+}
+
+const char * linphone_address_get_uri_param(const LinphoneAddress *addr, const char *name) {
+ return sal_address_get_uri_param(addr, name);
+}
+
LinphoneAddress * linphone_core_create_address(LinphoneCore *lc, const char *address) {
return linphone_address_new(address);
}
diff --git a/coreapi/authentication.c b/coreapi/authentication.c
index f0f5eec2e..ec956f5f1 100644
--- a/coreapi/authentication.c
+++ b/coreapi/authentication.c
@@ -44,151 +44,226 @@ LinphoneAuthInfo *linphone_auth_info_new(const char *username, const char *useri
LinphoneAuthInfo *linphone_auth_info_clone(const LinphoneAuthInfo *ai){
LinphoneAuthInfo *obj=ms_new0(LinphoneAuthInfo,1);
- if (ai->username) obj->username=ms_strdup(ai->username);
- if (ai->userid) obj->userid=ms_strdup(ai->userid);
- if (ai->passwd) obj->passwd=ms_strdup(ai->passwd);
- if (ai->ha1) obj->ha1=ms_strdup(ai->ha1);
- if (ai->realm) obj->realm=ms_strdup(ai->realm);
- if (ai->domain) obj->domain=ms_strdup(ai->domain);
+ if (ai->username) obj->username = ms_strdup(ai->username);
+ if (ai->userid) obj->userid = ms_strdup(ai->userid);
+ if (ai->passwd) obj->passwd = ms_strdup(ai->passwd);
+ if (ai->ha1) obj->ha1 = ms_strdup(ai->ha1);
+ if (ai->realm) obj->realm = ms_strdup(ai->realm);
+ if (ai->domain) obj->domain = ms_strdup(ai->domain);
+ if (ai->tls_cert) obj->tls_cert = ms_strdup(ai->tls_cert);
+ if (ai->tls_key) obj->tls_key = ms_strdup(ai->tls_key);
+ if (ai->tls_cert_path) obj->tls_cert_path = ms_strdup(ai->tls_cert_path);
+ if (ai->tls_key_path) obj->tls_key_path = ms_strdup(ai->tls_key_path);
return obj;
}
-const char *linphone_auth_info_get_username(const LinphoneAuthInfo *i){
+const char *linphone_auth_info_get_username(const LinphoneAuthInfo *i) {
return i->username;
}
-const char *linphone_auth_info_get_passwd(const LinphoneAuthInfo *i){
+const char *linphone_auth_info_get_passwd(const LinphoneAuthInfo *i) {
return i->passwd;
}
-const char *linphone_auth_info_get_userid(const LinphoneAuthInfo *i){
+const char *linphone_auth_info_get_userid(const LinphoneAuthInfo *i) {
return i->userid;
}
-const char *linphone_auth_info_get_realm(const LinphoneAuthInfo *i){
+const char *linphone_auth_info_get_realm(const LinphoneAuthInfo *i) {
return i->realm;
}
-const char *linphone_auth_info_get_domain(const LinphoneAuthInfo *i){
+const char *linphone_auth_info_get_domain(const LinphoneAuthInfo *i) {
return i->domain;
}
-const char *linphone_auth_info_get_ha1(const LinphoneAuthInfo *i){
+const char *linphone_auth_info_get_ha1(const LinphoneAuthInfo *i) {
return i->ha1;
}
-void linphone_auth_info_set_passwd(LinphoneAuthInfo *info, const char *passwd){
- if (info->passwd!=NULL) {
+const char *linphone_auth_info_get_tls_cert(const LinphoneAuthInfo *i) {
+ return i->tls_cert;
+}
+
+const char *linphone_auth_info_get_tls_key(const LinphoneAuthInfo *i) {
+ return i->tls_key;
+}
+
+const char *linphone_auth_info_get_tls_cert_path(const LinphoneAuthInfo *i) {
+ return i->tls_cert_path;
+}
+
+const char *linphone_auth_info_get_tls_key_path(const LinphoneAuthInfo *i) {
+ return i->tls_key_path;
+}
+
+
+void linphone_auth_info_set_passwd(LinphoneAuthInfo *info, const char *passwd) {
+ if (info->passwd) {
ms_free(info->passwd);
- info->passwd=NULL;
+ info->passwd = NULL;
}
- if (passwd!=NULL && (strlen(passwd)>0)) info->passwd=ms_strdup(passwd);
+ if (passwd && strlen(passwd) > 0) info->passwd = ms_strdup(passwd);
}
-void linphone_auth_info_set_username(LinphoneAuthInfo *info, const char *username){
- if (info->username){
+void linphone_auth_info_set_username(LinphoneAuthInfo *info, const char *username) {
+ if (info->username) {
ms_free(info->username);
- info->username=NULL;
+ info->username = NULL;
}
- if (username && strlen(username)>0) info->username=ms_strdup(username);
+ if (username && strlen(username) > 0) info->username = ms_strdup(username);
}
-void linphone_auth_info_set_userid(LinphoneAuthInfo *info, const char *userid){
- if (info->userid){
+void linphone_auth_info_set_userid(LinphoneAuthInfo *info, const char *userid) {
+ if (info->userid) {
ms_free(info->userid);
- info->userid=NULL;
+ info->userid = NULL;
}
- if (userid && strlen(userid)>0) info->userid=ms_strdup(userid);
+ if (userid && strlen(userid) > 0) info->userid = ms_strdup(userid);
}
-void linphone_auth_info_set_realm(LinphoneAuthInfo *info, const char *realm){
- if (info->realm){
+void linphone_auth_info_set_realm(LinphoneAuthInfo *info, const char *realm) {
+ if (info->realm) {
ms_free(info->realm);
- info->realm=NULL;
+ info->realm = NULL;
}
- if (realm && strlen(realm)>0) info->realm=ms_strdup(realm);
+ if (realm && strlen(realm) > 0) info->realm = ms_strdup(realm);
}
-void linphone_auth_info_set_domain(LinphoneAuthInfo *info, const char *domain){
- if (info->domain){
+void linphone_auth_info_set_domain(LinphoneAuthInfo *info, const char *domain) {
+ if (info->domain) {
ms_free(info->domain);
- info->domain=NULL;
+ info->domain = NULL;
}
- if (domain && strlen(domain)>0) info->domain=ms_strdup(domain);
+ if (domain && strlen(domain) > 0) info->domain = ms_strdup(domain);
}
-void linphone_auth_info_set_ha1(LinphoneAuthInfo *info, const char *ha1){
- if (info->ha1){
+void linphone_auth_info_set_ha1(LinphoneAuthInfo *info, const char *ha1) {
+ if (info->ha1) {
ms_free(info->ha1);
- info->ha1=NULL;
+ info->ha1 = NULL;
}
- if (ha1 && strlen(ha1)>0) info->ha1=ms_strdup(ha1);
+ if (ha1 && strlen(ha1) > 0) info->ha1 = ms_strdup(ha1);
+}
+
+void linphone_auth_info_set_tls_cert(LinphoneAuthInfo *info, const char *tls_cert) {
+ if (info->tls_cert) {
+ ms_free(info->tls_cert);
+ info->tls_cert = NULL;
+ }
+ if (tls_cert && strlen(tls_cert) > 0) info->tls_cert = ms_strdup(tls_cert);
+}
+
+void linphone_auth_info_set_tls_key(LinphoneAuthInfo *info, const char *tls_key) {
+ if (info->tls_key) {
+ ms_free(info->tls_key);
+ info->tls_key = NULL;
+ }
+ if (tls_key && strlen(tls_key) > 0) info->tls_key = ms_strdup(tls_key);
+}
+
+void linphone_auth_info_set_tls_cert_path(LinphoneAuthInfo *info, const char *tls_cert_path) {
+ if (info->tls_cert_path) {
+ ms_free(info->tls_cert_path);
+ info->tls_cert_path = NULL;
+ }
+ if (tls_cert_path && strlen(tls_cert_path) > 0) info->tls_cert_path = ms_strdup(tls_cert_path);
+}
+
+void linphone_auth_info_set_tls_key_path(LinphoneAuthInfo *info, const char *tls_key_path) {
+ if (info->tls_key_path) {
+ ms_free(info->tls_key_path);
+ info->tls_key_path = NULL;
+ }
+ if (tls_key_path && strlen(tls_key_path) > 0) info->tls_key_path = ms_strdup(tls_key_path);
}
/**
* Destroys a LinphoneAuthInfo object.
**/
void linphone_auth_info_destroy(LinphoneAuthInfo *obj){
- if (obj->username!=NULL) ms_free(obj->username);
- if (obj->userid!=NULL) ms_free(obj->userid);
- if (obj->passwd!=NULL) ms_free(obj->passwd);
- if (obj->ha1!=NULL) ms_free(obj->ha1);
- if (obj->realm!=NULL) ms_free(obj->realm);
- if (obj->domain!=NULL) ms_free(obj->domain);
+ if (obj->username != NULL) ms_free(obj->username);
+ if (obj->userid != NULL) ms_free(obj->userid);
+ if (obj->passwd != NULL) ms_free(obj->passwd);
+ if (obj->ha1 != NULL) ms_free(obj->ha1);
+ if (obj->realm != NULL) ms_free(obj->realm);
+ if (obj->domain != NULL) ms_free(obj->domain);
+ if (obj->tls_cert != NULL) ms_free(obj->tls_cert);
+ if (obj->tls_key != NULL) ms_free(obj->tls_key);
+ if (obj->tls_cert_path != NULL) ms_free(obj->tls_cert_path);
+ if (obj->tls_key_path != NULL) ms_free(obj->tls_key_path);
ms_free(obj);
}
-void linphone_auth_info_write_config(LpConfig *config, LinphoneAuthInfo *obj, int pos)
-{
+void linphone_auth_info_write_config(LpConfig *config, LinphoneAuthInfo *obj, int pos) {
char key[50];
- sprintf(key,"auth_info_%i",pos);
- lp_config_clean_section(config,key);
+ bool_t store_ha1_passwd = lp_config_get_int(config, "sip", "store_ha1_passwd", 1);
- if (obj==NULL || lp_config_get_int(config, "sip", "store_auth_info", 1) == 0){
+ sprintf(key, "auth_info_%i", pos);
+ lp_config_clean_section(config, key);
+
+ if (obj == NULL || lp_config_get_int(config, "sip", "store_auth_info", 1) == 0) {
return;
}
- if (!obj->ha1 && obj->realm && obj->passwd && (obj->username||obj->userid) && lp_config_get_int(config, "sip", "store_ha1_passwd", 1) == 1) {
+ if (!obj->ha1 && obj->realm && obj->passwd && (obj->username || obj->userid) && store_ha1_passwd) {
/*compute ha1 to avoid storing clear text password*/
- obj->ha1=ms_malloc(33);
- sal_auth_compute_ha1(obj->userid?obj->userid:obj->username,obj->realm,obj->passwd,obj->ha1);
+ obj->ha1 = ms_malloc(33);
+ sal_auth_compute_ha1(obj->userid ? obj->userid : obj->username, obj->realm, obj->passwd, obj->ha1);
}
- if (obj->username!=NULL){
- lp_config_set_string(config,key,"username",obj->username);
+ if (obj->username != NULL) {
+ lp_config_set_string(config, key, "username", obj->username);
}
- if (obj->userid!=NULL){
- lp_config_set_string(config,key,"userid",obj->userid);
+ if (obj->userid != NULL) {
+ lp_config_set_string(config, key, "userid", obj->userid);
}
- if (obj->ha1!=NULL){
- lp_config_set_string(config,key,"ha1",obj->ha1);
- } else if (obj->passwd!=NULL){ /*only write passwd if no ha1*/
- lp_config_set_string(config,key,"passwd",obj->passwd);
+ if (obj->ha1 != NULL) {
+ lp_config_set_string(config, key, "ha1", obj->ha1);
}
- if (obj->realm!=NULL){
- lp_config_set_string(config,key,"realm",obj->realm);
+ if (obj->passwd != NULL) {
+ if (store_ha1_passwd && obj->ha1) {
+ /*if we have our ha1 and store_ha1_passwd set to TRUE, then drop the clear text password for security*/
+ linphone_auth_info_set_passwd(obj, NULL);
+ } else {
+ /*we store clear text password only if store_ha1_passwd is FALSE AND we have an ha1 to store. Otherwise, passwd would simply be removed, which might bring major auth issue*/
+ lp_config_set_string(config, key, "passwd", obj->passwd);
+ }
}
- if (obj->domain!=NULL){
- lp_config_set_string(config,key,"domain",obj->domain);
+ if (obj->realm != NULL) {
+ lp_config_set_string(config, key, "realm", obj->realm);
+ }
+ if (obj->domain != NULL) {
+ lp_config_set_string(config, key, "domain", obj->domain);
+ }
+ if (obj->tls_cert_path != NULL) {
+ lp_config_set_string(config, key, "client_cert_chain", obj->tls_cert_path);
+ }
+ if (obj->tls_key_path != NULL) {
+ lp_config_set_string(config, key, "client_cert_key", obj->tls_key_path);
}
}
LinphoneAuthInfo *linphone_auth_info_new_from_config_file(LpConfig * config, int pos)
{
char key[50];
- const char *username,*userid,*passwd,*ha1,*realm,*domain;
+ const char *username,*userid,*passwd,*ha1,*realm,*domain,*tls_cert_path,*tls_key_path;
LinphoneAuthInfo *ret;
- sprintf(key,"auth_info_%i",pos);
- if (!lp_config_has_section(config,key)){
+ sprintf(key, "auth_info_%i", pos);
+ if (!lp_config_has_section(config, key)) {
return NULL;
}
- username=lp_config_get_string(config,key,"username",NULL);
- userid=lp_config_get_string(config,key,"userid",NULL);
- passwd=lp_config_get_string(config,key,"passwd",NULL);
- ha1=lp_config_get_string(config,key,"ha1",NULL);
- realm=lp_config_get_string(config,key,"realm",NULL);
- domain=lp_config_get_string(config,key,"domain",NULL);
- ret=linphone_auth_info_new(username,userid,passwd,ha1,realm,domain);
+ username = lp_config_get_string(config, key, "username", NULL);
+ userid = lp_config_get_string(config, key, "userid", NULL);
+ passwd = lp_config_get_string(config, key, "passwd", NULL);
+ ha1 = lp_config_get_string(config, key, "ha1", NULL);
+ realm = lp_config_get_string(config, key, "realm", NULL);
+ domain = lp_config_get_string(config, key, "domain", NULL);
+ tls_cert_path = lp_config_get_string(config, key, "client_cert_chain", NULL);
+ tls_key_path = lp_config_get_string(config, key, "client_cert_key", NULL);
+ ret = linphone_auth_info_new(username, userid, passwd, ha1, realm, domain);
+ linphone_auth_info_set_tls_cert_path(ret, tls_cert_path);
+ linphone_auth_info_set_tls_key_path(ret, tls_key_path);
return ret;
}
@@ -218,8 +293,8 @@ static bool_t realm_match(const char *realm1, const char *realm2){
return FALSE;
}
-static const LinphoneAuthInfo *find_auth_info(LinphoneCore *lc, const char *username, const char *realm, const char *domain){
- MSList *elem;
+static const LinphoneAuthInfo *find_auth_info(LinphoneCore *lc, const char *username, const char *realm, const char *domain, bool_t ignore_realm){
+ bctbx_list_t *elem;
const LinphoneAuthInfo *ret=NULL;
for (elem=lc->auth_info;elem!=NULL;elem=elem->next) {
@@ -238,9 +313,9 @@ static const LinphoneAuthInfo *find_auth_info(LinphoneCore *lc, const char *user
}
ret=pinfo;
}
- } else if (domain && pinfo->domain && strcmp(domain,pinfo->domain)==0 && pinfo->ha1==NULL) {
+ } else if (domain && pinfo->domain && strcmp(domain,pinfo->domain)==0 && (pinfo->ha1==NULL || ignore_realm)) {
return pinfo;
- } else if (!domain && pinfo->ha1==NULL) {
+ } else if (!domain && (pinfo->ha1==NULL || ignore_realm)) {
return pinfo;
}
}
@@ -248,39 +323,62 @@ static const LinphoneAuthInfo *find_auth_info(LinphoneCore *lc, const char *user
return ret;
}
-/**
- * Find authentication info matching realm, username, domain criteria.
- * First of all, (realm,username) pair are searched. If multiple results (which should not happen because realm are supposed to be unique), then domain is added to the search.
- * @param lc the LinphoneCore
- * @param realm the authentication 'realm' (optional)
- * @param username the SIP username to be authenticated (mandatory)
- * @param domain the SIP domain name (optional)
- * @return a #LinphoneAuthInfo
-**/
-const LinphoneAuthInfo *linphone_core_find_auth_info(LinphoneCore *lc, const char *realm, const char *username, const char *domain){
+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 (pinfo->tls_cert && pinfo->tls_key) {
+ return pinfo;
+ } else if (pinfo->tls_cert_path && pinfo->tls_key_path) {
+ return pinfo;
+ }
+ }
+ return NULL;
+}
+
+const LinphoneAuthInfo *_linphone_core_find_auth_info(LinphoneCore *lc, const char *realm, const char *username, const char *domain, bool_t ignore_realm){
const LinphoneAuthInfo *ai=NULL;
if (realm){
- ai=find_auth_info(lc,username,realm,NULL);
+ ai=find_auth_info(lc,username,realm,NULL, FALSE);
if (ai==NULL && domain){
- ai=find_auth_info(lc,username,realm,domain);
+ ai=find_auth_info(lc,username,realm,domain, FALSE);
}
}
if (ai == NULL && domain != NULL) {
- ai=find_auth_info(lc,username,NULL,domain);
+ ai=find_auth_info(lc,username,NULL,domain, ignore_realm);
}
if (ai==NULL){
- ai=find_auth_info(lc,username,NULL,NULL);
+ ai=find_auth_info(lc,username,NULL,NULL, ignore_realm);
}
- /*if (ai) ms_message("linphone_core_find_auth_info(): returning auth info username=%s, realm=%s", ai->username, ai->realm);*/
+ if (ai) ms_message("linphone_core_find_auth_info(): returning auth info username=%s, realm=%s", ai->username ? ai->username : "", ai->realm ? ai->realm : "");
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, 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){
- MSList *elem;
+ bctbx_list_t *elem;
int i;
if (!linphone_core_ready(lc)) return;
- for(elem=lc->auth_info,i=0;elem!=NULL;elem=ms_list_next(elem),i++){
+ 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);
}
@@ -298,40 +396,46 @@ LinphoneAuthInfo * linphone_core_create_auth_info(LinphoneCore *lc, const char *
**/
void linphone_core_add_auth_info(LinphoneCore *lc, const LinphoneAuthInfo *info){
LinphoneAuthInfo *ai;
- MSList *elem;
- MSList *l;
+ bctbx_list_t *elem;
+ bctbx_list_t *l;
int restarted_op_count=0;
bool_t updating=FALSE;
if (info->ha1==NULL && info->passwd==NULL){
- ms_error("linphone_core_add_auth_info(): info supplied with empty password or ha1.");
- return;
+ ms_warning("linphone_core_add_auth_info(): info supplied with empty password or ha1.");
}
/* find if we are attempting to modify an existing auth info */
ai=(LinphoneAuthInfo*)linphone_core_find_auth_info(lc,info->realm,info->username,info->domain);
if (ai!=NULL && ai->domain && info->domain && strcmp(ai->domain, info->domain)==0){
- lc->auth_info=ms_list_remove(lc->auth_info,ai);
+ lc->auth_info=bctbx_list_remove(lc->auth_info,ai);
linphone_auth_info_destroy(ai);
updating=TRUE;
}
- lc->auth_info=ms_list_append(lc->auth_info,linphone_auth_info_clone(info));
+ lc->auth_info=bctbx_list_append(lc->auth_info,linphone_auth_info_clone(info));
/* retry pending authentication operations */
for(l=elem=sal_get_pending_auths(lc->sal);elem!=NULL;elem=elem->next){
SalOp *op=(SalOp*)elem->data;
LinphoneAuthInfo *ai;
const SalAuthInfo *req_sai=sal_op_get_auth_requested(op);
- ai=(LinphoneAuthInfo*)linphone_core_find_auth_info(lc,req_sai->realm,req_sai->username,req_sai->domain);
+ ai=(LinphoneAuthInfo*)_linphone_core_find_auth_info(lc,req_sai->realm,req_sai->username,req_sai->domain, FALSE);
if (ai){
SalAuthInfo sai;
- MSList* proxy;
+ bctbx_list_t* proxy;
sai.username=ai->username;
sai.userid=ai->userid;
sai.realm=ai->realm;
sai.password=ai->passwd;
sai.ha1=ai->ha1;
+ if (ai->tls_cert && ai->tls_key) {
+ sal_certificates_chain_parse(&sai, ai->tls_cert, SAL_CERTIFICATE_RAW_FORMAT_PEM);
+ sal_signing_key_parse(&sai, ai->tls_key, "");
+ } else if (ai->tls_cert_path && ai->tls_key_path) {
+ sal_certificates_chain_parse_file(&sai, ai->tls_cert_path, SAL_CERTIFICATE_RAW_FORMAT_PEM);
+ sal_signing_key_parse_file(&sai, ai->tls_key_path, "");
+ }
/*proxy case*/
- for (proxy=(MSList*)linphone_core_get_proxy_config_list(lc);proxy!=NULL;proxy=proxy->next) {
+ for (proxy=(bctbx_list_t*)linphone_core_get_proxy_config_list(lc);proxy!=NULL;proxy=proxy->next) {
if (proxy->data == sal_op_get_user_pointer(op)) {
linphone_proxy_config_set_state((LinphoneProxyConfig*)(proxy->data),LinphoneRegistrationProgress,"Authentication...");
break;
@@ -352,8 +456,8 @@ void linphone_core_add_auth_info(LinphoneCore *lc, const LinphoneAuthInfo *info)
info->realm ? info->realm : "",
info->domain ? info->domain : "");
}
- ms_list_free(l);
- if(lc->sip_conf.save_auth_info) write_auth_infos(lc);
+ bctbx_list_free(l);
+ write_auth_infos(lc);
}
@@ -371,9 +475,9 @@ void linphone_core_remove_auth_info(LinphoneCore *lc, const LinphoneAuthInfo *in
LinphoneAuthInfo *r;
r=(LinphoneAuthInfo*)linphone_core_find_auth_info(lc,info->realm,info->username,info->domain);
if (r){
- lc->auth_info=ms_list_remove(lc->auth_info,r);
+ lc->auth_info=bctbx_list_remove(lc->auth_info,r);
linphone_auth_info_destroy(r);
- if(lc->sip_conf.save_auth_info) write_auth_infos(lc);
+ write_auth_infos(lc);
}
}
@@ -382,7 +486,7 @@ void linphone_core_remove_auth_info(LinphoneCore *lc, const LinphoneAuthInfo *in
* @param[in] lc The LinphoneCore object
* @return \mslist{LinphoneAuthInfo}
**/
-const MSList *linphone_core_get_auth_info_list(const LinphoneCore *lc){
+const bctbx_list_t *linphone_core_get_auth_info_list(const LinphoneCore *lc){
return lc->auth_info;
}
@@ -390,14 +494,14 @@ const MSList *linphone_core_get_auth_info_list(const LinphoneCore *lc){
* Clear all authentication information.
**/
void linphone_core_clear_all_auth_info(LinphoneCore *lc){
- MSList *elem;
+ bctbx_list_t *elem;
int i;
- for(i=0,elem=lc->auth_info;elem!=NULL;elem=ms_list_next(elem),i++){
+ for(i=0,elem=lc->auth_info;elem!=NULL;elem=bctbx_list_next(elem),i++){
LinphoneAuthInfo *info=(LinphoneAuthInfo*)elem->data;
linphone_auth_info_destroy(info);
linphone_auth_info_write_config(lc->config,NULL,i);
}
- ms_list_free(lc->auth_info);
+ bctbx_list_free(lc->auth_info);
lc->auth_info=NULL;
}
diff --git a/coreapi/bellesip_sal/sal_address_impl.c b/coreapi/bellesip_sal/sal_address_impl.c
index 54df93fd4..3ee8343d7 100644
--- a/coreapi/bellesip_sal/sal_address_impl.c
+++ b/coreapi/bellesip_sal/sal_address_impl.c
@@ -114,6 +114,15 @@ const char* sal_address_get_transport_name(const SalAddress* addr){
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);
@@ -188,17 +197,41 @@ void sal_address_set_param(SalAddress *addr,const char* name,const char* 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_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);
}
@@ -213,6 +246,10 @@ 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));
}
diff --git a/coreapi/bellesip_sal/sal_impl.c b/coreapi/bellesip_sal/sal_impl.c
index 8bf38740c..8ccde1378 100644
--- a/coreapi/bellesip_sal/sal_impl.c
+++ b/coreapi/bellesip_sal/sal_impl.c
@@ -62,7 +62,7 @@ void sal_op_set_privacy_from_message(SalOp* op,belle_sip_message_t* msg) {
}
static void set_tls_properties(Sal *ctx);
-void _belle_sip_log(belle_sip_log_level lev, const char *fmt, va_list args) {
+void _belle_sip_log(const char *domain, belle_sip_log_level lev, const char *fmt, va_list args) {
int ortp_level;
switch(lev) {
case BELLE_SIP_LOG_FATAL:
@@ -82,8 +82,8 @@ void _belle_sip_log(belle_sip_log_level lev, const char *fmt, va_list args) {
ortp_level=ORTP_DEBUG;
break;
}
- if (ortp_log_level_enabled(ortp_level)){
- ortp_logv(ortp_level,fmt,args);
+ if (ortp_log_level_enabled("belle-sip", ortp_level)){
+ ortp_logv("belle-sip", ortp_level,fmt,args);
}
}
@@ -115,8 +115,8 @@ void sal_set_log_level(OrtpLogLevel level) {
}
void sal_add_pending_auth(Sal *sal, SalOp *op){
- if (ms_list_find(sal->pending_auths,op)==NULL){
- sal->pending_auths=ms_list_append(sal->pending_auths,op);
+ if (bctbx_list_find(sal->pending_auths,op)==NULL){
+ sal->pending_auths=bctbx_list_append(sal->pending_auths,op);
op->has_auth_pending=TRUE;
}
}
@@ -124,8 +124,8 @@ void sal_add_pending_auth(Sal *sal, SalOp *op){
void sal_remove_pending_auth(Sal *sal, SalOp *op){
if (op->has_auth_pending){
op->has_auth_pending=FALSE;
- if (ms_list_find(sal->pending_auths,op)){
- sal->pending_auths=ms_list_remove(sal->pending_auths,op);
+ if (bctbx_list_find(sal->pending_auths,op)){
+ sal->pending_auths=bctbx_list_remove(sal->pending_auths,op);
}
}
}
@@ -226,6 +226,7 @@ static void process_request_event(void *ud, const belle_sip_request_event_t *eve
belle_sip_header_address_t* address=NULL;
belle_sip_header_from_t* from_header;
belle_sip_header_to_t* to;
+ belle_sip_header_diversion_t* diversion;
belle_sip_response_t* resp;
belle_sip_header_t *evh;
const char *method=belle_sip_request_get_method(req);
@@ -235,53 +236,75 @@ static void process_request_event(void *ud, const belle_sip_request_event_t *eve
if (dialog) {
op=(SalOp*)belle_sip_dialog_get_application_data(dialog);
+
+ if (op == NULL && strcmp("NOTIFY",method) == 0) {
+ /*special case for Dialog created by notify mathing subscribe*/
+ belle_sip_transaction_t * sub_trans = belle_sip_dialog_get_last_transaction(dialog);
+ op = (SalOp*)belle_sip_transaction_get_application_data(sub_trans);
+ }
if (op==NULL || op->state==SalOpStateTerminated){
ms_warning("Receiving request for null or terminated op [%p], ignored",op);
return;
}
- }else if (strcmp("INVITE",method)==0) {
- op=sal_op_new(sal);
- op->dir=SalOpDirIncoming;
- sal_op_call_fill_cbs(op);
- }else if ((strcmp("SUBSCRIBE",method)==0 || strcmp("NOTIFY",method)==0) && (evh=belle_sip_message_get_header(BELLE_SIP_MESSAGE(req),"Event"))!=NULL) {
- op=sal_op_new(sal);
- op->dir=SalOpDirIncoming;
- if (strncmp(belle_sip_header_get_unparsed_value(evh),"presence",strlen("presence"))==0){
- sal_op_presence_fill_cbs(op);
- }else
- sal_op_subscribe_fill_cbs(op);
- }else if (strcmp("MESSAGE",method)==0) {
- op=sal_op_new(sal);
- op->dir=SalOpDirIncoming;
- sal_op_message_fill_cbs(op);
- }else if (strcmp("OPTIONS",method)==0) {
- resp=belle_sip_response_create_from_request(req,200);
- belle_sip_provider_send_response(sal->prov,resp);
- return;
- }else if (strcmp("INFO",method)==0) {
- resp=belle_sip_response_create_from_request(req,481);/*INFO out of call dialogs are not allowed*/
- belle_sip_provider_send_response(sal->prov,resp);
- return;
- }else if (strcmp("BYE",method)==0) {
- resp=belle_sip_response_create_from_request(req,481);/*out of dialog BYE */
- belle_sip_provider_send_response(sal->prov,resp);
- return;
- }else if (strcmp("CANCEL",method)==0) {
- resp=belle_sip_response_create_from_request(req,481);/*out of dialog CANCEL */
- belle_sip_provider_send_response(sal->prov,resp);
- return;
- }else if (sal->enable_test_features && strcmp("PUBLISH",method)==0) {
- resp=belle_sip_response_create_from_request(req,200);/*out of dialog BYE */
- belle_sip_message_add_header((belle_sip_message_t*)resp,belle_sip_header_create("SIP-Etag","4441929FFFZQOA"));
- belle_sip_provider_send_response(sal->prov,resp);
- return;
- }else {
- ms_error("sal process_request_event not implemented yet for method [%s]",belle_sip_request_get_method(req));
- resp=belle_sip_response_create_from_request(req,405);
- belle_sip_message_add_header(BELLE_SIP_MESSAGE(resp)
- ,BELLE_SIP_HEADER(belle_sip_header_allow_create("INVITE, CANCEL, ACK, BYE, SUBSCRIBE, NOTIFY, MESSAGE, OPTIONS, INFO")));
- belle_sip_provider_send_response(sal->prov,resp);
- return;
+ }else{
+ /*handle the case where we are receiving a request with to tag but it is not belonging to any dialog*/
+ belle_sip_header_to_t *to = belle_sip_message_get_header_by_type(req, belle_sip_header_to_t);
+ if ((strcmp("INVITE",method)==0 || strcmp("NOTIFY",method)==0) && (belle_sip_header_to_get_tag(to) != NULL)) {
+ ms_warning("Receiving %s with to-tag but no know dialog here. Rejecting.", method);
+ resp=belle_sip_response_create_from_request(req,481);
+ belle_sip_provider_send_response(sal->prov,resp);
+ return;
+ /* by default (eg. when a to-tag is present), out of dialog ACK are automatically
+ handled in lower layers (belle-sip) but in case it misses, it will be forwarded to us */
+ } else if (strcmp("ACK",method)==0 && (belle_sip_header_to_get_tag(to) == NULL)) {
+ ms_warning("Receiving ACK without to-tag but no know dialog here. Ignoring");
+ return;
+ }
+
+ if (strcmp("INVITE",method)==0) {
+ op=sal_op_new(sal);
+ op->dir=SalOpDirIncoming;
+ sal_op_call_fill_cbs(op);
+ }else if ((strcmp("SUBSCRIBE",method)==0 || strcmp("NOTIFY",method)==0) && (evh=belle_sip_message_get_header(BELLE_SIP_MESSAGE(req),"Event"))!=NULL) {
+ op=sal_op_new(sal);
+ op->dir=SalOpDirIncoming;
+ if (strncmp(belle_sip_header_get_unparsed_value(evh),"presence",strlen("presence"))==0){
+ sal_op_presence_fill_cbs(op);
+ }else
+ sal_op_subscribe_fill_cbs(op);
+ }else if (strcmp("MESSAGE",method)==0) {
+ op=sal_op_new(sal);
+ op->dir=SalOpDirIncoming;
+ sal_op_message_fill_cbs(op);
+ }else if (strcmp("OPTIONS",method)==0) {
+ resp=belle_sip_response_create_from_request(req,200);
+ belle_sip_provider_send_response(sal->prov,resp);
+ return;
+ }else if (strcmp("INFO",method)==0) {
+ resp=belle_sip_response_create_from_request(req,481);/*INFO out of call dialogs are not allowed*/
+ belle_sip_provider_send_response(sal->prov,resp);
+ return;
+ }else if (strcmp("BYE",method)==0) {
+ resp=belle_sip_response_create_from_request(req,481);/*out of dialog BYE */
+ belle_sip_provider_send_response(sal->prov,resp);
+ return;
+ }else if (strcmp("CANCEL",method)==0) {
+ resp=belle_sip_response_create_from_request(req,481);/*out of dialog CANCEL */
+ belle_sip_provider_send_response(sal->prov,resp);
+ return;
+ }else if (sal->enable_test_features && strcmp("PUBLISH",method)==0) {
+ resp=belle_sip_response_create_from_request(req,200);/*out of dialog PUBLISH */
+ belle_sip_message_add_header((belle_sip_message_t*)resp,belle_sip_header_create("SIP-Etag","4441929FFFZQOA"));
+ belle_sip_provider_send_response(sal->prov,resp);
+ return;
+ }else {
+ ms_error("sal process_request_event not implemented yet for method [%s]",belle_sip_request_get_method(req));
+ resp=belle_sip_response_create_from_request(req,405);
+ belle_sip_message_add_header(BELLE_SIP_MESSAGE(resp)
+ ,BELLE_SIP_HEADER(belle_sip_header_allow_create("INVITE, CANCEL, ACK, BYE, SUBSCRIBE, NOTIFY, MESSAGE, OPTIONS, INFO")));
+ belle_sip_provider_send_response(sal->prov,resp);
+ return;
+ }
}
if (!op->base.from_address) {
@@ -316,6 +339,24 @@ static void process_request_event(void *ud, const belle_sip_request_event_t *eve
belle_sip_object_unref(address);
}
+ if(!op->base.diversion_address){
+ diversion=belle_sip_message_get_header_by_type(BELLE_SIP_MESSAGE(req),belle_sip_header_diversion_t);
+ if (diversion) {
+ if (belle_sip_header_address_get_uri(BELLE_SIP_HEADER_ADDRESS(diversion)))
+ address=belle_sip_header_address_create(belle_sip_header_address_get_displayname(BELLE_SIP_HEADER_ADDRESS(diversion))
+ ,belle_sip_header_address_get_uri(BELLE_SIP_HEADER_ADDRESS(diversion)));
+ else if ((belle_sip_header_address_get_absolute_uri(BELLE_SIP_HEADER_ADDRESS(diversion))))
+ address=belle_sip_header_address_create2(belle_sip_header_address_get_displayname(BELLE_SIP_HEADER_ADDRESS(diversion))
+ ,belle_sip_header_address_get_absolute_uri(BELLE_SIP_HEADER_ADDRESS(diversion)));
+ else
+ ms_warning("Cannot not find diversion header from request [%p]",req);
+ if (address) {
+ sal_op_set_diversion_address(op,(SalAddress*)address);
+ belle_sip_object_unref(address);
+ }
+ }
+ }
+
if (!op->base.origin) {
/*set origin uri*/
origin_address=belle_sip_header_address_create(NULL,belle_sip_request_extract_origin(req));
@@ -456,13 +497,13 @@ static void process_auth_requested(void *sal, belle_sip_auth_event_t *event) {
sal_auth_info_delete(auth_info);
}
-Sal * sal_init(){
+Sal * sal_init(MSFactory *factory){
belle_sip_listener_callbacks_t listener_callbacks;
Sal * sal=ms_new0(Sal,1);
/*belle_sip_object_enable_marshal_check(TRUE);*/
sal->auto_contacts=TRUE;
-
+ sal->factory = factory;
/*first create the stack, which initializes the belle-sip object's pool for this thread*/
belle_sip_set_log_handler(_belle_sip_log);
sal->stack = belle_sip_stack_new(NULL);
@@ -470,6 +511,8 @@ Sal * sal_init(){
sal->user_agent=belle_sip_header_user_agent_new();
#if defined(PACKAGE_NAME) && defined(LIBLINPHONE_VERSION)
belle_sip_header_user_agent_add_product(sal->user_agent, PACKAGE_NAME "/" LIBLINPHONE_VERSION);
+#else
+ belle_sip_header_user_agent_add_product(sal->user_agent, "Unknown");
#endif
sal_append_stack_string_to_user_agent(sal);
belle_sip_object_ref(sal->user_agent);
@@ -491,6 +534,7 @@ Sal * sal_init(){
sal->refresher_retry_after=60000; /*default value in ms*/
sal->enable_sip_update=TRUE;
sal->pending_trans_checking=TRUE;
+ sal->ssl_config = NULL;
return sal;
}
@@ -502,7 +546,7 @@ void *sal_get_user_pointer(const Sal *sal){
return sal->up;
}
-static void unimplemented_stub(){
+static void unimplemented_stub(void){
ms_warning("Unimplemented SAL callback");
}
@@ -534,8 +578,8 @@ void sal_set_callbacks(Sal *ctx, const SalCallbacks *cbs){
ctx->callbacks.notify=(SalOnNotify)unimplemented_stub;
if (ctx->callbacks.subscribe_received==NULL)
ctx->callbacks.subscribe_received=(SalOnSubscribeReceived)unimplemented_stub;
- if (ctx->callbacks.subscribe_closed==NULL)
- ctx->callbacks.subscribe_closed=(SalOnSubscribeClosed)unimplemented_stub;
+ if (ctx->callbacks.incoming_subscribe_closed==NULL)
+ ctx->callbacks.incoming_subscribe_closed=(SalOnIncomingSubscribeClosed)unimplemented_stub;
if (ctx->callbacks.parse_presence_requested==NULL)
ctx->callbacks.parse_presence_requested=(SalOnParsePresenceRequested)unimplemented_stub;
if (ctx->callbacks.convert_presence_to_xml_requested==NULL)
@@ -568,9 +612,10 @@ void sal_uninit(Sal* sal){
belle_sip_object_unref(sal->stack);
belle_sip_object_unref(sal->listener);
if (sal->supported) belle_sip_object_unref(sal->supported);
- ms_list_free_with_data(sal->supported_tags,ms_free);
+ bctbx_list_free_with_data(sal->supported_tags,ms_free);
if (sal->uuid) ms_free(sal->uuid);
if (sal->root_ca) ms_free(sal->root_ca);
+ if (sal->root_ca_data) ms_free(sal->root_ca_data);
ms_free(sal);
};
@@ -587,6 +632,10 @@ int sal_transport_available(Sal *sal, SalTransport t){
return FALSE;
}
+bool_t sal_content_encoding_available(Sal *sal, const char *content_encoding) {
+ return (bool_t)belle_sip_stack_content_encoding_available(sal->stack, content_encoding);
+}
+
static int sal_add_listen_port(Sal *ctx, SalAddress* addr, bool_t is_tunneled){
int result;
belle_sip_listening_point_t* lp;
@@ -614,7 +663,9 @@ static int sal_add_listen_port(Sal *ctx, SalAddress* addr, bool_t is_tunneled){
if (lp) {
belle_sip_listening_point_set_keep_alive(lp,ctx->keep_alive);
result = belle_sip_provider_add_listening_point(ctx->prov,lp);
- if (sal_address_get_transport(addr)==SalTransportTLS) set_tls_properties(ctx);
+ if (sal_address_get_transport(addr)==SalTransportTLS) {
+ set_tls_properties(ctx);
+ }
} else {
return -1;
}
@@ -724,25 +775,39 @@ static void set_tls_properties(Sal *ctx){
belle_sip_listening_point_t *lp=belle_sip_provider_get_listening_point(ctx->prov,"TLS");
if (lp){
belle_sip_tls_listening_point_t *tlp=BELLE_SIP_TLS_LISTENING_POINT(lp);
- int verify_exceptions=0;
-
- if (!ctx->tls_verify) verify_exceptions=BELLE_SIP_TLS_LISTENING_POINT_BADCERT_ANY_REASON;
- else if (!ctx->tls_verify_cn) verify_exceptions=BELLE_SIP_TLS_LISTENING_POINT_BADCERT_CN_MISMATCH;
-
- belle_sip_tls_listening_point_set_root_ca(tlp,ctx->root_ca); /*root_ca might be NULL */
- belle_sip_tls_listening_point_set_verify_exceptions(tlp,verify_exceptions);
+ belle_tls_crypto_config_t *crypto_config = belle_tls_crypto_config_new();
+ int verify_exceptions = BELLE_TLS_VERIFY_NONE;
+ if (!ctx->tls_verify) verify_exceptions = BELLE_TLS_VERIFY_ANY_REASON;
+ else if (!ctx->tls_verify_cn) verify_exceptions = BELLE_TLS_VERIFY_CN_MISMATCH;
+ belle_tls_crypto_config_set_verify_exceptions(crypto_config, verify_exceptions);
+ if (ctx->root_ca != NULL) belle_tls_crypto_config_set_root_ca(crypto_config, ctx->root_ca);
+ if (ctx->root_ca_data != NULL) belle_tls_crypto_config_set_root_ca_data(crypto_config, ctx->root_ca_data);
+ if (ctx->ssl_config != NULL) belle_tls_crypto_config_set_ssl_config(crypto_config, ctx->ssl_config);
+ belle_sip_tls_listening_point_set_crypto_config(tlp, crypto_config);
+ belle_sip_object_unref(crypto_config);
}
}
-void sal_set_root_ca(Sal* ctx, const char* rootCa){
- if (ctx->root_ca){
+void sal_set_root_ca(Sal* ctx, const char* rootCa) {
+ if (ctx->root_ca) {
ms_free(ctx->root_ca);
- ctx->root_ca=NULL;
+ ctx->root_ca = NULL;
}
if (rootCa)
- ctx->root_ca=ms_strdup(rootCa);
+ ctx->root_ca = ms_strdup(rootCa);
set_tls_properties(ctx);
- return ;
+ return;
+}
+
+void sal_set_root_ca_data(Sal* ctx, const char* data) {
+ if (ctx->root_ca_data) {
+ ms_free(ctx->root_ca_data);
+ ctx->root_ca_data = NULL;
+ }
+ if (data)
+ ctx->root_ca_data = ms_strdup(data);
+ set_tls_properties(ctx);
+ return;
}
void sal_verify_server_certificates(Sal *ctx, bool_t verify){
@@ -757,6 +822,12 @@ void sal_verify_server_cn(Sal *ctx, bool_t verify){
return ;
}
+void sal_set_ssl_config(Sal *ctx, void *ssl_config) {
+ ctx->ssl_config = ssl_config;
+ set_tls_properties(ctx);
+ return ;
+}
+
void sal_use_tcp_tls_keepalive(Sal *ctx, bool_t enabled) {
ctx->use_tcp_tls_keep_alive=enabled;
}
@@ -765,13 +836,10 @@ int sal_iterate(Sal *sal){
belle_sip_stack_sleep(sal->stack,0);
return 0;
}
-MSList * sal_get_pending_auths(Sal *sal){
- return ms_list_copy(sal->pending_auths);
+bctbx_list_t * sal_get_pending_auths(Sal *sal){
+ return bctbx_list_copy(sal->pending_auths);
}
-#define payload_type_set_number(pt,n) (pt)->user_data=(void*)((long)n);
-#define payload_type_get_number(pt) ((int)(long)(pt)->user_data)
-
/*misc*/
void sal_get_default_local_ip(Sal *sal, int address_family, char *ip, size_t iplen){
strncpy(ip,address_family==AF_INET6 ? "::1" : "127.0.0.1",iplen);
@@ -809,6 +877,7 @@ bool_t sal_nat_helper_enabled(Sal *sal) {
void sal_set_dns_timeout(Sal* sal,int timeout) {
belle_sip_stack_set_dns_timeout(sal->stack, timeout);
}
+
int sal_get_dns_timeout(const Sal* sal) {
return belle_sip_stack_get_dns_timeout(sal->stack);
}
@@ -816,16 +885,38 @@ int sal_get_dns_timeout(const Sal* sal) {
void sal_set_transport_timeout(Sal* sal,int timeout) {
belle_sip_stack_set_transport_timeout(sal->stack, timeout);
}
+
int sal_get_transport_timeout(const Sal* sal) {
return belle_sip_stack_get_transport_timeout(sal->stack);
}
+
+void sal_set_dns_servers(Sal *sal, const bctbx_list_t *servers){
+ belle_sip_list_t *l = NULL;
+
+ /*we have to convert the bctbx_list_t into a belle_sip_list_t first*/
+ for (; servers != NULL; servers = servers->next){
+ l = belle_sip_list_append(l, servers->data);
+ }
+ belle_sip_stack_set_dns_servers(sal->stack, l);
+ belle_sip_list_free(l);
+}
+
void sal_enable_dns_srv(Sal *sal, bool_t enable) {
belle_sip_stack_enable_dns_srv(sal->stack, (unsigned char)enable);
}
+
bool_t sal_dns_srv_enabled(const Sal *sal) {
return (bool_t)belle_sip_stack_dns_srv_enabled(sal->stack);
}
+void sal_enable_dns_search(Sal *sal, bool_t enable) {
+ belle_sip_stack_enable_dns_search(sal->stack, (unsigned char)enable);
+}
+
+bool_t sal_dns_search_enabled(const Sal *sal) {
+ return (bool_t)belle_sip_stack_dns_search_enabled(sal->stack);
+}
+
void sal_set_dns_user_hosts_file(Sal *sal, const char *hosts_file) {
belle_sip_stack_set_dns_user_hosts_file(sal->stack, hosts_file);
}
@@ -917,6 +1008,40 @@ const SalCustomHeader *sal_op_get_recv_custom_header(SalOp *op){
return b->recv_custom_headers;
}
+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);
+}
+
void sal_set_uuid(Sal *sal, const char *uuid){
if (sal->uuid){
ms_free(sal->uuid);
@@ -935,8 +1060,7 @@ typedef struct {
unsigned char node[6];
} sal_uuid_t;
-
-int sal_create_uuid(Sal*ctx, char *uuid, size_t len){
+int sal_generate_uuid(char *uuid, size_t len) {
sal_uuid_t uuid_struct;
int i;
int written;
@@ -952,19 +1076,26 @@ int sal_create_uuid(Sal*ctx, char *uuid, size_t len){
written=snprintf(uuid,len,"%8.8x-%4.4x-%4.4x-%2.2x%2.2x-", uuid_struct.time_low, uuid_struct.time_mid,
uuid_struct.time_hi_and_version, uuid_struct.clock_seq_hi_and_reserved,
uuid_struct.clock_seq_low);
- if (written>len+13){
+ if ((written < 0) || ((size_t)written > (len +13))) {
ms_error("sal_create_uuid(): buffer is too short !");
return -1;
}
for (i = 0; i < 6; i++)
written+=snprintf(uuid+written,len-written,"%2.2x", uuid_struct.node[i]);
uuid[len-1]='\0';
- sal_set_uuid(ctx,uuid);
return 0;
}
+int sal_create_uuid(Sal*ctx, char *uuid, size_t len) {
+ if (sal_generate_uuid(uuid, len) == 0) {
+ sal_set_uuid(ctx, uuid);
+ return 0;
+ }
+ return -1;
+}
+
static void make_supported_header(Sal *sal){
- MSList *it;
+ bctbx_list_t *it;
char *alltags=NULL;
size_t buflen=64;
size_t written=0;
@@ -977,7 +1108,7 @@ static void make_supported_header(Sal *sal){
const char *tag=(const char*)it->data;
size_t taglen=strlen(tag);
if (alltags==NULL || (written+taglen+1>=buflen)) alltags=ms_realloc(alltags,(buflen=buflen*2));
- snprintf(alltags+written,buflen-written,it->next ? "%s, " : "%s",tag);
+ written+=snprintf(alltags+written,buflen-written,it->next ? "%s, " : "%s",tag);
}
if (alltags){
sal->supported=belle_sip_header_create("Supported",alltags);
@@ -989,7 +1120,7 @@ static void make_supported_header(Sal *sal){
}
void sal_set_supported_tags(Sal *ctx, const char* tags){
- ctx->supported_tags=ms_list_free_with_data(ctx->supported_tags,ms_free);
+ ctx->supported_tags=bctbx_list_free_with_data(ctx->supported_tags,ms_free);
if (tags){
char *iter;
char *buffer=ms_strdup(tags);
@@ -998,7 +1129,7 @@ void sal_set_supported_tags(Sal *ctx, const char* tags){
iter=buffer;
while((tag=strtok_r(iter,", ",&context))!=NULL){
iter=NULL;
- ctx->supported_tags=ms_list_append(ctx->supported_tags,ms_strdup(tag));
+ ctx->supported_tags=bctbx_list_append(ctx->supported_tags,ms_strdup(tag));
}
ms_free(buffer);
}
@@ -1013,19 +1144,19 @@ const char *sal_get_supported_tags(Sal *ctx){
}
void sal_add_supported_tag(Sal *ctx, const char* tag){
- MSList *elem=ms_list_find_custom(ctx->supported_tags,(MSCompareFunc)strcasecmp,tag);
+ bctbx_list_t *elem=bctbx_list_find_custom(ctx->supported_tags,(bctbx_compare_func)strcasecmp,tag);
if (!elem){
- ctx->supported_tags=ms_list_append(ctx->supported_tags,ms_strdup(tag));
+ ctx->supported_tags=bctbx_list_append(ctx->supported_tags,ms_strdup(tag));
make_supported_header(ctx);
}
}
void sal_remove_supported_tag(Sal *ctx, const char* tag){
- MSList *elem=ms_list_find_custom(ctx->supported_tags,(MSCompareFunc)strcasecmp,tag);
+ bctbx_list_t *elem=bctbx_list_find_custom(ctx->supported_tags,(bctbx_compare_func)strcasecmp,tag);
if (elem){
ms_free(elem->data);
- ctx->supported_tags=ms_list_remove_link(ctx->supported_tags,elem);
+ ctx->supported_tags=bctbx_list_erase_link(ctx->supported_tags,elem);
make_supported_header(ctx);
}
}
@@ -1063,11 +1194,14 @@ SalResolverContext * sal_resolve_a(Sal* sal, const char *name, int port, int fam
return (SalResolverContext*)belle_sip_stack_resolve_a(sal->stack,name,port,family,(belle_sip_resolver_callback_t)cb,data);
}
-/*
-void sal_resolve_cancel(Sal *sal, SalResolverContext* ctx){
- belle_sip_stack_resolve_cancel(sal->stack,ctx);
+SalResolverContext * sal_resolve(Sal *sal, const char *service, const char *transport, const char *name, int port, int family, SalResolverCallback cb, void *data) {
+ return (SalResolverContext *)belle_sip_stack_resolve(sal->stack, service, transport, name, port, family, (belle_sip_resolver_callback_t)cb, data);
}
-*/
+
+void sal_resolve_cancel(SalResolverContext* ctx){
+ belle_sip_resolver_context_cancel((belle_sip_resolver_context_t*)ctx);
+}
+
void sal_enable_unconditional_answer(Sal *sal,int value) {
belle_sip_provider_enable_unconditional_answer(sal->prov,value);
@@ -1079,7 +1213,7 @@ void sal_enable_unconditional_answer(Sal *sal,int value) {
* @param format either PEM or DER
*/
void sal_certificates_chain_parse_file(SalAuthInfo* auth_info, const char* path, SalCertificateRawFormat format) {
- auth_info->certificates = (SalCertificatesChain*) belle_sip_certificates_chain_parse_file(path, (belle_sip_certificate_raw_format_t)format); //
+ auth_info->certificates = (SalCertificatesChain*) 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);
}
@@ -1093,6 +1227,28 @@ void sal_signing_key_parse_file(SalAuthInfo* auth_info, const char* path, const
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 = (SalCertificatesChain*) 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 = (SalSigningKey *) 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
*
@@ -1182,3 +1338,153 @@ int sal_enable_pending_trans_checking(Sal *sal, bool_t value) {
sal->pending_trans_checking = value;
return 0;
}
+void sal_set_http_proxy_host(Sal *sal, const char *host) {
+ belle_sip_stack_set_http_proxy_host(sal->stack, host);
+}
+
+void sal_set_http_proxy_port(Sal *sal, int port) {
+ belle_sip_stack_set_http_proxy_port(sal->stack, port);
+}
+const char *sal_get_http_proxy_host(const Sal *sal) {
+ return belle_sip_stack_get_http_proxy_host(sal->stack);
+}
+
+int sal_get_http_proxy_port(const Sal *sal) {
+ return belle_sip_stack_get_http_proxy_port(sal->stack);
+}
+
+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 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);
+}
+
+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;
+}
+
+void *sal_get_stack_impl(Sal *sal) {
+ return sal->stack;
+}
+
+
diff --git a/coreapi/bellesip_sal/sal_impl.h b/coreapi/bellesip_sal/sal_impl.h
index 1ca8e058f..174d2b435 100644
--- a/coreapi/bellesip_sal/sal_impl.h
+++ b/coreapi/bellesip_sal/sal_impl.h
@@ -25,6 +25,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#include "belle-sip/belle-sdp.h"
struct Sal{
+ MSFactory *factory;
SalCallbacks callbacks;
MSList *pending_auths;/*MSList of SalOp */
belle_sip_stack_t* stack;
@@ -36,6 +37,7 @@ struct Sal{
int session_expires;
unsigned int keep_alive;
char *root_ca;
+ char *root_ca_data;
char *uuid;
int refresher_retry_after; /*retry after value for refresher*/
MSList *supported_tags;/*list of char * */
@@ -52,6 +54,7 @@ struct Sal{
bool_t enable_sip_update; /*true by default*/
SalOpSDPHandling default_sdp_handling;
bool_t pending_trans_checking; /*testing purpose*/
+ void *ssl_config;
};
typedef enum SalOpState {
@@ -99,7 +102,7 @@ struct SalOp{
int ref;
SalOpType type;
SalPrivacyMask privacy;
- belle_sip_header_t *event; /*used by SalOpSubscribe kinds*/
+ belle_sip_header_event_t *event; /*used by SalOpSubscribe kinds*/
SalOpSDPHandling sdp_handling;
int auth_requests; /*number of auth requested for this op*/
bool_t cnx_ip_to_0000_if_sendonly_enabled;
@@ -109,6 +112,7 @@ struct SalOp{
bool_t manual_refresher;
bool_t has_auth_pending;
bool_t supports_session_timers;
+ bool_t op_released;
};
@@ -126,6 +130,7 @@ SalOp* sal_op_ref(SalOp* op);
void* sal_op_unref(SalOp* op);
void sal_op_release_impl(SalOp *op);
+void sal_op_set_replaces(SalOp* op,belle_sip_header_replaces_t* replaces);
void sal_op_set_remote_ua(SalOp*op,belle_sip_message_t* message);
int sal_op_send_request(SalOp* op, belle_sip_request_t* request);
int sal_op_send_request_with_expires(SalOp* op, belle_sip_request_t* request,int expires);
@@ -165,11 +170,12 @@ belle_sip_response_t *sal_create_response_from_request(Sal *sal, belle_sip_reque
void sal_op_assign_recv_headers(SalOp *op, belle_sip_message_t *incoming);
-void sal_op_add_body(SalOp *op, belle_sip_message_t *req, const SalBody *body);
-bool_t sal_op_get_body(SalOp *op, belle_sip_message_t *msg, SalBody *salbody);
+SalBodyHandler * sal_op_get_body_handler(SalOp *op, belle_sip_message_t *msg);
-SalReason sal_reason_to_sip_code(SalReason r);
+int sal_reason_to_sip_code(SalReason r);
void _sal_op_add_custom_headers(SalOp *op, belle_sip_message_t *msg);
+SalSubscribeStatus belle_sip_message_get_subscription_state(const belle_sip_message_t *msg);
+
#endif /* SAL_IMPL_H_ */
diff --git a/coreapi/bellesip_sal/sal_op_call.c b/coreapi/bellesip_sal/sal_op_call.c
index 85b2a125f..d393dc1af 100644
--- a/coreapi/bellesip_sal/sal_op_call.c
+++ b/coreapi/bellesip_sal/sal_op_call.c
@@ -32,8 +32,9 @@ static void call_set_released(SalOp* op){
}
}
-static void call_set_error(SalOp* op,belle_sip_response_t* response){
+static void call_set_error(SalOp* op,belle_sip_response_t* response, bool_t fatal){
sal_op_set_error_info_from_response(op,response);
+ if (fatal) op->state = SalOpStateTerminating;
op->base.root->callbacks.call_failure(op);
}
static void set_addr_to_0000(char value[]) {
@@ -56,20 +57,21 @@ static void sdp_process(SalOp *h){
h->result=sal_media_description_new();
if (h->sdp_offering){
- offer_answer_initiate_outgoing(h->base.local_media,h->base.remote_media,h->result);
+ offer_answer_initiate_outgoing(h->base.root->factory, h->base.local_media,h->base.remote_media,h->result);
}else{
int i;
if (h->sdp_answer){
belle_sip_object_unref(h->sdp_answer);
}
- offer_answer_initiate_incoming(h->base.local_media,h->base.remote_media,h->result,h->base.root->one_matching_codec);
+ offer_answer_initiate_incoming(h->base.root->factory, h->base.local_media,h->base.remote_media,h->result,h->base.root->one_matching_codec);
/*for backward compatibility purpose*/
if(h->cnx_ip_to_0000_if_sendonly_enabled && sal_media_description_has_dir(h->result,SalStreamSendOnly)) {
set_addr_to_0000(h->result->addr);
- for(i=0;iresult->nb_streams;++i){
- if (h->result->streams[i].dir == SalStreamSendOnly)
- set_addr_to_0000(h->result->streams[i].rtp_addr);
- set_addr_to_0000(h->result->streams[i].rtcp_addr);
+ for(i=0;iresult->streams[i].dir == SalStreamSendOnly) {
+ set_addr_to_0000(h->result->streams[i].rtp_addr);
+ set_addr_to_0000(h->result->streams[i].rtcp_addr);
+ }
}
}
h->sdp_answer=(belle_sdp_session_description_t *)belle_sip_object_ref(media_description_to_sdp(h->result));
@@ -78,7 +80,7 @@ static void sdp_process(SalOp *h){
strcpy(h->result->addr,h->base.remote_media->addr);
h->result->bandwidth=h->base.remote_media->bandwidth;
- for(i=0;iresult->nb_streams;++i){
+ for(i=0;iresult->streams[i].rtp_port!=0){ /*if stream was accepted*/
strcpy(h->result->streams[i].rtp_addr,h->base.remote_media->streams[i].rtp_addr);
@@ -88,7 +90,7 @@ static void sdp_process(SalOp *h){
strcpy(h->result->streams[i].rtcp_addr,h->base.remote_media->streams[i].rtcp_addr);
h->result->streams[i].rtcp_port=h->base.remote_media->streams[i].rtcp_port;
- if ((h->result->streams[i].proto == SalProtoRtpSavpf) || (h->result->streams[i].proto == SalProtoRtpSavp)) {
+ if (sal_stream_description_has_srtp(&h->result->streams[i])) {
h->result->streams[i].crypto[0] = h->base.remote_media->streams[i].crypto[0];
}
}
@@ -149,6 +151,7 @@ static void call_process_io_error(void *user_ctx, const belle_sip_io_error_event
/*call terminated very early*/
sal_error_info_set(&op->error_info,SalReasonIOError,503,"IO error",NULL);
op->base.root->callbacks.call_failure(op);
+ op->state = SalOpStateTerminating;
call_set_released(op);
} else {
/*dialog will terminated shortly, nothing to do*/
@@ -162,6 +165,14 @@ static void process_dialog_terminated(void *ctx, const belle_sip_dialog_terminat
ms_message("Dialog [%p] terminated for op [%p]",belle_sip_dialog_terminated_event_get_dialog(event),op);
switch(belle_sip_dialog_get_previous_state(op->dialog)) {
+ case BELLE_SIP_DIALOG_EARLY:
+ case BELLE_SIP_DIALOG_NULL:
+ if (op->state!=SalOpStateTerminated && op->state!=SalOpStateTerminating) {
+ /*this is an early termination due to incorrect response received*/
+ op->base.root->callbacks.call_failure(op);
+ op->state=SalOpStateTerminating;
+ }
+ break;
case BELLE_SIP_DIALOG_CONFIRMED:
if (op->state!=SalOpStateTerminated && op->state!=SalOpStateTerminating) {
/*this is probably a normal termination from a BYE*/
@@ -197,11 +208,31 @@ static void handle_sdp_from_response(SalOp* op,belle_sip_response_t* response) {
if (op->base.local_media) sdp_process(op);
}
-static void cancelling_invite(SalOp* op ){
+void sal_call_cancel_invite(SalOp* op) {
belle_sip_request_t* cancel;
ms_message("Cancelling INVITE request from [%s] to [%s] ",sal_op_get_from(op), sal_op_get_to(op));
cancel = belle_sip_client_transaction_create_cancel(op->pending_client_trans);
- sal_op_send_request(op,cancel);
+ if (cancel){
+ sal_op_send_request(op,cancel);
+ }else if (op->dialog){
+ belle_sip_dialog_state_t state = belle_sip_dialog_get_state(op->dialog);;
+ /*case where the response received is invalid (could not establish a dialog), but the transaction is not cancellable
+ * because already terminated*/
+ switch(state){
+ case BELLE_SIP_DIALOG_EARLY:
+ case BELLE_SIP_DIALOG_NULL:
+ /*force kill the dialog*/
+ ms_warning("op [%p]: force kill of dialog [%p]", op, op->dialog);
+ belle_sip_dialog_delete(op->dialog);
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+static void cancelling_invite(SalOp *op) {
+ sal_call_cancel_invite(op);
op->state=SalOpStateTerminating;
}
@@ -263,12 +294,12 @@ static void call_process_response(void *op_base, const belle_sip_response_event_
}
belle_sip_object_data_set(BELLE_SIP_OBJECT(dialog),"early_response",belle_sip_object_ref(response),belle_sip_object_unref);
} else if (code>=300){
- call_set_error(op,response);
+ call_set_error(op, response, TRUE);
if (op->dialog==NULL) call_set_released(op);
}
} else if (code >=200
&& code<300
- && strcmp("UPDATE",belle_sip_request_get_method(req))==0) {
+ && strcmp("UPDATE",method)==0) {
handle_sdp_from_response(op,response);
op->base.root->callbacks.call_accepted(op);
}
@@ -282,7 +313,7 @@ static void call_process_response(void *op_base, const belle_sip_response_event_
if (code >=200 && code<300) {
handle_sdp_from_response(op,response);
ack=belle_sip_dialog_create_ack(op->dialog,belle_sip_dialog_get_local_seq_number(op->dialog));
- if (ack==NULL) {
+ if (ack == NULL) {
ms_error("This call has been already terminated.");
return ;
}
@@ -291,18 +322,19 @@ static void call_process_response(void *op_base, const belle_sip_response_event_
belle_sip_object_unref(op->sdp_answer);
op->sdp_answer=NULL;
}
+ belle_sip_message_add_header(BELLE_SIP_MESSAGE(ack),BELLE_SIP_HEADER(op->base.root->user_agent));
belle_sip_dialog_send_ack(op->dialog,ack);
op->base.root->callbacks.call_accepted(op); /*INVITE*/
op->state=SalOpStateActive;
}else if (code >= 300){
- call_set_error(op,response);
+ call_set_error(op,response, FALSE);
}
}else if (strcmp("INFO",method)==0){
if (code == 491
&& (header_content_type = belle_sip_message_get_header_by_type(req,belle_sip_header_content_type_t))
&& strcmp("application",belle_sip_header_content_type_get_type(header_content_type))==0
&& strcmp("media_control+xml",belle_sip_header_content_type_get_subtype(header_content_type))==0) {
- unsigned int retry_in =1000*((float)rand()/RAND_MAX);
+ unsigned int retry_in = (unsigned int)(1000*((float)rand()/RAND_MAX));
belle_sip_source_t *s=sal_create_timer(op->base.root,vfu_retry,sal_op_ref(op), retry_in, "vfu request retry");
ms_message("Rejected vfu request on op [%p], just retry in [%ui] ms",op,retry_in);
belle_sip_object_unref(s);
@@ -311,6 +343,8 @@ static void call_process_response(void *op_base, const belle_sip_response_event_
}
}else if (strcmp("UPDATE",method)==0){
op->base.root->callbacks.call_accepted(op); /*INVITE*/
+ }else if (strcmp("CANCEL",method)==0){
+ op->base.root->callbacks.call_cancel_done(op);
}
break;
case SalOpStateTerminating:
@@ -324,7 +358,7 @@ static void call_process_response(void *op_base, const belle_sip_response_event_
break;
case BELLE_SIP_DIALOG_TERMINATED: {
if (strcmp("INVITE",method)==0 && code >= 300){
- call_set_error(op,response);
+ call_set_error(op,response, TRUE);
}
}
break;
@@ -345,6 +379,7 @@ static void call_process_timeout(void *user_ctx, const belle_sip_timeout_event_t
/*call terminated very early*/
sal_error_info_set(&op->error_info,SalReasonRequestTimeout,408,"Request timeout",NULL);
op->base.root->callbacks.call_failure(op);
+ op->state = SalOpStateTerminating;
call_set_released(op);
} else {
/*dialog will terminated shortly, nothing to do*/
@@ -357,6 +392,7 @@ static void call_process_transaction_terminated(void *user_ctx, const belle_sip_
belle_sip_server_transaction_t *server_transaction=belle_sip_transaction_terminated_event_get_server_transaction(event);
belle_sip_request_t* req;
belle_sip_response_t* resp;
+ int code = 0;
bool_t release_call=FALSE;
if (client_transaction) {
@@ -366,12 +402,20 @@ static void call_process_transaction_terminated(void *user_ctx, const belle_sip_
req=belle_sip_transaction_get_request(BELLE_SIP_TRANSACTION(server_transaction));
resp=belle_sip_transaction_get_response(BELLE_SIP_TRANSACTION(server_transaction));
}
- if (op->state ==SalOpStateTerminating
+ if (resp) code = belle_sip_response_get_status_code(resp);
+
+ if (op->state == SalOpStateTerminating
&& strcmp("BYE",belle_sip_request_get_method(req))==0
- && (!resp || (belle_sip_response_get_status_code(resp) !=401
- && belle_sip_response_get_status_code(resp) !=407))
+ && (!resp || (belle_sip_response_get_status_code(resp) != 401
+ && belle_sip_response_get_status_code(resp) != 407))
&& op->dialog==NULL) {
release_call=TRUE;
+ }else if (op->state == SalOpStateEarly && code < 200){
+ /*call terminated early*/
+ sal_error_info_set(&op->error_info,SalReasonIOError,503,"I/O error",NULL);
+ op->state = SalOpStateTerminating;
+ op->base.root->callbacks.call_failure(op);
+ release_call=TRUE;
}
if (server_transaction){
if (op->pending_server_trans==server_transaction){
@@ -388,6 +432,7 @@ static void call_process_transaction_terminated(void *user_ctx, const belle_sip_
static void call_terminated(SalOp* op,belle_sip_server_transaction_t* server_transaction, belle_sip_request_t* request,int status_code) {
belle_sip_response_t* resp;
+ op->state = SalOpStateTerminating;
op->base.root->callbacks.call_terminated(op,op->dir==SalOpDirIncoming?sal_op_get_from(op):sal_op_get_to(op));
resp=sal_op_create_response_from_request(op,request,status_code);
belle_sip_server_transaction_send_response(server_transaction,resp);
@@ -457,6 +502,7 @@ static int process_sdp_for_invite(SalOp* op,belle_sip_request_t* invite) {
belle_sdp_session_description_t* sdp;
int err=0;
SalReason reason;
+
if (extract_sdp(op,BELLE_SIP_MESSAGE(invite),&sdp,&reason)==0) {
if (sdp){
op->sdp_offering=FALSE;
@@ -497,8 +543,9 @@ static void process_request_event(void *op_base, const belle_sip_request_event_t
belle_sip_header_t* call_info;
const char *method=belle_sip_request_get_method(req);
bool_t is_update=FALSE;
+ bool_t drop_op = FALSE;
- if (strcmp("ACK",method)!=0){ /*ACK does'nt create srv transaction*/
+ if (strcmp("ACK",method)!=0){ /*ACK doesn't create a server transaction*/
server_transaction = belle_sip_provider_create_server_transaction(op->base.root->prov,belle_sip_request_event_get_request(event));
belle_sip_object_ref(server_transaction);
belle_sip_transaction_set_application_data(BELLE_SIP_TRANSACTION(server_transaction),sal_op_ref(op));
@@ -540,12 +587,14 @@ static void process_request_event(void *op_base, const belle_sip_request_event_t
}
}
op->base.root->callbacks.call_received(op);
+ }else{
+ /*the INVITE was declined by process_sdp_for_invite(). As we are not inside an established dialog, we can drop the op immediately*/
+ drop_op = TRUE;
}
break;
- } /* else same behavior as for EARLY state*/
+ } /* else same behavior as for EARLY state, thus NO BREAK*/
}
case BELLE_SIP_DIALOG_EARLY: {
- //hmm probably a cancel
if (strcmp("CANCEL",method)==0) {
if(belle_sip_request_event_get_server_transaction(event)) {
/*first answer 200 ok to cancel*/
@@ -629,15 +678,19 @@ static void process_request_event(void *op_base, const belle_sip_request_event_t
}
}else{
- SalBody salbody;
- if (sal_op_get_body(op,(belle_sip_message_t*)req,&salbody)) {
- if (sal_body_has_type(&salbody,"application","dtmf-relay")){
+ belle_sip_message_t *msg = BELLE_SIP_MESSAGE(req);
+ belle_sip_body_handler_t *body_handler = BELLE_SIP_BODY_HANDLER(sal_op_get_body_handler(op, msg));
+ if (body_handler) {
+ belle_sip_header_content_type_t *content_type = belle_sip_message_get_header_by_type(msg, belle_sip_header_content_type_t);
+ if (content_type
+ && (strcmp(belle_sip_header_content_type_get_type(content_type), "application") == 0)
+ && (strcmp(belle_sip_header_content_type_get_subtype(content_type), "dtmf-relay") == 0)) {
char tmp[10];
- if (sal_lines_get_value(salbody.data, "Signal",tmp, sizeof(tmp))){
+ if (sal_lines_get_value(belle_sip_message_get_body(msg), "Signal",tmp, sizeof(tmp))){
op->base.root->callbacks.dtmf_received(op,tmp[0]);
}
}else
- op->base.root->callbacks.info_received(op,&salbody);
+ op->base.root->callbacks.info_received(op, (SalBodyHandler *)body_handler);
} else {
op->base.root->callbacks.info_received(op,NULL);
}
@@ -667,7 +720,7 @@ static void process_request_event(void *op_base, const belle_sip_request_event_t
}
if (server_transaction) belle_sip_object_unref(server_transaction);
-
+ if (drop_op) sal_op_release(op);
}
@@ -832,9 +885,9 @@ int sal_call_accept(SalOp*h){
return -1;
}
ms_message("Accepting server transaction [%p] on op [%p]", transaction, h);
+
/* sends a 200 OK */
response = sal_op_create_response_from_request(h,belle_sip_transaction_get_request(BELLE_SIP_TRANSACTION(transaction)),200);
-
if (response==NULL){
ms_error("Fail to build answer for call");
return -1;
@@ -860,6 +913,9 @@ int sal_call_accept(SalOp*h){
belle_sip_object_unref(h->pending_update_server_trans);
h->pending_update_server_trans=NULL;
}
+ if (h->state == SalOpStateEarly){
+ h->state = SalOpStateActive;
+ }
return 0;
}
@@ -936,7 +992,7 @@ int sal_call_send_dtmf(SalOp *h, char dtmf){
if (h->dialog && (belle_sip_dialog_get_state(h->dialog) == BELLE_SIP_DIALOG_CONFIRMED || belle_sip_dialog_get_state(h->dialog) == BELLE_SIP_DIALOG_EARLY)){
belle_sip_request_t *req=belle_sip_dialog_create_queued_request(h->dialog,"INFO");
if (req){
- int bodylen;
+ size_t bodylen;
char dtmf_body[128]={0};
snprintf(dtmf_body, sizeof(dtmf_body)-1, "Signal=%c\r\nDuration=250\r\n", dtmf);
@@ -1037,6 +1093,24 @@ int sal_call_is_offerer(const SalOp *h){
return h->sdp_offering;
}
+bool_t sal_call_compare_op(const SalOp *op1, const SalOp *op2) {
+ if (strcmp(op1->base.call_id, op2->base.call_id) == 0) return TRUE;
+ return FALSE;
+}
+bool_t sal_call_dialog_request_pending(const SalOp *op) {
+ return belle_sip_dialog_request_pending(op->dialog) ? TRUE : FALSE;
+}
+const char * sal_call_get_local_tag(SalOp *op) {
+ return belle_sip_dialog_get_local_tag(op->dialog);
+}
+const char * sal_call_get_remote_tag(SalOp *op) {
+ return belle_sip_dialog_get_remote_tag(op->dialog);
+}
+
+void sal_call_set_replaces(SalOp *op, const char *call_id, const char *from_tag, const char *to_tag) {
+ belle_sip_header_replaces_t *replaces = belle_sip_header_replaces_create(call_id, from_tag, to_tag);
+ sal_op_set_replaces(op, replaces);
+}
diff --git a/coreapi/bellesip_sal/sal_op_call_transfer.c b/coreapi/bellesip_sal/sal_op_call_transfer.c
index ad9da3d5d..8adb8564f 100644
--- a/coreapi/bellesip_sal/sal_op_call_transfer.c
+++ b/coreapi/bellesip_sal/sal_op_call_transfer.c
@@ -22,13 +22,6 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
/*call transfer*/
-static void sal_op_set_replaces(SalOp* op,belle_sip_header_replaces_t* replaces) {
- if (op->replaces){
- belle_sip_object_unref(op->replaces);
- }
- op->replaces=replaces;
- belle_sip_object_ref(op->replaces);
-}
static void sal_op_set_referred_by(SalOp* op,belle_sip_header_referred_by_t* referred_by) {
if (op->referred_by){
belle_sip_object_unref(op->referred_by);
@@ -40,7 +33,7 @@ static void sal_op_set_referred_by(SalOp* op,belle_sip_header_referred_by_t* ref
int sal_call_refer_to(SalOp *op, belle_sip_header_refer_to_t* refer_to, belle_sip_header_referred_by_t* referred_by){
char* tmp;
- belle_sip_request_t* req=op->dialog?belle_sip_dialog_create_request(op->dialog,"REFER"):NULL; /*cannot create request if dialog not set yet*/
+ belle_sip_request_t* req=op->dialog?belle_sip_dialog_create_request(op->dialog,"REFER"):sal_op_build_request(op, "REFER");
if (!req) {
tmp=belle_sip_uri_to_string(belle_sip_header_address_get_uri(BELLE_SIP_HEADER_ADDRESS(refer_to)));
ms_error("Cannot refer to [%s] for op [%p]",tmp,op);
@@ -88,14 +81,19 @@ int sal_call_refer_with_replaces(SalOp *op, SalOp *other_call_op){
refer_to=belle_sip_header_refer_to_create(belle_sip_dialog_get_remote_party(other_call_op->dialog));
belle_sip_parameters_clean(BELLE_SIP_PARAMETERS(refer_to));
- if (belle_sip_dialog_is_server(other_call_op->dialog)) {
- to_tag=belle_sip_dialog_get_local_tag(other_call_op->dialog);
- from_tag=belle_sip_dialog_get_remote_tag(other_call_op->dialog);;
-
- } else {
- from_tag=belle_sip_dialog_get_local_tag(other_call_op->dialog);
- to_tag=belle_sip_dialog_get_remote_tag(other_call_op->dialog);;
- }
+ /*rfc3891
+ ...
+ 4. User Agent Client Behavior: Sending a Replaces Header
+
+ A User Agent that wishes to replace a single existing early or
+ confirmed dialog with a new dialog of its own, MAY send the target
+ User Agent an INVITE request containing a Replaces header field. The
+ User Agent Client (UAC) places the Call-ID, to-tag, and from-tag
+ information for the target dialog in a single Replaces header field
+ and sends the new INVITE to the target.*/
+ from_tag=belle_sip_dialog_get_local_tag(other_call_op->dialog);
+ to_tag=belle_sip_dialog_get_remote_tag(other_call_op->dialog);
+
replaces=belle_sip_header_replaces_create(belle_sip_header_call_id_get_call_id(belle_sip_dialog_get_call_id(other_call_op->dialog))
,from_tag,to_tag);
escaped_replaces=belle_sip_header_replaces_value_to_escaped_string(replaces);
@@ -121,11 +119,30 @@ int sal_call_set_referer(SalOp *h, SalOp *refered_call){
/* returns the SalOp of a call that should be replaced by h, if any */
SalOp *sal_call_get_replaces(SalOp *op){
if (op && op->replaces){
+ /*rfc3891
+ 3. User Agent Server Behavior: Receiving a Replaces Header
+
+ The Replaces header contains information used to match an existing
+ SIP dialog (call-id, to-tag, and from-tag). Upon receiving an INVITE
+ with a Replaces header, the User Agent (UA) attempts to match this
+ information with a confirmed or early dialog. The User Agent Server
+ (UAS) matches the to-tag and from-tag parameters as if they were tags
+ present in an incoming request. In other words, the to-tag parameter
+ is compared to the local tag, and the from-tag parameter is compared
+ to the remote tag.
+ */
belle_sip_dialog_t* dialog=belle_sip_provider_find_dialog(op->base.root->prov
,belle_sip_header_replaces_get_call_id(op->replaces)
- ,belle_sip_header_replaces_get_from_tag(op->replaces)
- ,belle_sip_header_replaces_get_to_tag(op->replaces));
+ ,belle_sip_header_replaces_get_to_tag(op->replaces)
+ ,belle_sip_header_replaces_get_from_tag(op->replaces));
+ if (!dialog) {
+ /*for backward compatibility with liblinphone <= 3.10.2-243 */
+ dialog=belle_sip_provider_find_dialog(op->base.root->prov
+ ,belle_sip_header_replaces_get_call_id(op->replaces)
+ ,belle_sip_header_replaces_get_from_tag(op->replaces)
+ ,belle_sip_header_replaces_get_to_tag(op->replaces));
+ }
if (dialog) {
return (SalOp*)belle_sip_dialog_get_application_data(dialog);
}
diff --git a/coreapi/bellesip_sal/sal_op_events.c b/coreapi/bellesip_sal/sal_op_events.c
index e6b456042..1d03c09a5 100644
--- a/coreapi/bellesip_sal/sal_op_events.c
+++ b/coreapi/bellesip_sal/sal_op_events.c
@@ -18,7 +18,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "sal_impl.h"
-SalSubscribeStatus get_subscription_state(belle_sip_message_t *msg){
+SalSubscribeStatus belle_sip_message_get_subscription_state(const belle_sip_message_t *msg){
belle_sip_header_subscription_state_t* subscription_state_header=belle_sip_message_get_header_by_type(msg,belle_sip_header_subscription_state_t);
SalSubscribeStatus sss=SalSubscribeNone;
if (subscription_state_header){
@@ -35,7 +35,7 @@ SalSubscribeStatus get_subscription_state(belle_sip_message_t *msg){
static void subscribe_refresher_listener (belle_sip_refresher_t* refresher
,void* user_pointer
,unsigned int status_code
- ,const char* reason_phrase) {
+ ,const char* reason_phrase, int will_retry) {
SalOp* op = (SalOp*)user_pointer;
belle_sip_transaction_t *tr=BELLE_SIP_TRANSACTION(belle_sip_refresher_get_transaction(refresher));
/*belle_sip_response_t* response=belle_sip_transaction_get_response(tr);*/
@@ -46,10 +46,14 @@ static void subscribe_refresher_listener (belle_sip_refresher_t* refresher
if (status_code==200) sss=SalSubscribeActive;
else if (status_code==202) sss=SalSubscribePending;
set_or_update_dialog(op,belle_sip_transaction_get_dialog(tr));
- }
- if (status_code>=200){
- sal_error_info_set(&op->error_info,SalReasonUnknown,status_code,reason_phrase,NULL);
- op->base.root->callbacks.subscribe_response(op,sss);
+ op->base.root->callbacks.subscribe_response(op,sss, will_retry);
+ } else if (status_code >= 300) {
+ SalReason reason = SalReasonUnknown;
+ if (status_code == 503) { /*refresher returns 503 for IO error*/
+ reason = SalReasonIOError;
+ }
+ sal_error_info_set(&op->error_info,reason,status_code,reason_phrase,NULL);
+ op->base.root->callbacks.subscribe_response(op,sss, will_retry);
}else if (status_code==0){
op->base.root->callbacks.on_expire(op);
}
@@ -57,27 +61,86 @@ static void subscribe_refresher_listener (belle_sip_refresher_t* refresher
}
static void subscribe_process_io_error(void *user_ctx, const belle_sip_io_error_event_t *event){
- ms_error("subscribe_process_io_error not implemented yet");
+ SalOp *op = (SalOp*)user_ctx;
+ belle_sip_object_t *src = belle_sip_io_error_event_get_source(event);
+ if (BELLE_SIP_OBJECT_IS_INSTANCE_OF(src, belle_sip_client_transaction_t)){
+ belle_sip_client_transaction_t *tr = BELLE_SIP_CLIENT_TRANSACTION(src);
+ belle_sip_request_t* req = belle_sip_transaction_get_request((belle_sip_transaction_t*)tr);
+ const char *method=belle_sip_request_get_method(req);
+
+ if (!op->dialog) {
+ /*this is handling outgoing out-of-dialog notifies*/
+ if (strcmp(method,"NOTIFY")==0){
+ SalErrorInfo *ei=&op->error_info;
+ sal_error_info_set(ei,SalReasonIOError,0,NULL,NULL);
+ op->base.root->callbacks.on_notify_response(op);
+ }
+ }
+ }
}
static void subscribe_process_dialog_terminated(void *ctx, const belle_sip_dialog_terminated_event_t *event) {
+ belle_sip_dialog_t *dialog = belle_sip_dialog_terminated_event_get_dialog(event);
SalOp* op= (SalOp*)ctx;
if (op->dialog) {
- op->dialog=NULL;
- sal_op_unref(op);
+ if (belle_sip_dialog_terminated_event_is_expired(event)){
+ if (!belle_sip_dialog_is_server(dialog)){
+ /*notify the app that our subscription is dead*/
+ const char *eventname = NULL;
+ if (op->event){
+ eventname = belle_sip_header_event_get_package_name(op->event);
+ }
+ op->base.root->callbacks.notify(op, SalSubscribeTerminated, eventname, NULL);
+ }else{
+ op->base.root->callbacks.incoming_subscribe_closed(op);
+ }
+ }
+ set_or_update_dialog(op, NULL);
}
}
static void subscribe_response_event(void *op_base, const belle_sip_response_event_t *event){
+ SalOp *op = (SalOp*)op_base;
+ belle_sip_request_t * req;
+ const char *method;
+ belle_sip_client_transaction_t *tr = belle_sip_response_event_get_client_transaction(event);
+
+ if (!tr) return;
+ req = belle_sip_transaction_get_request((belle_sip_transaction_t*)tr);
+ method = belle_sip_request_get_method(req);
+
+ if (!op->dialog) {
+ if (strcmp(method,"NOTIFY")==0){
+ sal_op_set_error_info_from_response(op,belle_sip_response_event_get_response(event));
+ op->base.root->callbacks.on_notify_response(op);
+ }
+ }
}
static void subscribe_process_timeout(void *user_ctx, const belle_sip_timeout_event_t *event) {
+ SalOp *op = (SalOp*)user_ctx;
+ belle_sip_request_t * req;
+ const char *method;
+ belle_sip_client_transaction_t *tr = belle_sip_timeout_event_get_client_transaction(event);
+
+ if (!tr) return;
+ req = belle_sip_transaction_get_request((belle_sip_transaction_t*)tr);
+ method = belle_sip_request_get_method(req);
+
+ if (!op->dialog) {
+ /*this is handling outgoing out-of-dialog notifies*/
+ if (strcmp(method,"NOTIFY")==0){
+ SalErrorInfo *ei=&op->error_info;
+ sal_error_info_set(ei,SalReasonRequestTimeout,0,NULL,NULL);
+ op->base.root->callbacks.on_notify_response(op);
+ }
+ }
}
static void subscribe_process_transaction_terminated(void *user_ctx, const belle_sip_transaction_terminated_event_t *event) {
}
-static void handle_notify(SalOp *op, belle_sip_request_t *req, const char *eventname, SalBody * body){
+static void handle_notify(SalOp *op, belle_sip_request_t *req, const char *eventname, SalBodyHandler* body_handler){
SalSubscribeStatus sub_state;
belle_sip_header_subscription_state_t* subscription_state_header=belle_sip_message_get_header_by_type(req,belle_sip_header_subscription_state_t);
belle_sip_response_t* resp;
@@ -89,7 +152,7 @@ static void handle_notify(SalOp *op, belle_sip_request_t *req, const char *event
} else
sub_state=SalSubscribeActive;
sal_op_ref(op);
- op->base.root->callbacks.notify(op,sub_state,eventname,body);
+ op->base.root->callbacks.notify(op,sub_state,eventname,body_handler);
resp=sal_op_create_response_from_request(op,req,200);
belle_sip_server_transaction_send_response(server_transaction,resp);
sal_op_unref(op);
@@ -101,39 +164,46 @@ static void subscribe_process_request_event(void *op_base, const belle_sip_reque
belle_sip_request_t* req = belle_sip_request_event_get_request(event);
belle_sip_dialog_state_t dialog_state;
belle_sip_header_expires_t* expires = belle_sip_message_get_header_by_type(req,belle_sip_header_expires_t);
- belle_sip_header_t *event_header;
- SalBody body;
+ belle_sip_header_event_t *event_header;
+ belle_sip_body_handler_t *body_handler;
belle_sip_response_t* resp;
const char *eventname=NULL;
const char *method=belle_sip_request_get_method(req);
+ belle_sip_dialog_t *dialog = NULL;
belle_sip_object_ref(server_transaction);
if (op->pending_server_trans) belle_sip_object_unref(op->pending_server_trans);
op->pending_server_trans=server_transaction;
- event_header=belle_sip_message_get_header((belle_sip_message_t*)req,"Event");
- sal_op_get_body(op,(belle_sip_message_t*)req,&body);
+ event_header=belle_sip_message_get_header_by_type(req,belle_sip_header_event_t);
+ body_handler = BELLE_SIP_BODY_HANDLER(sal_op_get_body_handler(op, BELLE_SIP_MESSAGE(req)));
if (event_header==NULL){
ms_warning("No event header in incoming SUBSCRIBE.");
resp=sal_op_create_response_from_request(op,req,400);
belle_sip_server_transaction_send_response(server_transaction,resp);
+ if (!op->dialog) sal_op_release(op);
return;
}
if (op->event==NULL) {
op->event=event_header;
belle_sip_object_ref(op->event);
}
- eventname=belle_sip_header_get_unparsed_value(event_header);
+ eventname=belle_sip_header_event_get_package_name(event_header);
if (!op->dialog) {
if (strcmp(method,"SUBSCRIBE")==0){
- op->dialog=belle_sip_provider_create_dialog(op->base.root->prov,BELLE_SIP_TRANSACTION(server_transaction));
- belle_sip_dialog_set_application_data(op->dialog,op);
- sal_op_ref(op);
+ dialog = belle_sip_provider_create_dialog(op->base.root->prov,BELLE_SIP_TRANSACTION(server_transaction));
+ if (!dialog){
+ resp=sal_op_create_response_from_request(op,req,481);
+ belle_sip_server_transaction_send_response(server_transaction,resp);
+ sal_op_release(op);
+ return;
+ }
+ set_or_update_dialog(op, dialog);
ms_message("new incoming subscription from [%s] to [%s]",sal_op_get_from(op),sal_op_get_to(op));
}else{ /*this is a NOTIFY*/
- handle_notify(op,req,eventname,&body);
+ handle_notify(op, req, eventname, (SalBodyHandler *)body_handler);
return;
}
}
@@ -141,7 +211,10 @@ static void subscribe_process_request_event(void *op_base, const belle_sip_reque
switch(dialog_state) {
case BELLE_SIP_DIALOG_NULL: {
- op->base.root->callbacks.subscribe_received(op,eventname,body.type ? &body : NULL);
+ const char *type = NULL;
+ belle_sip_header_content_type_t *content_type = belle_sip_message_get_header_by_type(BELLE_SIP_MESSAGE(req), belle_sip_header_content_type_t);
+ if (content_type) type = belle_sip_header_content_type_get_type(content_type);
+ op->base.root->callbacks.subscribe_received(op, eventname, type ? (SalBodyHandler *)body_handler : NULL);
break;
}
case BELLE_SIP_DIALOG_EARLY:
@@ -150,7 +223,7 @@ static void subscribe_process_request_event(void *op_base, const belle_sip_reque
case BELLE_SIP_DIALOG_CONFIRMED:
if (strcmp("NOTIFY",method)==0) {
- handle_notify(op,req,eventname,&body);
+ handle_notify(op, req, eventname, (SalBodyHandler *)body_handler);
} else if (strcmp("SUBSCRIBE",method)==0) {
/*either a refresh of an unsubscribe*/
if (expires && belle_sip_header_expires_get_expires(expires)>0) {
@@ -160,7 +233,7 @@ static void subscribe_process_request_event(void *op_base, const belle_sip_reque
ms_message("Unsubscribe received from [%s]",sal_op_get_from(op));
resp=sal_op_create_response_from_request(op,req,200);
belle_sip_server_transaction_send_response(server_transaction,resp);
- op->base.root->callbacks.subscribe_closed(op);
+ op->base.root->callbacks.incoming_subscribe_closed(op);
}
}
break;
@@ -172,6 +245,18 @@ static void subscribe_process_request_event(void *op_base, const belle_sip_reque
static belle_sip_listener_callbacks_t op_subscribe_callbacks={ 0 };
+/*Invoke when sal_op_release is called by upper layer*/
+static void sal_op_release_cb(struct SalOpBase* op_base) {
+ SalOp *op =(SalOp*)op_base;
+ if(op->refresher) {
+ belle_sip_refresher_stop(op->refresher);
+ belle_sip_object_unref(op->refresher);
+ op->refresher=NULL;
+ set_or_update_dialog(op,NULL); /*only if we have refresher. else dialog terminated event will remove association*/
+ }
+
+}
+
void sal_op_subscribe_fill_cbs(SalOp*op) {
if (op_subscribe_callbacks.process_io_error==NULL){
op_subscribe_callbacks.process_io_error=subscribe_process_io_error;
@@ -183,10 +268,11 @@ void sal_op_subscribe_fill_cbs(SalOp*op) {
}
op->callbacks=&op_subscribe_callbacks;
op->type=SalOpSubscribe;
+ op->base.release_cb=sal_op_release_cb;
}
-int sal_subscribe(SalOp *op, const char *from, const char *to, const char *eventname, int expires, const SalBody *body){
+int sal_subscribe(SalOp *op, const char *from, const char *to, const char *eventname, int expires, const SalBodyHandler *body_handler){
belle_sip_request_t *req=NULL;
if (from)
@@ -196,25 +282,20 @@ int sal_subscribe(SalOp *op, const char *from, const char *to, const char *event
if (!op->dialog){
sal_op_subscribe_fill_cbs(op);
- /*???sal_exosip_fix_route(op); make sure to ha ;lr*/
req=sal_op_build_request(op,"SUBSCRIBE");
if( req == NULL ) {
return -1;
}
- if (eventname){
- if (op->event) belle_sip_object_unref(op->event);
- op->event=belle_sip_header_create("Event",eventname);
- belle_sip_object_ref(op->event);
- }
- belle_sip_message_add_header(BELLE_SIP_MESSAGE(req),op->event);
+ sal_op_set_event(op, eventname);
+ belle_sip_message_add_header(BELLE_SIP_MESSAGE(req),BELLE_SIP_HEADER(op->event));
belle_sip_message_add_header(BELLE_SIP_MESSAGE(req),BELLE_SIP_HEADER(belle_sip_header_expires_create(expires)));
- sal_op_add_body(op,(belle_sip_message_t*)req,body);
+ belle_sip_message_set_body_handler(BELLE_SIP_MESSAGE(req), BELLE_SIP_BODY_HANDLER(body_handler));
return sal_op_send_and_create_refresher(op,req,expires,subscribe_refresher_listener);
}else if (op->refresher){
const belle_sip_transaction_t *tr=(const belle_sip_transaction_t*) belle_sip_refresher_get_transaction(op->refresher);
belle_sip_request_t *last_req=belle_sip_transaction_get_request(tr);
/* modify last request to update body*/
- sal_op_add_body(op,(belle_sip_message_t*)last_req,body);
+ belle_sip_message_set_body_handler(BELLE_SIP_MESSAGE(last_req), BELLE_SIP_BODY_HANDLER(body_handler));
return belle_sip_refresher_refresh(op->refresher,expires);
}
ms_warning("sal_subscribe(): no dialog and no refresher ?");
@@ -225,7 +306,7 @@ int sal_unsubscribe(SalOp *op){
if (op->refresher){
const belle_sip_transaction_t *tr=(const belle_sip_transaction_t*) belle_sip_refresher_get_transaction(op->refresher);
belle_sip_request_t *last_req=belle_sip_transaction_get_request(tr);
- sal_op_add_body(op,(belle_sip_message_t*)last_req,NULL);
+ belle_sip_message_set_body(BELLE_SIP_MESSAGE(last_req), NULL, 0);
belle_sip_refresher_refresh(op->refresher,0);
return 0;
}
@@ -241,6 +322,29 @@ int sal_subscribe_accept(SalOp *op){
return 0;
}
+int sal_notify_pending_state(SalOp *op){
+
+ if (op->dialog != NULL && op->pending_server_trans) {
+ belle_sip_request_t* notify;
+ belle_sip_header_subscription_state_t* sub_state;
+ ms_message("Sending NOTIFY with subscription state pending for op [%p]",op);
+ if (!(notify=belle_sip_dialog_create_request(op->dialog,"NOTIFY"))) {
+ ms_error("Cannot create NOTIFY on op [%p]",op);
+ return -1;
+ }
+ if (op->event) belle_sip_message_add_header(BELLE_SIP_MESSAGE(notify),BELLE_SIP_HEADER(op->event));
+ sub_state=belle_sip_header_subscription_state_new();
+ belle_sip_header_subscription_state_set_state(sub_state,BELLE_SIP_SUBSCRIPTION_STATE_PENDING);
+ belle_sip_message_add_header(BELLE_SIP_MESSAGE(notify), BELLE_SIP_HEADER(sub_state));
+ return sal_op_send_request(op,notify);
+ } else {
+ ms_warning("NOTIFY with subscription state pending for op [%p] not implemented in this case (either dialog pending trans does not exist",op);
+ }
+
+ return 0;
+}
+
+
int sal_subscribe_decline(SalOp *op, SalReason reason){
belle_sip_response_t* resp = belle_sip_response_create_from_request(belle_sip_transaction_get_request(BELLE_SIP_TRANSACTION(op->pending_server_trans)),
sal_reason_to_sip_code(reason));
@@ -248,19 +352,24 @@ int sal_subscribe_decline(SalOp *op, SalReason reason){
return 0;
}
-int sal_notify(SalOp *op, const SalBody *body){
+int sal_notify(SalOp *op, const SalBodyHandler *body_handler){
belle_sip_request_t* notify;
- if (!op->dialog) return -1;
-
- if (!(notify=belle_sip_dialog_create_queued_request(op->dialog,"NOTIFY"))) return -1;
+ if (op->dialog){
+ if (!(notify=belle_sip_dialog_create_queued_request(op->dialog,"NOTIFY"))) return -1;
+ }else{
+ sal_op_subscribe_fill_cbs(op);
+ notify = sal_op_build_request(op, "NOTIFY");
+ }
- if (op->event) belle_sip_message_add_header(BELLE_SIP_MESSAGE(notify),op->event);
+ if (op->event) belle_sip_message_add_header(BELLE_SIP_MESSAGE(notify),BELLE_SIP_HEADER(op->event));
belle_sip_message_add_header(BELLE_SIP_MESSAGE(notify)
- ,BELLE_SIP_HEADER(belle_sip_header_subscription_state_create(BELLE_SIP_SUBSCRIPTION_STATE_ACTIVE,600)));
-
- sal_op_add_body(op,(belle_sip_message_t*)notify, body);
+ ,op->dialog ?
+ BELLE_SIP_HEADER(belle_sip_header_subscription_state_create(BELLE_SIP_SUBSCRIPTION_STATE_ACTIVE,600)) :
+ BELLE_SIP_HEADER(belle_sip_header_subscription_state_create(BELLE_SIP_SUBSCRIPTION_STATE_TERMINATED,0))
+ );
+ belle_sip_message_set_body_handler(BELLE_SIP_MESSAGE(notify), BELLE_SIP_BODY_HANDLER(body_handler));
return sal_op_send_request(op,notify);
}
@@ -268,7 +377,7 @@ int sal_notify_close(SalOp *op){
belle_sip_request_t* notify;
if (!op->dialog) return -1;
if (!(notify=belle_sip_dialog_create_queued_request(op->dialog,"NOTIFY"))) return -1;
- if (op->event) belle_sip_message_add_header(BELLE_SIP_MESSAGE(notify),op->event);
+ if (op->event) belle_sip_message_add_header(BELLE_SIP_MESSAGE(notify),BELLE_SIP_HEADER(op->event));
belle_sip_message_add_header(BELLE_SIP_MESSAGE(notify)
,BELLE_SIP_HEADER(belle_sip_header_subscription_state_create(BELLE_SIP_SUBSCRIPTION_STATE_TERMINATED,-1)));
return sal_op_send_request(op,notify);
diff --git a/coreapi/bellesip_sal/sal_op_impl.c b/coreapi/bellesip_sal/sal_op_impl.c
index 41413637c..634bd22fb 100644
--- a/coreapi/bellesip_sal/sal_op_impl.c
+++ b/coreapi/bellesip_sal/sal_op_impl.c
@@ -30,14 +30,22 @@ SalOp * sal_op_new(Sal *sal){
return op;
}
+void sal_op_kill_dialog(SalOp *op) {
+ ms_warning("op [%p]: force kill of dialog [%p]", op, op->dialog);
+ belle_sip_dialog_delete(op->dialog);
+}
+
void sal_op_release(SalOp *op){
/*if in terminating state, keep this state because it means we are waiting for a response to be able to terminate the operation.*/
if (op->state!=SalOpStateTerminating)
op->state=SalOpStateTerminated;
sal_op_set_user_pointer(op,NULL);/*mandatory because releasing op doesn't not mean freeing op. Make sure back pointer will not be used later*/
+ if (op->base.release_cb)
+ op->base.release_cb(&op->base);
if (op->refresher) {
belle_sip_refresher_stop(op->refresher);
}
+ op->op_released = TRUE;
sal_op_unref(op);
}
@@ -72,7 +80,7 @@ void sal_op_authenticate(SalOp *op, const SalAuthInfo *info){
/*Registration authenticate is just about registering again*/
sal_register_refresh(op,-1);
}else {
- /*for sure auth info will be accesible from the provider*/
+ /*for sure auth info will be accessible from the provider*/
sal_process_authentication(op);
}
return ;
@@ -162,7 +170,7 @@ belle_sip_request_t* sal_op_build_request(SalOp *op,const char* method) {
ms_error("No To: address, cannot build request");
return NULL;
}
-
+
to_uri = belle_sip_header_address_get_uri(BELLE_SIP_HEADER_ADDRESS(to_address));
if( to_uri == NULL ){
ms_error("To: address is invalid, cannot build request");
@@ -232,6 +240,14 @@ int sal_ping(SalOp *op, const char *from, const char *to){
return sal_op_send_request(op,sal_op_build_request(op,"OPTIONS"));
}
+void sal_op_set_replaces(SalOp* op,belle_sip_header_replaces_t* replaces) {
+ if (op->replaces){
+ belle_sip_object_unref(op->replaces);
+ }
+ op->replaces=replaces;
+ belle_sip_object_ref(op->replaces);
+}
+
void sal_op_set_remote_ua(SalOp*op,belle_sip_message_t* message) {
belle_sip_header_user_agent_t* user_agent=belle_sip_message_get_header_by_type(message,belle_sip_header_user_agent_t);
char user_agent_string[256];
@@ -339,6 +355,9 @@ static int _sal_op_send_request_with_contact(SalOp* op, belle_sip_request_t* req
}
#endif
}
+ /*because in case of tunnel, transport can be changed*/
+ transport=belle_sip_uri_get_transport_param(next_hop_uri);
+
if ((strcmp(method,"REGISTER")==0 || strcmp(method,"SUBSCRIBE")==0) && transport &&
(strcasecmp(transport,"TCP")==0 || strcasecmp(transport,"TLS")==0)){
/*RFC 5923: add 'alias' parameter to tell the server that we want it to keep the connection for future requests*/
@@ -392,7 +411,7 @@ int sal_op_send_request(SalOp* op, belle_sip_request_t* request) {
return _sal_op_send_request_with_contact(op, request,need_contact);
}
-SalReason sal_reason_to_sip_code(SalReason r){
+int sal_reason_to_sip_code(SalReason r){
int ret=500;
switch(r){
case SalReasonNone:
@@ -517,14 +536,14 @@ SalReason _sal_reason_from_sip_code(int code) {
return SalReasonNotImplemented;
case 502:
return SalReasonBadGateway;
+ case 503:
+ return SalReasonServiceUnavailable;
case 504:
return SalReasonServerTimeout;
case 600:
return SalReasonDoNotDisturb;
case 603:
return SalReasonDeclined;
- case 503:
- return SalReasonServiceUnavailable;
default:
return SalReasonUnknown;
}
@@ -700,55 +719,21 @@ void sal_op_assign_recv_headers(SalOp *op, belle_sip_message_t *incoming){
const char *sal_op_get_remote_contact(const SalOp *op){
/*
* remote contact is filled in process_response
- * return sal_custom_header_find(op->base.recv_custom_headers,"Contact");
*/
return op->base.remote_contact;
}
-void sal_op_add_body(SalOp *op, belle_sip_message_t *req, const SalBody *body){
- belle_sip_message_remove_header((belle_sip_message_t*)req,"Content-type");
- belle_sip_message_remove_header((belle_sip_message_t*)req,"Content-length");
- belle_sip_message_remove_header((belle_sip_message_t*)req,"Content-encoding");
- belle_sip_message_set_body((belle_sip_message_t*)req,NULL,0);
- if (body && body->type && body->subtype && body->data){
- belle_sip_message_add_header((belle_sip_message_t*)req,
- (belle_sip_header_t*)belle_sip_header_content_type_create(body->type,body->subtype));
- belle_sip_message_add_header((belle_sip_message_t*)req,
- (belle_sip_header_t*)belle_sip_header_content_length_create(body->size));
- belle_sip_message_set_body((belle_sip_message_t*)req,(const char*)body->data,body->size);
- if (body->encoding){
- belle_sip_message_add_header((belle_sip_message_t*)req,(belle_sip_header_t*)
- belle_sip_header_create("Content-encoding",body->encoding));
- }
+SalBodyHandler * sal_op_get_body_handler(SalOp *op, belle_sip_message_t *msg) {
+ belle_sip_body_handler_t *body_handler = belle_sip_message_get_body_handler(msg);
+ if (body_handler != NULL) {
+ belle_sip_header_content_type_t *content_type = belle_sip_message_get_header_by_type(msg, belle_sip_header_content_type_t);
+ belle_sip_header_content_length_t *content_length = belle_sip_message_get_header_by_type(msg, belle_sip_header_content_length_t);
+ belle_sip_header_t *content_encoding = belle_sip_message_get_header(msg, "Content-Encoding");
+ if (content_type != NULL) belle_sip_body_handler_add_header(body_handler, BELLE_SIP_HEADER(content_type));
+ if (content_length != NULL) belle_sip_body_handler_add_header(body_handler, BELLE_SIP_HEADER(content_length));
+ if (content_encoding != NULL) belle_sip_body_handler_add_header(body_handler, content_encoding);
}
-}
-
-
-bool_t sal_op_get_body(SalOp *op, belle_sip_message_t *msg, SalBody *salbody){
- const char *body = NULL;
- belle_sip_header_content_type_t *content_type;
- belle_sip_header_content_length_t *clen=NULL;
- belle_sip_header_t *content_encoding;
-
- content_type=belle_sip_message_get_header_by_type(msg,belle_sip_header_content_type_t);
- if (content_type){
- body=belle_sip_message_get_body(msg);
- clen=belle_sip_message_get_header_by_type(msg,belle_sip_header_content_length_t);
- }
- content_encoding=belle_sip_message_get_header(msg,"Content-encoding");
-
- memset(salbody,0,sizeof(SalBody));
-
- if (content_type && body && clen) {
- salbody->type=belle_sip_header_content_type_get_type(content_type);
- salbody->subtype=belle_sip_header_content_type_get_subtype(content_type);
- salbody->data=body;
- salbody->size=belle_sip_header_content_length_get_content_length(clen);
- if (content_encoding)
- salbody->encoding=belle_sip_header_get_unparsed_value(content_encoding);
- return TRUE;
- }
- return FALSE;
+ return (SalBodyHandler *)body_handler;
}
void sal_op_set_privacy(SalOp* op,SalPrivacyMask privacy) {
@@ -769,10 +754,10 @@ void sal_op_set_manual_refresher_mode(SalOp *op, bool_t enabled){
op->manual_refresher=enabled;
}
-bool_t sal_op_is_ipv6(SalOp *op){
+int sal_op_get_address_family(SalOp *op){
belle_sip_transaction_t *tr=NULL;
belle_sip_header_address_t *contact;
- belle_sip_request_t *req;
+
if (op->refresher)
tr=(belle_sip_transaction_t *)belle_sip_refresher_get_transaction(op->refresher);
@@ -781,17 +766,29 @@ bool_t sal_op_is_ipv6(SalOp *op){
tr=(belle_sip_transaction_t *)op->pending_client_trans;
if (tr==NULL)
tr=(belle_sip_transaction_t *)op->pending_server_trans;
-
+
if (tr==NULL){
ms_error("Unable to determine IP version from signaling operation.");
- return FALSE;
+ return AF_UNSPEC;
}
- req=belle_sip_transaction_get_request(tr);
- contact=(belle_sip_header_address_t*)belle_sip_message_get_header_by_type(req,belle_sip_header_contact_t);
- if (!contact){
- ms_error("Unable to determine IP version from signaling operation, no contact header found.");
+
+
+ if (op->refresher) {
+ belle_sip_response_t *resp = belle_sip_transaction_get_response(tr);
+ belle_sip_header_via_t *via = resp ?belle_sip_message_get_header_by_type(resp,belle_sip_header_via_t):NULL;
+ if (!via){
+ ms_error("Unable to determine IP version from signaling operation, no via header found.");
+ return AF_UNSPEC;
+ }
+ return (strchr(belle_sip_header_via_get_host(via),':') != NULL) ? AF_INET6 : AF_INET;
+ } else {
+ belle_sip_request_t *req = belle_sip_transaction_get_request(tr);
+ contact=(belle_sip_header_address_t*)belle_sip_message_get_header_by_type(req,belle_sip_header_contact_t);
+ if (!contact){
+ ms_error("Unable to determine IP version from signaling operation, no contact header found.");
+ }
+ return sal_address_is_ipv6((SalAddress*)contact) ? AF_INET6 : AF_INET;
}
- return sal_address_is_ipv6((SalAddress*)contact);
}
bool_t sal_op_is_idle(SalOp *op){
@@ -814,6 +811,52 @@ void sal_call_set_sdp_handling(SalOp *h, SalOpSDPHandling handling) {
void sal_op_cnx_ip_to_0000_if_sendonly_enable(SalOp *op,bool_t yesno) {
op->cnx_ip_to_0000_if_sendonly_enabled = yesno;
}
+
bool_t sal_op_cnx_ip_to_0000_if_sendonly_enabled(SalOp *op) {
return op->cnx_ip_to_0000_if_sendonly_enabled;
}
+
+bool_t sal_op_is_forked_of(const SalOp *op1, const SalOp *op2){
+ return op1->base.call_id && op2->base.call_id && strcmp(op1->base.call_id, op2->base.call_id) == 0;
+}
+int sal_op_refresh(SalOp *op) {
+ if (op->refresher) {
+ belle_sip_refresher_refresh(op->refresher,belle_sip_refresher_get_expires(op->refresher));
+ return 0;
+ }
+ ms_warning("sal_refresh on op [%p] of type [%s] no refresher",op,sal_op_type_to_string(op->type));
+ return -1;
+}
+
+void sal_op_set_event(SalOp *op, const char *eventname){
+ belle_sip_header_event_t *header = NULL;
+ if (op->event) belle_sip_object_unref(op->event);
+ if (eventname){
+ header = belle_sip_header_event_create(eventname);
+ belle_sip_object_ref(header);
+ }
+ op->event = header;
+}
+
+const char* sal_op_get_public_address(SalOp *op, int *port) {
+ if (op && op->refresher) {
+ return belle_sip_refresher_get_public_address(op->refresher, port);
+ }
+ return NULL;
+}
+
+const char* sal_op_get_local_address(SalOp *op, int *port) {
+ if (op && op->refresher) {
+ return belle_sip_refresher_get_local_address(op->refresher, port);
+ }
+ return NULL;
+}
+
+char* sal_op_get_dialog_id(const SalOp *op) {
+ if (op->dialog != NULL) {
+ return ms_strdup_printf("%s;to-tag=%s;from-tag=%s", ((SalOpBase*)op)->call_id,
+ belle_sip_dialog_get_remote_tag(op->dialog), belle_sip_dialog_get_local_tag(op->dialog));
+ }
+ return NULL;
+}
+
diff --git a/coreapi/bellesip_sal/sal_op_info.c b/coreapi/bellesip_sal/sal_op_info.c
index 7892dd6ec..2e2eb3e49 100644
--- a/coreapi/bellesip_sal/sal_op_info.c
+++ b/coreapi/bellesip_sal/sal_op_info.c
@@ -19,12 +19,12 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#include "sal_impl.h"
-int sal_send_info(SalOp *op, const char *from, const char *to, const SalBody *body){
+int sal_send_info(SalOp *op, const char *from, const char *to, const SalBodyHandler *body_handler){
if (op->dialog){
belle_sip_request_t *req;
belle_sip_dialog_enable_pending_trans_checking(op->dialog,op->base.root->pending_trans_checking);
req=belle_sip_dialog_create_queued_request(op->dialog,"INFO");
- sal_op_add_body(op,(belle_sip_message_t*)req,body);
+ belle_sip_message_set_body_handler(BELLE_SIP_MESSAGE(req), BELLE_SIP_BODY_HANDLER(body_handler));
return sal_op_send_request(op,req);
}
return -1;
diff --git a/coreapi/bellesip_sal/sal_op_message.c b/coreapi/bellesip_sal/sal_op_message.c
index a60febf7a..533453da4 100644
--- a/coreapi/bellesip_sal/sal_op_message.c
+++ b/coreapi/bellesip_sal/sal_op_message.c
@@ -110,122 +110,127 @@ void sal_process_incoming_message(SalOp *op,const belle_sip_request_event_t *eve
from_header=belle_sip_message_get_header_by_type(BELLE_SIP_MESSAGE(req),belle_sip_header_from_t);
content_type=belle_sip_message_get_header_by_type(BELLE_SIP_MESSAGE(req),belle_sip_header_content_type_t);
- /* check if we have a xml/cipher message to be decrypted */
- if (content_type && (cipher_xml=is_cipher_xml(content_type))) {
- /* access the zrtp cache to get keys needed to decipher the message */
- LinphoneCore *lc=(LinphoneCore *)sal_get_user_pointer(sal_op_get_sal(op));
- FILE *CACHEFD = fopen(lc->zrtp_secrets_cache, "rb+");
- if (CACHEFD == NULL) {
- ms_warning("Unable to access ZRTP ZID cache to decrypt message");
- goto error;
- } else {
- size_t cacheSize;
- char *cacheString;
- int retval;
- xmlDocPtr cacheXml;
-
- cacheString=ms_load_file_content(CACHEFD, &cacheSize);
- if (!cacheString){
- ms_warning("Unable to load content of ZRTP ZID cache to decrypt message");
- goto error;
- }
- cacheString[cacheSize] = '\0';
- cacheSize += 1;
- fclose(CACHEFD);
- cacheXml = xmlParseDoc((xmlChar*)cacheString);
- ms_free(cacheString);
- retval = lime_decryptMultipartMessage(cacheXml, (uint8_t *)belle_sip_message_get_body(BELLE_SIP_MESSAGE(req)), &decryptedMessage);
- if (retval != 0) {
- ms_warning("Unable to decrypt message, reason : %s - op [%p]", lime_error_code_to_string(retval), op);
- free(decryptedMessage);
- xmlFreeDoc(cacheXml);
- errcode = 488;
+
+ if (content_type){
+
+ /* check if we have a xml/cipher message to be decrypted */
+ if ((cipher_xml=is_cipher_xml(content_type))) {
+ /* access the zrtp cache to get keys needed to decipher the message */
+ LinphoneCore *lc=(LinphoneCore *)sal_get_user_pointer(sal_op_get_sal(op));
+ FILE *CACHEFD = NULL;
+ if (lc->zrtp_secrets_cache != NULL) CACHEFD = fopen(lc->zrtp_secrets_cache, "rb+");
+ if (CACHEFD == NULL) {
+ ms_warning("Unable to access ZRTP ZID cache to decrypt message");
goto error;
} else {
- /* dump updated cache to a string */
- xmlChar *xmlStringOutput;
- int xmlStringLength;
- xmlDocDumpFormatMemoryEnc(cacheXml, &xmlStringOutput, &xmlStringLength, "UTF-8", 0);
- /* write it to the cache file */
- CACHEFD = fopen(lc->zrtp_secrets_cache, "wb+");
- if (fwrite(xmlStringOutput, 1, xmlStringLength, CACHEFD)<=0){
- ms_warning("Fail to write cache");
+ size_t cacheSize;
+ char *cacheString;
+ int retval;
+ xmlDocPtr cacheXml;
+
+ cacheString=ms_load_file_content(CACHEFD, &cacheSize);
+ if (!cacheString){
+ ms_warning("Unable to load content of ZRTP ZID cache to decrypt message");
+ goto error;
}
- xmlFree(xmlStringOutput);
+ cacheString[cacheSize] = '\0';
+ cacheSize += 1;
fclose(CACHEFD);
+ cacheXml = xmlParseDoc((xmlChar*)cacheString);
+ ms_free(cacheString);
+ retval = lime_decryptMultipartMessage(cacheXml, (uint8_t *)belle_sip_message_get_body(BELLE_SIP_MESSAGE(req)), &decryptedMessage);
+ if (retval != 0) {
+ ms_warning("Unable to decrypt message, reason : %s - op [%p]", lime_error_code_to_string(retval), op);
+ free(decryptedMessage);
+ xmlFreeDoc(cacheXml);
+ errcode = 488;
+ goto error;
+ } else {
+ /* dump updated cache to a string */
+ xmlChar *xmlStringOutput;
+ int xmlStringLength;
+ xmlDocDumpFormatMemoryEnc(cacheXml, &xmlStringOutput, &xmlStringLength, "UTF-8", 0);
+ /* write it to the cache file */
+ CACHEFD = fopen(lc->zrtp_secrets_cache, "wb+");
+ if (fwrite(xmlStringOutput, 1, xmlStringLength, CACHEFD)<=0){
+ ms_warning("Fail to write cache");
+ }
+ xmlFree(xmlStringOutput);
+ fclose(CACHEFD);
+ }
+
+ xmlFreeDoc(cacheXml);
}
- xmlFreeDoc(cacheXml);
}
-
- }
-
- rcs_filetransfer=is_rcs_filetransfer(content_type);
- if (content_type && ((plain_text=is_plain_text(content_type))
- || (external_body=is_external_body(content_type))
- || (decryptedMessage!=NULL)
- || rcs_filetransfer)) {
- SalMessage salmsg;
- char message_id[256]={0};
+ external_body=is_external_body(content_type);
+ plain_text=is_plain_text(content_type);
+ rcs_filetransfer = is_rcs_filetransfer(content_type);
- if (op->pending_server_trans) belle_sip_object_unref(op->pending_server_trans);
- op->pending_server_trans=server_transaction;
- belle_sip_object_ref(op->pending_server_trans);
-
- address=belle_sip_header_address_create(belle_sip_header_address_get_displayname(BELLE_SIP_HEADER_ADDRESS(from_header))
- ,belle_sip_header_address_get_uri(BELLE_SIP_HEADER_ADDRESS(from_header)));
- from=belle_sip_object_to_string(BELLE_SIP_OBJECT(address));
- snprintf(message_id,sizeof(message_id)-1,"%s%i"
- ,belle_sip_header_call_id_get_call_id(call_id)
- ,belle_sip_header_cseq_get_seq_number(cseq));
- salmsg.from=from;
- /* if we just deciphered a message, use the deciphered part(which can be a rcs xml body pointing to the file to retreive from server)*/
- if (cipher_xml) {
- salmsg.text = (char *)decryptedMessage;
- } else { /* message body wasn't ciphered */
- salmsg.text=(plain_text||rcs_filetransfer)?belle_sip_message_get_body(BELLE_SIP_MESSAGE(req)):NULL;
- }
- salmsg.url=NULL;
- salmsg.content_type = NULL;
- if (rcs_filetransfer) { /* if we have a rcs file transfer, set the type, message body (stored in salmsg.text) contains all needed information to retrieve the file */
- salmsg.content_type = "application/vnd.gsma.rcs-ft-http+xml";
- }
- if (external_body && belle_sip_parameters_get_parameter(BELLE_SIP_PARAMETERS(content_type),"URL")) {
- size_t url_length=strlen(belle_sip_parameters_get_parameter(BELLE_SIP_PARAMETERS(content_type),"URL"));
- salmsg.url = ms_strdup(belle_sip_parameters_get_parameter(BELLE_SIP_PARAMETERS(content_type),"URL")+1); /* skip first "*/
- ((char*)salmsg.url)[url_length-2]='\0'; /*remove trailing "*/
- }
- salmsg.message_id=message_id;
- salmsg.time=date ? belle_sip_header_date_get_time(date) : time(NULL);
- op->base.root->callbacks.text_received(op,&salmsg);
+ if (external_body || plain_text || rcs_filetransfer || decryptedMessage!=NULL) {
+ SalMessage salmsg;
+ char message_id[256]={0};
+
+ if (op->pending_server_trans) belle_sip_object_unref(op->pending_server_trans);
+ op->pending_server_trans=server_transaction;
+ belle_sip_object_ref(op->pending_server_trans);
+
+ address=belle_sip_header_address_create(belle_sip_header_address_get_displayname(BELLE_SIP_HEADER_ADDRESS(from_header))
+ ,belle_sip_header_address_get_uri(BELLE_SIP_HEADER_ADDRESS(from_header)));
+ from=belle_sip_object_to_string(BELLE_SIP_OBJECT(address));
+ snprintf(message_id,sizeof(message_id)-1,"%s%i"
+ ,belle_sip_header_call_id_get_call_id(call_id)
+ ,belle_sip_header_cseq_get_seq_number(cseq));
+ salmsg.from=from;
+ /* if we just deciphered a message, use the deciphered part(which can be a rcs xml body pointing to the file to retreive from server)*/
+ if (cipher_xml) {
+ salmsg.text = (char *)decryptedMessage;
+ } else { /* message body wasn't ciphered */
+ salmsg.text=(plain_text||rcs_filetransfer)?belle_sip_message_get_body(BELLE_SIP_MESSAGE(req)):NULL;
+ }
+ salmsg.url=NULL;
+ salmsg.content_type = NULL;
+ if (rcs_filetransfer) { /* if we have a rcs file transfer, set the type, message body (stored in salmsg.text) contains all needed information to retrieve the file */
+ salmsg.content_type = "application/vnd.gsma.rcs-ft-http+xml";
+ }
+ if (external_body && belle_sip_parameters_get_parameter(BELLE_SIP_PARAMETERS(content_type),"URL")) {
+ size_t url_length=strlen(belle_sip_parameters_get_parameter(BELLE_SIP_PARAMETERS(content_type),"URL"));
+ salmsg.url = ms_strdup(belle_sip_parameters_get_parameter(BELLE_SIP_PARAMETERS(content_type),"URL")+1); /* skip first "*/
+ ((char*)salmsg.url)[url_length-2]='\0'; /*remove trailing "*/
+ }
+ salmsg.message_id=message_id;
+ salmsg.time=date ? belle_sip_header_date_get_time(date) : time(NULL);
+ op->base.root->callbacks.text_received(op,&salmsg);
- free(decryptedMessage);
- belle_sip_object_unref(address);
- belle_sip_free(from);
- if (salmsg.url) ms_free((char*)salmsg.url);
- } else if (content_type && is_im_iscomposing(content_type)) {
- SalIsComposing saliscomposing;
- address=belle_sip_header_address_create(belle_sip_header_address_get_displayname(BELLE_SIP_HEADER_ADDRESS(from_header))
- ,belle_sip_header_address_get_uri(BELLE_SIP_HEADER_ADDRESS(from_header)));
- from=belle_sip_object_to_string(BELLE_SIP_OBJECT(address));
- saliscomposing.from=from;
- saliscomposing.text=belle_sip_message_get_body(BELLE_SIP_MESSAGE(req));
- op->base.root->callbacks.is_composing_received(op,&saliscomposing);
- resp = belle_sip_response_create_from_request(req,200);
- belle_sip_server_transaction_send_response(server_transaction,resp);
- belle_sip_object_unref(address);
- belle_sip_free(from);
- } else {
- ms_error("Unsupported MESSAGE (content-type not recognized)");
- resp = belle_sip_response_create_from_request(req,415);
- add_message_accept((belle_sip_message_t*)resp);
- belle_sip_server_transaction_send_response(server_transaction,resp);
- sal_op_release(op);
- return;
+ free(decryptedMessage);
+ belle_sip_object_unref(address);
+ belle_sip_free(from);
+ if (salmsg.url) ms_free((char*)salmsg.url);
+ } else if (is_im_iscomposing(content_type)) {
+ SalIsComposing saliscomposing;
+ address=belle_sip_header_address_create(belle_sip_header_address_get_displayname(BELLE_SIP_HEADER_ADDRESS(from_header))
+ ,belle_sip_header_address_get_uri(BELLE_SIP_HEADER_ADDRESS(from_header)));
+ from=belle_sip_object_to_string(BELLE_SIP_OBJECT(address));
+ saliscomposing.from=from;
+ saliscomposing.text=belle_sip_message_get_body(BELLE_SIP_MESSAGE(req));
+ op->base.root->callbacks.is_composing_received(op,&saliscomposing);
+ resp = belle_sip_response_create_from_request(req,200);
+ belle_sip_server_transaction_send_response(server_transaction,resp);
+ belle_sip_object_unref(address);
+ belle_sip_free(from);
+ }else{
+ ms_error("Unsupported MESSAGE (content-type not recognized)");
+ errcode = 415;
+ goto error;
+ }
+ }else {
+ ms_error("Unsupported MESSAGE (no Content-Type)");
+ goto error;
}
return;
error:
resp = belle_sip_response_create_from_request(req, errcode);
+ add_message_accept((belle_sip_message_t*)resp);
belle_sip_server_transaction_send_response(server_transaction,resp);
sal_op_release(op);
}
@@ -239,8 +244,9 @@ int sal_message_send(SalOp *op, const char *from, const char *to, const char* co
belle_sip_request_t* req;
char content_type_raw[256];
size_t content_length = msg?strlen(msg):0;
- time_t curtime=time(NULL);
+ time_t curtime = ms_time(NULL);
uint8_t *multipartEncryptedMessage = NULL;
+ const char *body;
int retval;
if (op->dialog){
@@ -321,7 +327,11 @@ int sal_message_send(SalOp *op, const char *from, const char *to, const char* co
belle_sip_message_add_header(BELLE_SIP_MESSAGE(req),BELLE_SIP_HEADER(belle_sip_header_content_type_parse(content_type_raw)));
belle_sip_message_add_header(BELLE_SIP_MESSAGE(req),BELLE_SIP_HEADER(belle_sip_header_content_length_create(content_length)));
belle_sip_message_add_header(BELLE_SIP_MESSAGE(req),BELLE_SIP_HEADER(belle_sip_header_date_create_from_time(&curtime)));
- belle_sip_message_set_body(BELLE_SIP_MESSAGE(req),(multipartEncryptedMessage==NULL)?msg:(const char *)multipartEncryptedMessage,content_length);
+ body = (multipartEncryptedMessage==NULL) ? msg : (char*) multipartEncryptedMessage;
+ if (body){
+ /*don't call set_body() with null argument because it resets content type and content length*/
+ belle_sip_message_set_body(BELLE_SIP_MESSAGE(req), body, content_length);
+ }
retval = sal_op_send_request(op,req);
free(multipartEncryptedMessage);
diff --git a/coreapi/bellesip_sal/sal_op_presence.c b/coreapi/bellesip_sal/sal_op_presence.c
index e60ac3423..7dc9d75bf 100644
--- a/coreapi/bellesip_sal/sal_op_presence.c
+++ b/coreapi/bellesip_sal/sal_op_presence.c
@@ -47,41 +47,66 @@ void sal_add_presence_info(SalOp *op, belle_sip_message_t *notify, SalPresenceMo
}
static void presence_process_io_error(void *user_ctx, const belle_sip_io_error_event_t *event){
- ms_error("presence_process_io_error not implemented yet");
+ SalOp* op = (SalOp*)user_ctx;
+ belle_sip_request_t* request;
+ belle_sip_client_transaction_t* client_transaction = NULL;
+
+ if (BELLE_SIP_OBJECT_IS_INSTANCE_OF(belle_sip_io_error_event_get_source(event),
+ belle_sip_client_transaction_t)){
+ client_transaction = (belle_sip_client_transaction_t*)belle_sip_io_error_event_get_source(event);
+ }
+
+ if (!client_transaction) return;
+
+ request = belle_sip_transaction_get_request(BELLE_SIP_TRANSACTION(client_transaction));
+
+ if (strcmp("SUBSCRIBE",belle_sip_request_get_method(request))==0){
+ if (op->refresher){
+ ms_warning("presence_process_io_error() refresher is present, should not happen");
+ return;
+ }
+ ms_message("subscription to [%s] io error",sal_op_get_to(op));
+ if (!op->op_released){
+ op->base.root->callbacks.notify_presence(op,SalSubscribeTerminated, NULL,NULL); /*NULL = offline*/
+ }
+ }
}
static void presence_process_dialog_terminated(void *ctx, const belle_sip_dialog_terminated_event_t *event) {
SalOp* op= (SalOp*)ctx;
- if (op->dialog) {
- sal_op_unref(op);
- op->dialog=NULL;
- }
+ if (op->dialog && belle_sip_dialog_is_server(op->dialog)) {
+ ms_message("Incoming subscribtion from [%s] terminated",sal_op_get_from(op));
+ if (!op->op_released){
+ op->base.root->callbacks.subscribe_presence_closed(op, sal_op_get_from(op));
+ }
+ set_or_update_dialog(op, NULL);
+ }/* else client dialog is managed by refresher*/
}
-static void presence_refresher_listener(belle_sip_refresher_t* refresher, void* user_pointer, unsigned int status_code, const char* reason_phrase){
+static void presence_refresher_listener(belle_sip_refresher_t* refresher, void* user_pointer, unsigned int status_code, const char* reason_phrase, int will_retry){
SalOp* op = (SalOp*)user_pointer;
- switch(status_code){
- case 481: {
+ if (status_code >= 300) {
+ ms_message("The SUBSCRIBE dialog no longer works. Let's restart a new one.");
+ belle_sip_refresher_stop(op->refresher);
+ if (op->dialog) { /*delete previous dialog if any*/
+ set_or_update_dialog(op, NULL);
+ }
- ms_message("The server or remote ua lost the SUBSCRIBE dialog context. Let's restart a new one.");
- belle_sip_refresher_stop(op->refresher);
- if (op->dialog) { /*delete previous dialog if any*/
- belle_sip_dialog_set_application_data(op->dialog,NULL);
- belle_sip_object_unref(op->dialog);
- op->dialog=NULL;
- }
-
- if (sal_op_get_contact_address(op)) {
- /*contact is also probably not good*/
- SalAddress* contact=sal_address_clone(sal_op_get_contact_address(op));
- sal_address_set_port(contact,-1);
- sal_address_set_domain(contact,NULL);
- sal_op_set_contact_address(op,contact);
- sal_address_destroy(contact);
- }
-
- sal_subscribe_presence(op,NULL,NULL,-1);
- break;
+ if (sal_op_get_contact_address(op)) {
+ /*contact is also probably not good*/
+ SalAddress* contact=sal_address_clone(sal_op_get_contact_address(op));
+ sal_address_set_port(contact,-1);
+ sal_address_set_domain(contact,NULL);
+ sal_op_set_contact_address(op,contact);
+ sal_address_destroy(contact);
+ }
+ /*send a new SUBSCRIBE, that will attempt to establish a new dialog*/
+ sal_subscribe_presence(op,NULL,NULL,-1);
+ }
+ if (status_code == 0 || status_code == 503){
+ /*timeout or io error: the remote doesn't seem reachable.*/
+ if (!op->op_released){
+ op->base.root->callbacks.notify_presence(op,SalSubscribeActive, NULL,NULL); /*NULL = offline*/
}
}
}
@@ -99,9 +124,13 @@ static void presence_response_event(void *op_base, const belle_sip_response_even
sal_op_set_error_info_from_response(op,response);
if (code>=300) {
- ms_message("subscription to [%s] rejected",sal_op_get_to(op));
- op->base.root->callbacks.notify_presence(op,SalSubscribeTerminated, NULL,NULL); /*NULL = offline*/
- return;
+ if (strcmp("SUBSCRIBE",belle_sip_request_get_method(request))==0){
+ ms_message("subscription to [%s] rejected",sal_op_get_to(op));
+ if (!op->op_released){
+ op->base.root->callbacks.notify_presence(op,SalSubscribeTerminated, NULL,NULL); /*NULL = offline*/
+ }
+ return;
+ }
}
set_or_update_dialog(op_base,belle_sip_response_event_get_dialog(event));
if (!op->dialog) {
@@ -124,7 +153,7 @@ static void presence_response_event(void *op_base, const belle_sip_response_even
belle_sip_object_unref(op->refresher);
op->refresher=NULL;
}
- if (expires>0){
+ if ((expires != NULL) && (belle_sip_header_expires_get_expires(expires) > 0)) {
op->refresher=belle_sip_client_transaction_create_refresher(client_transaction);
belle_sip_refresher_set_listener(op->refresher,presence_refresher_listener,op);
belle_sip_refresher_set_realm(op->refresher,op->base.realm);
@@ -132,23 +161,28 @@ static void presence_response_event(void *op_base, const belle_sip_response_even
}
break;
}
- case BELLE_SIP_DIALOG_TERMINATED:
- if (op->refresher) {
- belle_sip_refresher_stop(op->refresher);
- belle_sip_object_unref(op->refresher);
- op->refresher=NULL;
- }
- break;
default: {
ms_error("presence op [%p] receive answer [%i] not implemented",op,code);
}
/* no break */
}
-
-
}
+
static void presence_process_timeout(void *user_ctx, const belle_sip_timeout_event_t *event) {
- ms_error("presence_process_timeout not implemented yet");
+ SalOp* op = (SalOp*)user_ctx;
+ belle_sip_client_transaction_t* client_transaction = belle_sip_timeout_event_get_client_transaction(event);
+ belle_sip_request_t* request;
+
+ if (!client_transaction) return;
+
+ request = belle_sip_transaction_get_request(BELLE_SIP_TRANSACTION(client_transaction));
+
+ if (strcmp("SUBSCRIBE",belle_sip_request_get_method(request))==0){
+ ms_message("subscription to [%s] timeout",sal_op_get_to(op));
+ if (!op->op_released){
+ op->base.root->callbacks.notify_presence(op,SalSubscribeTerminated, NULL,NULL); /*NULL = offline*/
+ }
+ }
}
static void presence_process_transaction_terminated(void *user_ctx, const belle_sip_transaction_terminated_event_t *event) {
@@ -167,37 +201,44 @@ static SalPresenceModel * process_presence_notification(SalOp *op, belle_sip_req
return NULL;
if (body==NULL) return NULL;
-
- op->base.root->callbacks.parse_presence_requested(op,
+ if (!op->op_released){
+ op->base.root->callbacks.parse_presence_requested(op,
belle_sip_header_content_type_get_type(content_type),
belle_sip_header_content_type_get_subtype(content_type),
body,
&result);
+ }
return result;
}
-static void handle_notify(SalOp *op, belle_sip_request_t *req){
+static void handle_notify(SalOp *op, belle_sip_request_t *req, belle_sip_dialog_t *dialog){
belle_sip_response_t* resp=NULL;
belle_sip_server_transaction_t* server_transaction=op->pending_server_trans;
belle_sip_header_subscription_state_t* subscription_state_header=belle_sip_message_get_header_by_type(req,belle_sip_header_subscription_state_t);
SalSubscribeStatus sub_state;
-
+
if (strcmp("NOTIFY",belle_sip_request_get_method(req))==0) {
SalPresenceModel *presence_model = NULL;
const char* body = belle_sip_message_get_body(BELLE_SIP_MESSAGE(req));
+
+ if (op->dialog !=NULL && dialog != op->dialog){
+ ms_warning("Receiving a NOTIFY from a dialog we haven't stored (op->dialog=%p dialog=%p)", op->dialog, dialog);
+ }
if (!subscription_state_header || strcasecmp(BELLE_SIP_SUBSCRIPTION_STATE_TERMINATED,belle_sip_header_subscription_state_get_state(subscription_state_header)) ==0) {
sub_state=SalSubscribeTerminated;
ms_message("Outgoing subscription terminated by remote [%s]",sal_op_get_to(op));
} else {
- sub_state=SalSubscribeActive;
+ sub_state=belle_sip_message_get_subscription_state(BELLE_SIP_MESSAGE(req));
}
presence_model = process_presence_notification(op, req);
if (presence_model != NULL || body==NULL) {
/* Presence notification body parsed successfully. */
resp = sal_op_create_response_from_request(op, req, 200); /*create first because the op may be destroyed by notify_presence */
- op->base.root->callbacks.notify_presence(op, sub_state, presence_model, NULL);
+ if (!op->op_released){
+ op->base.root->callbacks.notify_presence(op, sub_state, presence_model, NULL);
+ }
} else if (body){
/* Formatting error in presence notification body. */
ms_warning("Wrongly formatted presence document.");
@@ -212,31 +253,55 @@ static void presence_process_request_event(void *op_base, const belle_sip_reques
belle_sip_server_transaction_t* server_transaction = belle_sip_provider_create_server_transaction(op->base.root->prov,belle_sip_request_event_get_request(event));
belle_sip_request_t* req = belle_sip_request_event_get_request(event);
belle_sip_dialog_state_t dialog_state;
- belle_sip_header_expires_t* expires = belle_sip_message_get_header_by_type(req,belle_sip_header_expires_t);
belle_sip_response_t* resp;
const char *method=belle_sip_request_get_method(req);
-
+ belle_sip_header_event_t *event_header;
belle_sip_object_ref(server_transaction);
if (op->pending_server_trans) belle_sip_object_unref(op->pending_server_trans);
op->pending_server_trans=server_transaction;
+ event_header=belle_sip_message_get_header_by_type(req,belle_sip_header_event_t);
+
+ if (event_header==NULL){
+ ms_warning("No event header in incoming SUBSCRIBE.");
+ resp=sal_op_create_response_from_request(op,req,400);
+ belle_sip_server_transaction_send_response(server_transaction,resp);
+ if (!op->dialog) sal_op_release(op);
+ return;
+ }
+ if (op->event==NULL) {
+ op->event=event_header;
+ belle_sip_object_ref(op->event);
+ }
if (!op->dialog) {
if (strcmp(method,"SUBSCRIBE")==0){
- op->dialog=belle_sip_provider_create_dialog(op->base.root->prov,BELLE_SIP_TRANSACTION(server_transaction));
- belle_sip_dialog_set_application_data(op->dialog,op);
- sal_op_ref(op);
+ belle_sip_dialog_t *dialog = belle_sip_provider_create_dialog(op->base.root->prov,BELLE_SIP_TRANSACTION(server_transaction));
+ if (!dialog){
+ resp=sal_op_create_response_from_request(op,req,481);
+ belle_sip_server_transaction_send_response(server_transaction,resp);
+ sal_op_release(op);
+ return;
+ }
+ set_or_update_dialog(op, dialog);
ms_message("new incoming subscription from [%s] to [%s]",sal_op_get_from(op),sal_op_get_to(op));
- }else{ /* this is a NOTIFY */
+ }else if (strcmp(method,"NOTIFY")==0 && belle_sip_request_event_get_dialog(event)) {
+ /*special case of dialog created by notify matching subscribe*/
+ set_or_update_dialog(op, belle_sip_request_event_get_dialog(event));
+ } else {/* this is a NOTIFY */
ms_message("Receiving out of dialog notify");
- handle_notify(op,req);
+ handle_notify(op, req, belle_sip_request_event_get_dialog(event));
return;
}
}
dialog_state=belle_sip_dialog_get_state(op->dialog);
switch(dialog_state) {
case BELLE_SIP_DIALOG_NULL: {
- op->base.root->callbacks.subscribe_presence_received(op,sal_op_get_from(op));
+ if (strcmp("NOTIFY",method)==0) {
+ handle_notify(op, req, belle_sip_request_event_get_dialog(event));
+ } else if (strcmp("SUBSCRIBE",method)==0) {
+ op->base.root->callbacks.subscribe_presence_received(op,sal_op_get_from(op));
+ }
break;
}
case BELLE_SIP_DIALOG_EARLY:
@@ -245,16 +310,13 @@ static void presence_process_request_event(void *op_base, const belle_sip_reques
case BELLE_SIP_DIALOG_CONFIRMED:
if (strcmp("NOTIFY",method)==0) {
- handle_notify(op,req);
+ handle_notify(op, req, belle_sip_request_event_get_dialog(event));
} else if (strcmp("SUBSCRIBE",method)==0) {
- /*either a refresh or an unsubscribe*/
- if (expires && belle_sip_header_expires_get_expires(expires)>0) {
- op->base.root->callbacks.subscribe_presence_received(op,sal_op_get_from(op));
- } else if(expires) {
- ms_message("Unsubscribe received from [%s]",sal_op_get_from(op));
- resp=sal_op_create_response_from_request(op,req,200);
- belle_sip_server_transaction_send_response(server_transaction,resp);
- }
+ /*either a refresh or an unsubscribe.
+ If it is a refresh there is nothing to notify to the app. If it is an unSUBSCRIBE, then the dialog
+ will be terminated shortly, and this will be notified to the app through the dialog_terminated callback.*/
+ resp=sal_op_create_response_from_request(op,req,200);
+ belle_sip_server_transaction_send_response(server_transaction,resp);
}
break;
default:
@@ -265,6 +327,17 @@ static void presence_process_request_event(void *op_base, const belle_sip_reques
static belle_sip_listener_callbacks_t op_presence_callbacks={0};
+/*Invoke when sal_op_release is called by upper layer*/
+static void sal_op_release_cb(struct SalOpBase* op_base) {
+ SalOp *op =(SalOp*)op_base;
+ if(op->refresher) {
+ belle_sip_refresher_stop(op->refresher);
+ belle_sip_object_unref(op->refresher);
+ op->refresher=NULL;
+ set_or_update_dialog(op,NULL); /*only if we have refresher. else dialog terminated event will remove association*/
+ }
+
+}
void sal_op_presence_fill_cbs(SalOp*op) {
if (op_presence_callbacks.process_request_event==NULL){
op_presence_callbacks.process_io_error=presence_process_io_error;
@@ -276,6 +349,7 @@ void sal_op_presence_fill_cbs(SalOp*op) {
}
op->callbacks=&op_presence_callbacks;
op->type=SalOpPresence;
+ op->base.release_cb=sal_op_release_cb;
}
@@ -300,14 +374,14 @@ int sal_subscribe_presence(SalOp *op, const char *from, const char *to, int expi
}
}
if (!op->event){
- op->event=belle_sip_header_create("Event","presence");
+ op->event=belle_sip_header_event_create("presence");
belle_sip_object_ref(op->event);
}
belle_sip_parameters_remove_parameter(BELLE_SIP_PARAMETERS(op->base.from_address),"tag");
belle_sip_parameters_remove_parameter(BELLE_SIP_PARAMETERS(op->base.to_address),"tag");
req=sal_op_build_request(op,"SUBSCRIBE");
if( req ){
- belle_sip_message_add_header(BELLE_SIP_MESSAGE(req),op->event);
+ belle_sip_message_add_header(BELLE_SIP_MESSAGE(req),BELLE_SIP_HEADER(op->event));
belle_sip_message_add_header(BELLE_SIP_MESSAGE(req),BELLE_SIP_HEADER(belle_sip_header_expires_create(expires)));
}
@@ -330,8 +404,8 @@ static int sal_op_check_dialog_state(SalOp *op) {
return -1;
} else
return 0;
-
}
+
int sal_notify_presence(SalOp *op, SalPresenceModel *presence){
belle_sip_request_t* notify=NULL;
if (sal_op_check_dialog_state(op)) {
@@ -348,6 +422,7 @@ int sal_notify_presence(SalOp *op, SalPresenceModel *presence){
int sal_notify_presence_close(SalOp *op){
belle_sip_request_t* notify=NULL;
+ int status;
if (sal_op_check_dialog_state(op)) {
return -1;
}
@@ -357,7 +432,9 @@ int sal_notify_presence_close(SalOp *op){
sal_add_presence_info(op,BELLE_SIP_MESSAGE(notify),NULL); /*FIXME, what about expires ??*/
belle_sip_message_add_header(BELLE_SIP_MESSAGE(notify)
,BELLE_SIP_HEADER(belle_sip_header_subscription_state_create(BELLE_SIP_SUBSCRIPTION_STATE_TERMINATED,-1)));
- return sal_op_send_request(op,notify);
+ status = sal_op_send_request(op,notify);
+ set_or_update_dialog(op,NULL); /*because we may be chalanged for the notify, so we must release dialog right now*/
+ return status;
}
diff --git a/coreapi/bellesip_sal/sal_op_publish.c b/coreapi/bellesip_sal/sal_op_publish.c
index b2f17c27e..7e832c6bb 100644
--- a/coreapi/bellesip_sal/sal_op_publish.c
+++ b/coreapi/bellesip_sal/sal_op_publish.c
@@ -22,20 +22,20 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
static void publish_refresher_listener (belle_sip_refresher_t* refresher
,void* user_pointer
,unsigned int status_code
- ,const char* reason_phrase) {
+ ,const char* reason_phrase, int will_retry) {
SalOp* op = (SalOp*)user_pointer;
const belle_sip_client_transaction_t* last_publish_trans=belle_sip_refresher_get_transaction(op->refresher);
- belle_sip_request_t* last_publish=belle_sip_transaction_get_request(BELLE_SIP_TRANSACTION(last_publish_trans));
belle_sip_response_t *response=belle_sip_transaction_get_response(BELLE_SIP_TRANSACTION(last_publish_trans));
- /*belle_sip_response_t* response=belle_sip_transaction_get_response(BELLE_SIP_TRANSACTION(belle_sip_refresher_get_transaction(refresher)));*/
ms_message("Publish refresher [%i] reason [%s] for proxy [%s]",status_code,reason_phrase?reason_phrase:"none",sal_op_get_proxy(op));
- if (status_code==412){
- /*resubmit the request after removing the SIP-If-Match*/
- belle_sip_message_remove_header((belle_sip_message_t*)last_publish,"SIP-If-Match");
- belle_sip_refresher_refresh(op->refresher,BELLE_SIP_REFRESHER_REUSE_EXPIRES);
- }else if (status_code==0){
+ if (status_code==0){
op->base.root->callbacks.on_expire(op);
}else if (status_code>=200){
+ belle_sip_header_t *sip_etag;
+ const char *sip_etag_string = NULL;
+ if (response && (sip_etag = belle_sip_message_get_header(BELLE_SIP_MESSAGE(response), "SIP-ETag"))) {
+ sip_etag_string = belle_sip_header_get_unparsed_value(sip_etag);
+ }
+ sal_op_set_entity_tag(op, sip_etag_string);
sal_error_info_set(&op->error_info,SalReasonUnknown,status_code,reason_phrase,NULL);
sal_op_assign_recv_headers(op,(belle_sip_message_t*)response);
op->base.root->callbacks.on_publish_response(op);
@@ -60,43 +60,7 @@ void sal_op_publish_fill_cbs(SalOp *op) {
op->type=SalOpPublish;
}
-/*
- * Sending a publish with 0 expires removes the event state and such request shall not contain a body.
- * See RFC3903, section 4.5
- */
-
-/*presence publish */
-int sal_publish_presence(SalOp *op, const char *from, const char *to, int expires, SalPresenceModel *presence){
- belle_sip_request_t *req=NULL;
- if(!op->refresher || !belle_sip_refresher_get_transaction(op->refresher)) {
- if (from)
- sal_op_set_from(op,from);
- if (to)
- sal_op_set_to(op,to);
-
- op->type=SalOpPublish;
- req=sal_op_build_request(op,"PUBLISH");
-
- if( req == NULL ){
- return -1;
- }
-
- if (sal_op_get_contact_address(op)){
- belle_sip_message_add_header(BELLE_SIP_MESSAGE(req),BELLE_SIP_HEADER(sal_op_create_contact(op)));
- }
- belle_sip_message_add_header(BELLE_SIP_MESSAGE(req),belle_sip_header_create("Event","presence"));
- sal_add_presence_info(op,BELLE_SIP_MESSAGE(req),presence);
- return sal_op_send_and_create_refresher(op,req,expires,publish_refresher_listener);
- } else {
- /*update presence status*/
- const belle_sip_client_transaction_t* last_publish_trans=belle_sip_refresher_get_transaction(op->refresher);
- belle_sip_request_t* last_publish=belle_sip_transaction_get_request(BELLE_SIP_TRANSACTION(last_publish_trans));
- sal_add_presence_info(op,BELLE_SIP_MESSAGE(last_publish),expires!=0 ? presence : NULL);
- return belle_sip_refresher_refresh(op->refresher,expires);
- }
-}
-
-int sal_publish(SalOp *op, const char *from, const char *to, const char *eventname, int expires, const SalBody *body){
+int sal_publish(SalOp *op, const char *from, const char *to, const char *eventname, int expires, const SalBodyHandler *body_handler){
belle_sip_request_t *req=NULL;
if(!op->refresher || !belle_sip_refresher_get_transaction(op->refresher)) {
if (from)
@@ -109,12 +73,16 @@ int sal_publish(SalOp *op, const char *from, const char *to, const char *eventna
if( req == NULL ){
return -1;
}
-
+
+ if (sal_op_get_entity_tag(op)) {
+ belle_sip_message_add_header(BELLE_SIP_MESSAGE(req),belle_sip_header_create("SIP-If-Match", sal_op_get_entity_tag(op)));
+ }
+
if (sal_op_get_contact_address(op)){
belle_sip_message_add_header(BELLE_SIP_MESSAGE(req),BELLE_SIP_HEADER(sal_op_create_contact(op)));
}
belle_sip_message_add_header(BELLE_SIP_MESSAGE(req),belle_sip_header_create("Event",eventname));
- sal_op_add_body(op,BELLE_SIP_MESSAGE(req),body);
+ belle_sip_message_set_body_handler(BELLE_SIP_MESSAGE(req), BELLE_SIP_BODY_HANDLER(body_handler));
if (expires!=-1)
return sal_op_send_and_create_refresher(op,req,expires,publish_refresher_listener);
else return sal_op_send_request(op,req);
@@ -123,7 +91,22 @@ int sal_publish(SalOp *op, const char *from, const char *to, const char *eventna
const belle_sip_client_transaction_t* last_publish_trans=belle_sip_refresher_get_transaction(op->refresher);
belle_sip_request_t* last_publish=belle_sip_transaction_get_request(BELLE_SIP_TRANSACTION(last_publish_trans));
/*update body*/
- sal_op_add_body(op,BELLE_SIP_MESSAGE(last_publish),expires!=0 ? body : NULL);
+ if (expires == 0) {
+ belle_sip_message_set_body(BELLE_SIP_MESSAGE(last_publish), NULL, 0);
+ } else {
+ belle_sip_message_set_body_handler(BELLE_SIP_MESSAGE(last_publish), BELLE_SIP_BODY_HANDLER(body_handler));
+ }
return belle_sip_refresher_refresh(op->refresher,expires==-1 ? BELLE_SIP_REFRESHER_REUSE_EXPIRES : expires);
}
}
+
+int sal_op_unpublish(SalOp *op){
+ if (op->refresher){
+ const belle_sip_transaction_t *tr=(const belle_sip_transaction_t*) belle_sip_refresher_get_transaction(op->refresher);
+ belle_sip_request_t *last_req=belle_sip_transaction_get_request(tr);
+ belle_sip_message_set_body(BELLE_SIP_MESSAGE(last_req), NULL, 0);
+ belle_sip_refresher_refresh(op->refresher,0);
+ return 0;
+ }
+ return -1;
+}
diff --git a/coreapi/bellesip_sal/sal_op_registration.c b/coreapi/bellesip_sal/sal_op_registration.c
index 480ee08ad..72b57b773 100644
--- a/coreapi/bellesip_sal/sal_op_registration.c
+++ b/coreapi/bellesip_sal/sal_op_registration.c
@@ -22,11 +22,11 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
static void register_refresher_listener (belle_sip_refresher_t* refresher
,void* user_pointer
,unsigned int status_code
- ,const char* reason_phrase) {
+ ,const char* reason_phrase, int will_retry) {
SalOp* op = (SalOp*)user_pointer;
belle_sip_response_t* response=belle_sip_transaction_get_response(BELLE_SIP_TRANSACTION(belle_sip_refresher_get_transaction(refresher)));
- ms_message("Register refresher [%i] reason [%s] for proxy [%s]",status_code,reason_phrase,sal_op_get_proxy(op));
-
+ ms_message("Register refresher [%i] reason [%s] for proxy [%s]",status_code,reason_phrase,sal_op_get_proxy(op));
+
if (belle_sip_refresher_get_auth_events(refresher)) {
if (op->auth_info) sal_auth_info_delete(op->auth_info);
/*only take first one for now*/
@@ -46,7 +46,7 @@ static void register_refresher_listener (belle_sip_refresher_t* refresher
}
sal_op_set_service_route(op,(const SalAddress*)service_route_address);
if (service_route_address) belle_sip_object_unref(service_route_address);
-
+
sal_remove_pending_auth(op->base.root,op); /*just in case*/
if (contact) {
sal_op_set_contact_address(op,(SalAddress*)(BELLE_SIP_HEADER_ADDRESS(contact))); /*update contact with real value*/
@@ -79,13 +79,13 @@ int sal_register(SalOp *op, const char *proxy, const char *from, int expires,Sal
belle_sip_request_t *req;
belle_sip_uri_t* req_uri;
belle_sip_header_t* accept_header;
-
+
if (op->refresher){
belle_sip_refresher_stop(op->refresher);
belle_sip_object_unref(op->refresher);
op->refresher=NULL;
}
-
+
op->type=SalOpRegister;
sal_op_set_from(op,from);
sal_op_set_to(op,from);
diff --git a/coreapi/bellesip_sal/sal_sdp.c b/coreapi/bellesip_sal/sal_sdp.c
index 69a28a8e9..590f7a440 100644
--- a/coreapi/bellesip_sal/sal_sdp.c
+++ b/coreapi/bellesip_sal/sal_sdp.c
@@ -1,3 +1,4 @@
+
/*
linphone
Copyright (C) 2012 Belledonne Communications, Grenoble, France
@@ -69,7 +70,7 @@ static void add_ice_remote_candidates(belle_sdp_media_description_t *md, const S
}
static bool_t is_rtcp_fb_trr_int_the_same_for_all_payloads(const SalStreamDescription *stream, uint16_t *trr_int) {
- MSList *pt_it;
+ 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;
@@ -118,14 +119,14 @@ static void add_rtcp_fb_ccm_attribute(belle_sdp_media_description_t *media_desc,
}
static void add_rtcp_fb_attributes(belle_sdp_media_description_t *media_desc, const SalMediaDescription *md, const SalStreamDescription *stream) {
- MSList *pt_it;
+ 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) {
+ 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) {
@@ -143,7 +144,7 @@ static void add_rtcp_fb_attributes(belle_sdp_media_description_t *media_desc, co
avpf_params = payload_type_get_avpf_params(pt);
/* Add trr-int if not set generally. */
- if (general_trr_int != TRUE) {
+ if (general_trr_int != TRUE && trr_int != 0) {
add_rtcp_fb_trr_int_attribute(media_desc, payload_type_get_number(pt), avpf_params.trr_interval);
}
@@ -190,7 +191,7 @@ static void stream_description_to_sdp ( belle_sdp_session_description_t *session
belle_sdp_mime_parameter_t* mime_param;
belle_sdp_media_description_t* media_desc;
int j;
- MSList* pt_it;
+ bctbx_list_t* pt_it;
PayloadType* pt;
char buffer[1024];
char* dir=NULL;
@@ -250,7 +251,7 @@ static void stream_description_to_sdp ( belle_sdp_session_description_t *session
if ( stream->bandwidth>0 )
belle_sdp_media_description_set_bandwidth ( media_desc,"AS",stream->bandwidth );
- if ((stream->proto == SalProtoRtpSavpf) || (stream->proto == SalProtoRtpSavp)) {
+ if (sal_stream_description_has_srtp(stream)) {
/* add crypto lines */
for ( j=0; jhaveZrtpHash == 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";*/
@@ -304,6 +310,10 @@ static void stream_description_to_sdp ( belle_sdp_session_description_t *session
}
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 ) );
+ }
+
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)) {
@@ -315,7 +325,7 @@ static void stream_description_to_sdp ( belle_sdp_session_description_t *session
belle_sdp_media_description_add_attribute(media_desc,belle_sdp_attribute_create ("rtcp",buffer));
}
}
- if (stream->ice_completed == TRUE) {
+ if (stream->set_nortpproxy == TRUE) {
belle_sdp_media_description_add_attribute(media_desc,belle_sdp_attribute_create ("nortpproxy","yes"));
}
if (stream->ice_mismatch == TRUE) {
@@ -331,7 +341,7 @@ static void stream_description_to_sdp ( belle_sdp_session_description_t *session
}
}
- if ((rtp_port != 0) && ((stream->proto == SalProtoRtpAvpf) || (stream->proto == SalProtoRtpSavpf))) {
+ if ((rtp_port != 0) && (sal_stream_description_has_avpf(stream) || sal_stream_description_has_implicit_avpf(stream))) {
add_rtcp_fb_attributes(media_desc, md, stream);
}
@@ -355,6 +365,16 @@ static void stream_description_to_sdp ( belle_sdp_session_description_t *session
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
@@ -408,13 +428,21 @@ belle_sdp_session_description_t * media_description_to_sdp ( const SalMediaDescr
belle_sdp_session_description_set_bandwidth ( session_desc,"AS",desc->bandwidth );
}
- if (desc->ice_completed == TRUE) belle_sdp_session_description_add_attribute(session_desc, belle_sdp_attribute_create("nortpproxy","yes"));
+ 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->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]);
@@ -442,7 +470,7 @@ static void sdp_parse_payload_types(belle_sdp_media_description_t *media_desc, S
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=ms_list_append ( stream->payloads,pt );
+ stream->payloads=bctbx_list_append ( stream->payloads,pt );
stream->ptime=belle_sdp_mime_parameter_get_ptime ( mime_param );
ms_message ( "Found payload %s/%i fmtp=%s",pt->mime_type,pt->clock_rate,
pt->send_fmtp ? pt->send_fmtp : "" );
@@ -511,7 +539,7 @@ static void sdp_parse_media_ice_parameters(belle_sdp_media_description_t *media_
att_name = belle_sdp_attribute_get_name(attribute);
value = belle_sdp_attribute_get_value(attribute);
- if ( (nb_ice_candidates < sizeof (stream->ice_candidates)/sizeof(SalIceCandidate))
+ 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];
@@ -520,7 +548,10 @@ static void sdp_parse_media_ice_parameters(belle_sdp_media_description_t *media_
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 memset(candidate, 0, sizeof(*candidate));
+ 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;
@@ -549,7 +580,7 @@ static void sdp_parse_media_ice_parameters(belle_sdp_media_description_t *media_
}
static void enable_avpf_for_stream(SalStreamDescription *stream) {
- MSList *pt_it;
+ 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);
@@ -607,14 +638,15 @@ static void apply_rtcp_fb_attribute_to_payload(belle_sdp_rtcp_fb_attribute_t *fb
payload_type_set_avpf_params(pt, avpf_params);
}
-static void sdp_parse_rtcp_fb_parameters(belle_sdp_media_description_t *media_desc, SalStreamDescription *stream) {
+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;
- MSList *pt_it;
+ 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);
@@ -624,6 +656,7 @@ static void sdp_parse_rtcp_fb_parameters(belle_sdp_media_description_t *media_de
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;
}
}
}
@@ -637,12 +670,14 @@ static void sdp_parse_rtcp_fb_parameters(belle_sdp_media_description_t *media_de
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) {
@@ -702,8 +737,10 @@ static SalStreamDescription * sdp_to_stream_description(SalMediaDescription *md,
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 );
@@ -739,6 +776,8 @@ static SalStreamDescription * sdp_to_stream_description(SalMediaDescription *md,
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 );
@@ -760,6 +799,8 @@ static SalStreamDescription * sdp_to_stream_description(SalMediaDescription *md,
stream->dir=md->dir; /*takes default value if not present*/
}
+ stream->rtcp_mux = belle_sdp_media_description_get_attribute(media_desc, "rtcp-mux") != NULL;
+
/* Get media payload types */
sdp_parse_payload_types(media_desc, stream);
@@ -797,23 +838,41 @@ static SalStreamDescription * sdp_to_stream_description(SalMediaDescription *md,
}
/* Read crypto lines if any */
- if ((stream->proto == SalProtoRtpSavpf) || (stream->proto == SalProtoRtpSavp)) {
+ 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;
+ }
+ }
+
/* 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 ((stream->proto == SalProtoRtpAvpf) || (stream->proto == SalProtoRtpSavpf)) {
+ if (sal_stream_description_has_avpf(stream)) {
enable_avpf_for_stream(stream);
- sdp_parse_rtcp_fb_parameters(media_desc, 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 */
+ 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;
+ stream->custom_sdp_attributes = sal_custom_sdp_attribute_append(stream->custom_sdp_attributes, belle_sdp_attribute_get_name(attr), belle_sdp_attribute_get_value(attr));
+ }
+
md->nb_streams++;
return stream;
}
@@ -824,6 +883,7 @@ int sdp_to_media_description ( belle_sdp_session_description_t *session_desc, S
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;
@@ -885,6 +945,12 @@ int sdp_to_media_description ( belle_sdp_session_description_t *session_desc, S
/* Get session RTCP-XR attributes if any */
sdp_parse_session_rtcp_xr_parameters(session_desc, &desc->rtcp_xr);
+ /* Get the custom attributes */
+ 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));
+ }
+
for ( media_desc_it=belle_sdp_session_description_get_media_descriptions ( session_desc )
; media_desc_it!=NULL
; media_desc_it=media_desc_it->next ) {
diff --git a/coreapi/buffer.h b/coreapi/buffer.h
index 718b577a1..8bc9d6b85 100644
--- a/coreapi/buffer.h
+++ b/coreapi/buffer.h
@@ -144,4 +144,4 @@ LINPHONE_PUBLIC bool_t linphone_buffer_is_empty(const LinphoneBuffer *buffer);
}
#endif
-#endif /* LINPHONE_CONTENT_H_ */
+#endif /* LINPHONE_BUFFER_H_ */
diff --git a/coreapi/call_log.c b/coreapi/call_log.c
index 7109e2557..a5150e9bd 100644
--- a/coreapi/call_log.c
+++ b/coreapi/call_log.c
@@ -22,6 +22,20 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#include
#include "private.h"
+#ifdef SQLITE_STORAGE_ENABLED
+#ifndef _WIN32
+#if !defined(ANDROID) && !defined(__QNXNTO__)
+# include
+# include
+# include
+#endif
+#else
+#include
+#endif
+
+#define MAX_PATH_SIZE 1024
+#include "sqlite3.h"
+#endif
/*******************************************************************************
* Internal functions *
@@ -60,13 +74,15 @@ static void set_call_log_date(LinphoneCallLog *cl, time_t start_time){
******************************************************************************/
void call_logs_write_to_config_file(LinphoneCore *lc){
- MSList *elem;
+ 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;
@@ -133,7 +149,7 @@ void call_logs_read_from_config_file(LinphoneCore *lc){
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);
- lc->call_logs=ms_list_append(lc->call_logs,cl);
+ lc->call_logs=bctbx_list_append(lc->call_logs,cl);
}else break;
}
}
@@ -234,6 +250,10 @@ bool_t linphone_call_log_video_enabled(LinphoneCallLog *cl) {
return cl->video_enabled;
}
+bool_t linphone_call_log_was_conference(LinphoneCallLog *cl) {
+ return cl->was_conference;
+}
+
/*******************************************************************************
* Reference and user data handling functions *
@@ -260,16 +280,17 @@ void linphone_call_log_unref(LinphoneCallLog *cl) {
* Constructor and destructor functions *
******************************************************************************/
-static void _linphone_call_log_destroy(LinphoneCallLog *cl){
+static void _linphone_call_log_destroy(LinphoneCallLog *cl) {
if (cl->from!=NULL) linphone_address_destroy(cl->from);
if (cl->to!=NULL) linphone_address_destroy(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]);
}
-LinphoneCallLog * linphone_call_log_new(LinphoneCallDir dir, LinphoneAddress *from, LinphoneAddress *to){
+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);
@@ -278,9 +299,11 @@ LinphoneCallLog * linphone_call_log_new(LinphoneCallDir dir, LinphoneAddress *fr
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;
}
@@ -298,3 +321,408 @@ BELLE_SIP_INSTANCIATE_VPTR(LinphoneCallLog, belle_sip_object_t,
NULL, // marshal
FALSE
);
+
+
+/*******************************************************************************
+ * SQL storage related functions *
+ ******************************************************************************/
+
+#ifdef SQLITE_STORAGE_ENABLED
+
+static void linphone_create_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_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;
+ }
+}
+
+/* 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) {
+ bctbx_list_t **list = (bctbx_list_t **)data;
+ LinphoneAddress *from;
+ LinphoneAddress *to;
+ LinphoneCallDir dir;
+ LinphoneCallLog *log;
+
+ unsigned int storage_id = (unsigned int)atoi(argv[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]);
+ }
+ }
+
+ *list = bctbx_list_append(*list, log);
+ return 0;
+
+error:
+ if (from){
+ linphone_address_destroy(from);
+ }
+ if (to){
+ linphone_address_destroy(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, bctbx_list_t **list) {
+ char* errmsg = NULL;
+ int ret;
+ ret = sqlite3_exec(db, stmt, create_call_log, list, &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));
+ }
+}
+
+static void copy_user_data_from_existing_log(bctbx_list_t *existing_logs, LinphoneCallLog *log) {
+ while (existing_logs) {
+ LinphoneCallLog *existing_log = (LinphoneCallLog *)existing_logs->data;
+ if (existing_log->storage_id == log->storage_id) {
+ log->user_data = existing_log->user_data;
+ break;
+ }
+ existing_logs = bctbx_list_next(existing_logs);
+ }
+}
+
+static void copy_user_data_from_existing_logs(bctbx_list_t *existing_logs, bctbx_list_t *new_logs) {
+ while (new_logs) {
+ LinphoneCallLog *new_log = (LinphoneCallLog *)new_logs->data;
+ copy_user_data_from_existing_log(existing_logs, new_log);
+ new_logs = bctbx_list_next(new_logs);
+ }
+}
+
+const bctbx_list_t *linphone_core_get_call_history(LinphoneCore *lc) {
+ char *buf;
+ uint64_t begin,end;
+ bctbx_list_t *result = NULL;
+
+ if (!lc || lc->logs_db == NULL) return NULL;
+
+ 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");
+ }
+
+ begin = ortp_get_cur_time_ms();
+ linphone_sql_request_call_log(lc->logs_db, buf, &result);
+ end = ortp_get_cur_time_ms();
+ ms_message("%s(): completed in %i ms",__FUNCTION__, (int)(end-begin));
+ sqlite3_free(buf);
+
+ if (lc->call_logs) {
+ copy_user_data_from_existing_logs(lc->call_logs, result);
+ }
+
+ lc->call_logs = bctbx_list_free_with_data(lc->call_logs, (void (*)(void*))linphone_call_log_unref);
+ lc->call_logs = 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 || lc->logs_db == NULL) return 0;
+
+ 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;
+ bctbx_list_t *result = NULL;
+
+ 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
+
+ begin = ortp_get_cur_time_ms();
+ linphone_sql_request_call_log(lc->logs_db, buf, &result);
+ end = ortp_get_cur_time_ms();
+ ms_message("%s(): completed in %i ms",__FUNCTION__, (int)(end-begin));
+ sqlite3_free(buf);
+ ms_free(sipAddress);
+
+ if (lc->call_logs) {
+ copy_user_data_from_existing_logs(lc->call_logs, result);
+ }
+
+ return result;
+}
+
+LinphoneCallLog * linphone_core_get_last_outgoing_call_log(LinphoneCore *lc) {
+ char *buf;
+ uint64_t begin,end;
+ bctbx_list_t *list = NULL;
+ 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");
+
+ begin = ortp_get_cur_time_ms();
+ linphone_sql_request_call_log(lc->logs_db, buf, &list);
+ end = ortp_get_cur_time_ms();
+ ms_message("%s(): completed in %i ms",__FUNCTION__, (int)(end-begin));
+ sqlite3_free(buf);
+
+ if (list) {
+ result = (LinphoneCallLog*)list->data;
+ }
+
+ if (lc->call_logs && result) {
+ copy_user_data_from_existing_log(lc->call_logs, result);
+ }
+
+ return result;
+}
+
+LinphoneCallLog * linphone_core_find_call_log_from_call_id(LinphoneCore *lc, const char *call_id) {
+ char *buf;
+ uint64_t begin,end;
+ bctbx_list_t *list = NULL;
+ 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);
+
+ begin = ortp_get_cur_time_ms();
+ linphone_sql_request_call_log(lc->logs_db, buf, &list);
+ end = ortp_get_cur_time_ms();
+ ms_message("%s(): completed in %i ms",__FUNCTION__, (int)(end-begin));
+ sqlite3_free(buf);
+
+ if (list) {
+ result = (LinphoneCallLog*)list->data;
+ }
+
+ if (lc->call_logs && result) {
+ copy_user_data_from_existing_log(lc->call_logs, result);
+ }
+
+ return result;
+}
+
+#else
+
+void linphone_core_call_log_storage_init(LinphoneCore *lc) {
+}
+
+void linphone_core_call_log_storage_close(LinphoneCore *lc) {
+}
+
+void linphone_core_store_call_log(LinphoneCore *lc, LinphoneCallLog *log) {
+}
+
+const bctbx_list_t *linphone_core_get_call_history(LinphoneCore *lc) {
+ return NULL;
+}
+
+void linphone_core_delete_call_history(LinphoneCore *lc) {
+}
+
+void linphone_core_delete_call_log(LinphoneCore *lc, LinphoneCallLog *log) {
+}
+
+int linphone_core_get_call_history_size(LinphoneCore *lc) {
+ return 0;
+}
+
+bctbx_list_t * linphone_core_get_call_history_for_address(LinphoneCore *lc, const LinphoneAddress *addr) {
+ return NULL;
+}
+
+LinphoneCallLog * linphone_core_get_last_outgoing_call_log(LinphoneCore *lc) {
+ return NULL;
+}
+
+LinphoneCallLog * linphone_core_find_call_log_from_call_id(LinphoneCore *lc, const char *call_id) {
+ return NULL;
+}
+
+#endif
diff --git a/coreapi/call_log.h b/coreapi/call_log.h
index da9cdfb6b..50301487a 100644
--- a/coreapi/call_log.h
+++ b/coreapi/call_log.h
@@ -179,6 +179,13 @@ LINPHONE_PUBLIC bool_t linphone_call_log_video_enabled(LinphoneCallLog *cl);
**/
LINPHONE_PUBLIC char * linphone_call_log_to_str(LinphoneCallLog *cl);
+/**
+ * Tells whether that call was a call to a conference server
+ * @param[in] cl #LinphoneCallLog object
+ * @return TRUE if the call was a call to a conference server
+ */
+LINPHONE_PUBLIC bool_t linphone_call_log_was_conference(LinphoneCallLog *cl);
+
/*******************************************************************************
* Reference and user data handling functions *
diff --git a/coreapi/call_params.c b/coreapi/call_params.c
index 5bd95550e..cffc6d8dc 100644
--- a/coreapi/call_params.c
+++ b/coreapi/call_params.c
@@ -72,6 +72,36 @@ SalStreamDir get_video_dir_from_call_params(const LinphoneCallParams *params) {
return sal_dir_from_call_params_dir(linphone_call_params_get_video_direction(params));
}
+void linphone_call_params_set_custom_headers(LinphoneCallParams *params, const SalCustomHeader *ch){
+ if (params->custom_headers){
+ sal_custom_header_free(params->custom_headers);
+ params->custom_headers = NULL;
+ }
+ if (ch){
+ params->custom_headers = sal_custom_header_clone(ch);
+ }
+}
+
+void linphone_call_params_set_custom_sdp_attributes(LinphoneCallParams *params, const SalCustomSdpAttribute *csa) {
+ if (params->custom_sdp_attributes) {
+ sal_custom_sdp_attribute_free(params->custom_sdp_attributes);
+ params->custom_sdp_attributes = NULL;
+ }
+ if (csa) {
+ params->custom_sdp_attributes = sal_custom_sdp_attribute_clone(csa);
+ }
+}
+
+void linphone_call_params_set_custom_sdp_media_attributes(LinphoneCallParams *params, LinphoneStreamType type, const SalCustomSdpAttribute *csa) {
+ if (params->custom_sdp_media_attributes[type]) {
+ sal_custom_sdp_attribute_free(params->custom_sdp_media_attributes[type]);
+ params->custom_sdp_media_attributes[type] = NULL;
+ }
+ if (csa) {
+ params->custom_sdp_media_attributes[type] = sal_custom_sdp_attribute_clone(csa);
+ }
+}
+
/*******************************************************************************
* Public functions *
@@ -81,7 +111,24 @@ void linphone_call_params_add_custom_header(LinphoneCallParams *params, const ch
params->custom_headers=sal_custom_header_append(params->custom_headers,header_name,header_value);
}
+void linphone_call_params_add_custom_sdp_attribute(LinphoneCallParams *params, const char *attribute_name, const char *attribute_value) {
+ params->custom_sdp_attributes = sal_custom_sdp_attribute_append(params->custom_sdp_attributes, attribute_name, attribute_value);
+}
+
+void linphone_call_params_add_custom_sdp_media_attribute(LinphoneCallParams *params, LinphoneStreamType type, const char *attribute_name, const char *attribute_value) {
+ params->custom_sdp_media_attributes[type] = sal_custom_sdp_attribute_append(params->custom_sdp_media_attributes[type], attribute_name, attribute_value);
+}
+
+void linphone_call_params_clear_custom_sdp_attributes(LinphoneCallParams *params) {
+ linphone_call_params_set_custom_sdp_attributes(params, NULL);
+}
+
+void linphone_call_params_clear_custom_sdp_media_attributes(LinphoneCallParams *params, LinphoneStreamType type) {
+ linphone_call_params_set_custom_sdp_media_attributes(params, type, NULL);
+}
+
LinphoneCallParams * linphone_call_params_copy(const LinphoneCallParams *cp){
+ unsigned int i;
LinphoneCallParams *ncp=linphone_call_params_new();
memcpy(ncp,cp,sizeof(LinphoneCallParams));
if (cp->record_file) ncp->record_file=ms_strdup(cp->record_file);
@@ -90,6 +137,10 @@ LinphoneCallParams * linphone_call_params_copy(const LinphoneCallParams *cp){
* The management of the custom headers is not optimal. We copy everything while ref counting would be more efficient.
*/
if (cp->custom_headers) ncp->custom_headers=sal_custom_header_clone(cp->custom_headers);
+ if (cp->custom_sdp_attributes) ncp->custom_sdp_attributes = sal_custom_sdp_attribute_clone(cp->custom_sdp_attributes);
+ for (i = 0; i < (unsigned int)LinphoneStreamTypeUnknown; i++) {
+ if (cp->custom_sdp_media_attributes[i]) ncp->custom_sdp_media_attributes[i] = sal_custom_sdp_attribute_clone(cp->custom_sdp_media_attributes[i]);
+ }
return ncp;
}
@@ -106,6 +157,17 @@ void linphone_call_params_enable_low_bandwidth(LinphoneCallParams *cp, bool_t en
cp->low_bandwidth=enabled;
}
+void linphone_call_params_enable_audio(LinphoneCallParams *cp, bool_t enabled){
+ cp->has_audio=enabled;
+ if (enabled && cp->audio_dir==LinphoneMediaDirectionInactive)
+ cp->audio_dir=LinphoneMediaDirectionSendRecv;
+}
+
+int linphone_call_params_enable_realtime_text(LinphoneCallParams *params, bool_t yesno) {
+ params->realtimetext_enabled=yesno;
+ return 0;
+}
+
void linphone_call_params_enable_video(LinphoneCallParams *cp, bool_t enabled){
cp->has_video=enabled;
if (enabled && cp->video_dir==LinphoneMediaDirectionInactive)
@@ -116,6 +178,14 @@ const char *linphone_call_params_get_custom_header(const LinphoneCallParams *par
return sal_custom_header_find(params->custom_headers,header_name);
}
+const char * linphone_call_params_get_custom_sdp_attribute(const LinphoneCallParams *params, const char *attribute_name) {
+ return sal_custom_sdp_attribute_find(params->custom_sdp_attributes, attribute_name);
+}
+
+const char * linphone_call_params_get_custom_sdp_media_attribute(const LinphoneCallParams *params, LinphoneStreamType type, const char *attribute_name) {
+ return sal_custom_sdp_attribute_find(params->custom_sdp_media_attributes[type], attribute_name);
+}
+
bool_t linphone_call_params_get_local_conference_mode(const LinphoneCallParams *cp){
return cp->in_conference;
}
@@ -164,6 +234,11 @@ const LinphonePayloadType* linphone_call_params_get_used_video_codec(const Linph
return cp->video_codec;
}
+const LinphonePayloadType* linphone_call_params_get_used_text_codec(const LinphoneCallParams *cp) {
+ return cp->text_codec;
+}
+
+
bool_t linphone_call_params_low_bandwidth_enabled(const LinphoneCallParams *cp) {
return cp->low_bandwidth;
}
@@ -196,6 +271,14 @@ void linphone_call_params_set_session_name(LinphoneCallParams *cp, const char *n
if (name) cp->session_name=ms_strdup(name);
}
+bool_t linphone_call_params_audio_enabled(const LinphoneCallParams *cp){
+ return cp->has_audio;
+}
+
+bool_t linphone_call_params_realtime_text_enabled(const LinphoneCallParams *params) {
+ return params->realtimetext_enabled;
+}
+
bool_t linphone_call_params_video_enabled(const LinphoneCallParams *cp){
return cp->has_video;
}
@@ -258,14 +341,22 @@ bool_t linphone_call_params_video_multicast_enabled(const LinphoneCallParams *pa
******************************************************************************/
static void _linphone_call_params_destroy(LinphoneCallParams *cp){
+ unsigned int i;
if (cp->record_file) ms_free(cp->record_file);
if (cp->custom_headers) sal_custom_header_free(cp->custom_headers);
+ if (cp->custom_sdp_attributes) sal_custom_sdp_attribute_free(cp->custom_sdp_attributes);
+ for (i = 0; i < (unsigned int)LinphoneStreamTypeUnknown; i++) {
+ if (cp->custom_sdp_media_attributes[i]) sal_custom_sdp_attribute_free(cp->custom_sdp_media_attributes[i]);
+ }
+ if (cp->session_name) ms_free(cp->session_name);
}
LinphoneCallParams * linphone_call_params_new(void) {
LinphoneCallParams *cp=belle_sip_object_new(LinphoneCallParams);
cp->audio_dir=LinphoneMediaDirectionSendRecv;
cp->video_dir=LinphoneMediaDirectionSendRecv;
+ cp->has_audio=TRUE;
+ cp->realtimetext_enabled = FALSE;
return cp;
}
diff --git a/coreapi/call_params.h b/coreapi/call_params.h
index 08f0c9d5f..84bef3f3f 100644
--- a/coreapi/call_params.h
+++ b/coreapi/call_params.h
@@ -101,6 +101,13 @@ LINPHONE_PUBLIC void linphone_call_params_enable_early_media_sending(LinphoneCal
**/
LINPHONE_PUBLIC void linphone_call_params_enable_low_bandwidth(LinphoneCallParams *cp, bool_t enabled);
+/**
+ * Enable audio stream.
+ * @param[in] cp LinphoneCallParams object
+ * @param[in] enabled A boolean value telling whether to enable audio or not.
+**/
+LINPHONE_PUBLIC void linphone_call_params_enable_audio(LinphoneCallParams *cp, bool_t enabled);
+
/**
* Enable video stream.
* @param[in] cp LinphoneCallParams object
@@ -118,6 +125,10 @@ LINPHONE_PUBLIC const char *linphone_call_params_get_custom_header(const Linphon
/**
* Tell whether the call is part of the locally managed conference.
+ * @warning If a conference server is used to manage conferences,
+ * that function does not return TRUE even if the conference is running.
+ * If you want to test whether the conference is running, you should test
+ * whether linphone_core_get_conference() return a non-null pointer.
* @param[in] cp LinphoneCallParams object
* @return A boolean value telling whether the call is part of the locally managed conference.
**/
@@ -201,6 +212,13 @@ LINPHONE_PUBLIC const LinphonePayloadType* linphone_call_params_get_used_audio_c
**/
LINPHONE_PUBLIC const LinphonePayloadType* linphone_call_params_get_used_video_codec(const LinphoneCallParams *cp);
+/**
+ * Get the text codec used in the call, described as a LinphonePayloadType structure.
+ * @param[in] cp LinphoneCallParams object
+ * @return The LinphonePayloadType object corresponding to the text codec being used in the call.
+**/
+LINPHONE_PUBLIC const LinphonePayloadType* linphone_call_params_get_used_text_codec(const LinphoneCallParams *cp);
+
/**
* Tell whether the call has been configured in low bandwidth mode or not.
* This mode can be automatically discovered thanks to a stun server when activate_edge_workarounds=1 in section [net] of configuration file.
@@ -233,7 +251,7 @@ LINPHONE_PUBLIC void linphone_call_params_set_media_encryption(LinphoneCallParam
* @param[in] cp LinphoneCallParams object
* @param[in] privacy The privacy mode to used for the call.
**/
-LINPHONE_PUBLIC void linphone_call_params_set_privacy(LinphoneCallParams *params, LinphonePrivacyMask privacy);
+LINPHONE_PUBLIC void linphone_call_params_set_privacy(LinphoneCallParams *cp, LinphonePrivacyMask privacy);
/**
* Enable recording of the call.
@@ -254,6 +272,13 @@ LINPHONE_PUBLIC void linphone_call_params_set_record_file(LinphoneCallParams *cp
**/
LINPHONE_PUBLIC void linphone_call_params_set_session_name(LinphoneCallParams *cp, const char *name);
+/**
+ * Tell whether audio is enabled or not.
+ * @param[in] cp LinphoneCallParams object
+ * @return A boolean value telling whether audio is enabled or not.
+**/
+LINPHONE_PUBLIC bool_t linphone_call_params_audio_enabled(const LinphoneCallParams *cp);
+
/**
* Tell whether video is enabled or not.
* @param[in] cp LinphoneCallParams object
@@ -263,29 +288,29 @@ LINPHONE_PUBLIC bool_t linphone_call_params_video_enabled(const LinphoneCallPara
/**
* Get the audio stream direction.
- * @param[in] cl LinphoneCallParams object
+ * @param[in] cp LinphoneCallParams object
* @return The audio stream direction associated with the call params.
**/
LINPHONE_PUBLIC LinphoneMediaDirection linphone_call_params_get_audio_direction(const LinphoneCallParams *cp);
/**
* Get the video stream direction.
- * @param[in] cl LinphoneCallParams object
+ * @param[in] cp LinphoneCallParams object
* @return The video stream direction associated with the call params.
**/
LINPHONE_PUBLIC LinphoneMediaDirection linphone_call_params_get_video_direction(const LinphoneCallParams *cp);
/**
- * Set the audio stream direction. Only relevant for multicast
- * @param[in] cl LinphoneCallParams object
- * @param[in] The audio stream direction associated with this call params.
+ * Set the audio stream direction.
+ * @param[in] cp LinphoneCallParams object
+ * @param[in] dir The audio stream direction associated with this call params.
**/
LINPHONE_PUBLIC void linphone_call_params_set_audio_direction(LinphoneCallParams *cp, LinphoneMediaDirection dir);
/**
- * Set the video stream direction. Only relevant for multicast
- * @param[in] cl LinphoneCallParams object
- * @param[in] The video stream direction associated with this call params.
+ * Set the video stream direction.
+ * @param[in] cp LinphoneCallParams object
+ * @param[in] dir The video stream direction associated with this call params.
**/
LINPHONE_PUBLIC void linphone_call_params_set_video_direction(LinphoneCallParams *cp, LinphoneMediaDirection dir);
@@ -296,28 +321,28 @@ LINPHONE_PUBLIC void linphone_call_params_set_video_direction(LinphoneCallParams
/**
* Get the user data associated with the call params.
- * @param[in] cl LinphoneCallParams object
+ * @param[in] cp LinphoneCallParams object
* @return The user data associated with the call params.
**/
LINPHONE_PUBLIC void *linphone_call_params_get_user_data(const LinphoneCallParams *cp);
/**
* Assign a user data to the call params.
- * @param[in] cl LinphoneCallParams object
+ * @param[in] cp LinphoneCallParams object
* @param[in] ud The user data to associate with the call params.
**/
LINPHONE_PUBLIC void linphone_call_params_set_user_data(LinphoneCallParams *cp, void *ud);
/**
* Acquire a reference to the call params.
- * @param[in] cl LinphoneCallParams object
+ * @param[in] cp LinphoneCallParams object
* @return The same LinphoneCallParams object
**/
LINPHONE_PUBLIC LinphoneCallParams * linphone_call_params_ref(LinphoneCallParams *cp);
/**
* Release a reference to the call params.
- * @param[in] cl LinphoneCallParams object
+ * @param[in] cp LinphoneCallParams object
**/
LINPHONE_PUBLIC void linphone_call_params_unref(LinphoneCallParams *cp);
@@ -326,36 +351,107 @@ LINPHONE_PUBLIC void linphone_call_params_unref(LinphoneCallParams *cp);
* Use to enable multicast rtp for audio stream.
* * If enabled, outgoing calls put a multicast address from #linphone_core_get_video_multicast_addr into audio cline. In case of outgoing call audio stream is sent to this multicast address.
*
For incoming calls behavior is unchanged.
- * @param core #LinphoneCallParams
+ * @param params #LinphoneCallParams
* @param yesno if yes, subsequent calls will propose multicast ip set by #linphone_core_set_audio_multicast_addr
* @ingroup media_parameters
**/
-LINPHONE_PUBLIC void linphone_call_params_enable_audio_multicast(LinphoneCallParams *param, bool_t yesno);
+LINPHONE_PUBLIC void linphone_call_params_enable_audio_multicast(LinphoneCallParams *params, bool_t yesno);
/**
* Use to get multicast state of audio stream.
- * @param core #LinphoneCallParams
+ * @param params #LinphoneCallParams
* @return true if subsequent calls will propose multicast ip set by #linphone_core_set_audio_multicast_addr
* @ingroup media_parameters
**/
-LINPHONE_PUBLIC bool_t linphone_call_params_audio_multicast_enabled(const LinphoneCallParams *param);
+LINPHONE_PUBLIC bool_t linphone_call_params_audio_multicast_enabled(const LinphoneCallParams *params);
/**
* Use to enable multicast rtp for video stream.
* If enabled, outgoing calls put a multicast address from #linphone_core_get_video_multicast_addr into video cline. In case of outgoing call video stream is sent to this multicast address.
*
For incoming calls behavior is unchanged.
- * @param core #LinphoneCallParams
+ * @param params #LinphoneCallParams
* @param yesno if yes, subsequent outgoing calls will propose multicast ip set by #linphone_core_set_video_multicast_addr
* @ingroup media_parameters
**/
-LINPHONE_PUBLIC void linphone_call_params_enable_video_multicast(LinphoneCallParams *param, bool_t yesno);
+LINPHONE_PUBLIC void linphone_call_params_enable_video_multicast(LinphoneCallParams *params, bool_t yesno);
+
/**
* Use to get multicast state of video stream.
- * @param core #LinphoneCallParams
+ * @param params #LinphoneCallParams
* @return true if subsequent calls will propose multicast ip set by #linphone_core_set_video_multicast_addr
* @ingroup media_parameters
**/
-LINPHONE_PUBLIC bool_t linphone_call_params_video_multicast_enabled(const LinphoneCallParams *param);
+LINPHONE_PUBLIC bool_t linphone_call_params_video_multicast_enabled(const LinphoneCallParams *params);
+
+/**
+ * Use to enable real time text following rfc4103.
+ * If enabled, outgoing calls put a m=text line in SDP offer .
+ * @param params #LinphoneCallParams
+ * @param yesno if yes, subsequent outgoing calls will propose rtt
+ * @ingroup media_parameters
+**/
+LINPHONE_PUBLIC int linphone_call_params_enable_realtime_text(LinphoneCallParams *params, bool_t yesno);
+
+/**
+ * Use to get real time text following rfc4103.
+ * @param params #LinphoneCallParams
+ * @returns returns true if call rtt is activated.
+ * @ingroup media_parameters
+**/
+LINPHONE_PUBLIC bool_t linphone_call_params_realtime_text_enabled(const LinphoneCallParams *params);
+
+/**
+ * Add a custom attribute related to all the streams in the SDP exchanged within SIP messages during a call.
+ * @param[in] params The #LinphoneCallParams to add a custom SDP attribute to.
+ * @param[in] attribute_name The name of the attribute to add.
+ * @param[in] attribute_value The content value of the attribute to add.
+ * @ingroup media_parameters
+**/
+LINPHONE_PUBLIC void linphone_call_params_add_custom_sdp_attribute(LinphoneCallParams *params, const char *attribute_name, const char *attribute_value);
+
+/**
+ * Add a custom attribute related to a specific stream in the SDP exchanged within SIP messages during a call.
+ * @param[in] params The #LinphoneCallParams to add a custom SDP attribute to.
+ * @param[in] type The type of the stream to add a custom SDP attribute to.
+ * @param[in] attribute_name The name of the attribute to add.
+ * @param[in] attribute_value The content value of the attribute to add.
+ * @ingroup media_parameters
+**/
+LINPHONE_PUBLIC void linphone_call_params_add_custom_sdp_media_attribute(LinphoneCallParams *params, LinphoneStreamType type, const char *attribute_name, const char *attribute_value);
+
+/**
+ * Get a custom SDP attribute that is related to all the streams.
+ * @param[in] params The #LinphoneCallParams to get the custom SDP attribute from.
+ * @param[in] attribute_name The name of the attribute to get.
+ * @return The content value of the attribute or NULL if not found.
+ * @ingroup media_parameters
+**/
+LINPHONE_PUBLIC const char * linphone_call_params_get_custom_sdp_attribute(const LinphoneCallParams *params, const char *attribute_name);
+
+/**
+ * Get a custom SDP attribute that is related to a specific stream.
+ * @param[in] params The #LinphoneCallParams to get the custom SDP attribute from.
+ * @param[in] type The type of the stream to add a custom SDP attribute to.
+ * @param[in] attribute_name The name of the attribute to get.
+ * @return The content value of the attribute or NULL if not found.
+ * @ingroup media_parameters
+**/
+LINPHONE_PUBLIC const char * linphone_call_params_get_custom_sdp_media_attribute(const LinphoneCallParams *params, LinphoneStreamType type, const char *attribute_name);
+
+/**
+ * Clear the custom SDP attributes related to all the streams in the SDP exchanged within SIP messages during a call.
+ * @param[in] params The #LinphoneCallParams to clear the custom SDP attributes from.
+ * @ingroup media_parameters
+**/
+LINPHONE_PUBLIC void linphone_call_params_clear_custom_sdp_attributes(LinphoneCallParams *params);
+
+/**
+ * Clear the custom SDP attributes related to a specific stream in the SDP exchanged within SIP messages during a call.
+ * @param[in] params The #LinphoneCallParams to clear the custom SDP attributes from.
+ * @param[in] type The type of the stream to clear the custom SDP attributes from.
+ * @ingroup media_parameters
+**/
+LINPHONE_PUBLIC void linphone_call_params_clear_custom_sdp_media_attributes(LinphoneCallParams *params, LinphoneStreamType type);
/*******************************************************************************
diff --git a/coreapi/callbacks.c b/coreapi/callbacks.c
index 339c0a905..9e6e359fb 100644
--- a/coreapi/callbacks.c
+++ b/coreapi/callbacks.c
@@ -36,10 +36,17 @@ static void register_failure(SalOp *op);
static int media_parameters_changed(LinphoneCall *call, SalMediaDescription *oldmd, SalMediaDescription *newmd) {
int result=0;
+ int otherdesc_changed;
+ char *tmp1=NULL;
+ char *tmp2=NULL;
if (call->params->in_conference != call->current_params->in_conference) return SAL_MEDIA_DESCRIPTION_FORCE_STREAM_RECONSTRUCTION;
if (call->up_bw != linphone_core_get_upload_bandwidth(call->core)) return SAL_MEDIA_DESCRIPTION_FORCE_STREAM_RECONSTRUCTION;
- if (call->localdesc_changed) ms_message("Local description has changed: %i", call->localdesc_changed);
- result = call->localdesc_changed | sal_media_description_equals(oldmd, newmd);
+ if (call->localdesc_changed) ms_message("Local description has changed: %s", tmp1 = sal_media_description_print_differences(call->localdesc_changed));
+ otherdesc_changed = sal_media_description_equals(oldmd, newmd);
+ if (otherdesc_changed) ms_message("Other description has changed: %s", tmp2 = sal_media_description_print_differences(otherdesc_changed));
+ result = call->localdesc_changed | otherdesc_changed;
+ if (tmp1) ms_free(tmp1);
+ if (tmp2) ms_free(tmp2);
return result;
}
@@ -49,7 +56,7 @@ void linphone_core_update_streams_destinations(LinphoneCore *lc, LinphoneCall *c
char *rtp_addr, *rtcp_addr;
int i;
- for (i = 0; i < new_md->nb_streams; i++) {
+ for (i = 0; i < SAL_MEDIA_DESCRIPTION_MAX_STREAMS; i++) {
if (!sal_stream_description_active(&new_md->streams[i])) continue;
if (new_md->streams[i].type == SalAudio) {
new_audiodesc = &new_md->streams[i];
@@ -104,12 +111,12 @@ void linphone_call_update_frozen_payloads(LinphoneCall *call, SalMediaDescriptio
SalMediaDescription *local=call->localdesc;
int i;
for(i=0;inb_streams;++i){
- MSList *elem;
+ bctbx_list_t *elem;
for (elem=result_desc->streams[i].payloads;elem!=NULL;elem=elem->next){
PayloadType *pt=(PayloadType*)elem->data;
if (is_payload_type_number_available(local->streams[i].already_assigned_payloads, payload_type_get_number(pt), NULL)){
/*new codec, needs to be added to the list*/
- local->streams[i].already_assigned_payloads=ms_list_append(local->streams[i].already_assigned_payloads, payload_type_clone(pt));
+ local->streams[i].already_assigned_payloads=bctbx_list_append(local->streams[i].already_assigned_payloads, payload_type_clone(pt));
ms_message("LinphoneCall[%p] : payload type %i %s/%i fmtp=%s added to frozen list.",
call, payload_type_get_number(pt), pt->mime_type, pt->clock_rate, pt->recv_fmtp ? pt->recv_fmtp : "");
}
@@ -129,18 +136,7 @@ void linphone_core_update_streams(LinphoneCore *lc, LinphoneCall *call, SalMedia
ms_error("linphone_core_update_streams() called with null media description");
return;
}
- if (call->biggestdesc==NULL || new_md->nb_streams>call->biggestdesc->nb_streams){
- /*we have been offered and now are ready to proceed, or we added a new stream*/
- /*store the media description to remember the mapping of calls*/
- if (call->biggestdesc){
- sal_media_description_unref(call->biggestdesc);
- call->biggestdesc=NULL;
- }
- if (sal_call_is_offerer(call->op))
- call->biggestdesc=sal_media_description_ref(call->localdesc);
- else
- call->biggestdesc=sal_media_description_ref(sal_call_get_remote_media_description(call->op));
- }
+ linphone_call_update_biggest_desc(call, call->localdesc);
sal_media_description_ref(new_md);
call->resultdesc=new_md;
if ((call->audiostream && call->audiostream->ms.state==MSStreamStarted) || (call->videostream && call->videostream->ms.state==MSStreamStarted)){
@@ -152,6 +148,7 @@ void linphone_core_update_streams(LinphoneCore *lc, LinphoneCall *call, SalMedia
if ((md_changed & ( SAL_MEDIA_DESCRIPTION_CODEC_CHANGED
|SAL_MEDIA_DESCRIPTION_STREAMS_CHANGED
|SAL_MEDIA_DESCRIPTION_NETWORK_XXXCAST_CHANGED
+ |SAL_MEDIA_DESCRIPTION_ICE_RESTART_DETECTED
|SAL_MEDIA_DESCRIPTION_FORCE_STREAM_RECONSTRUCTION ))){
ms_message("Media descriptions are different, need to restart the streams.");
} else if ( call->playing_ringbacktone) {
@@ -161,12 +158,12 @@ void linphone_core_update_streams(LinphoneCore *lc, LinphoneCall *call, SalMedia
if (call->all_muted){
ms_message("Early media finished, unmuting inputs...");
/*we were in early media, now we want to enable real media */
- linphone_call_enable_camera (call,linphone_call_camera_enabled (call));
+ call->all_muted = FALSE;
if (call->audiostream)
linphone_core_enable_mic(lc, linphone_core_mic_enabled(lc));
#ifdef VIDEO_ENABLED
if (call->videostream && call->camera_enabled)
- video_stream_change_camera(call->videostream,linphone_call_get_video_device(call));
+ linphone_call_enable_camera(call, linphone_call_camera_enabled(call));
#endif
}
/*FIXME ZRTP, might be restarted in any cases ? */
@@ -188,8 +185,9 @@ void linphone_core_update_streams(LinphoneCore *lc, LinphoneCall *call, SalMedia
linphone_call_stop_media_streams (call);
if (md_changed & SAL_MEDIA_DESCRIPTION_NETWORK_XXXCAST_CHANGED){
ms_message("Media ip type has changed, destroying sessions context on call [%p]",call);
- ms_media_stream_sessions_uninit(&call->sessions[0]);
- ms_media_stream_sessions_uninit(&call->sessions[1]);
+ ms_media_stream_sessions_uninit(&call->sessions[call->main_audio_stream_index]);
+ ms_media_stream_sessions_uninit(&call->sessions[call->main_video_stream_index]);
+ ms_media_stream_sessions_uninit(&call->sessions[call->main_text_stream_index]);
}
linphone_call_init_media_streams (call);
}
@@ -198,12 +196,12 @@ void linphone_core_update_streams(LinphoneCore *lc, LinphoneCall *call, SalMedia
/*this happens after pausing the call locally. The streams are destroyed and then we wait the 200Ok to recreate them*/
linphone_call_init_media_streams (call);
}
-
+
if (call->params->real_early_media && call->state==LinphoneCallOutgoingEarlyMedia){
prepare_early_media_forking(call);
}
linphone_call_start_media_streams(call, target_state);
- if (call->state==LinphoneCallPausing && call->paused_by_app && ms_list_size(lc->calls)==1){
+ if (call->state==LinphoneCallPausing && call->paused_by_app && bctbx_list_size(lc->calls)==1){
linphone_core_play_named_tone(lc,LinphoneToneCallOnHold);
}
linphone_call_update_frozen_payloads(call, new_md);
@@ -214,7 +212,7 @@ void linphone_core_update_streams(LinphoneCore *lc, LinphoneCall *call, SalMedia
}
#if 0
static bool_t is_duplicate_call(LinphoneCore *lc, const LinphoneAddress *from, const LinphoneAddress *to){
- MSList *elem;
+ bctbx_list_t *elem;
for(elem=lc->calls;elem!=NULL;elem=elem->next){
LinphoneCall *call=(LinphoneCall*)elem->data;
if (linphone_address_weak_equal(call->log->from,from) &&
@@ -227,7 +225,7 @@ static bool_t is_duplicate_call(LinphoneCore *lc, const LinphoneAddress *from, c
#endif
static bool_t already_a_call_with_remote_address(const LinphoneCore *lc, const LinphoneAddress *remote) {
- MSList *elem;
+ bctbx_list_t *elem;
ms_message("Searching for already_a_call_with_remote_address.");
for(elem=lc->calls;elem!=NULL;elem=elem->next){
@@ -242,9 +240,28 @@ static bool_t already_a_call_with_remote_address(const LinphoneCore *lc, const L
}
+static LinphoneCall * look_for_broken_call_to_replace(SalOp *h, LinphoneCore *lc) {
+ const bctbx_list_t *calls = linphone_core_get_calls(lc);
+ const bctbx_list_t *it = calls;
+ while (it != NULL) {
+ LinphoneCall *replaced_call = NULL;
+ LinphoneCall *call = (LinphoneCall *)bctbx_list_get_data(it);
+ SalOp *replaced_op = sal_call_get_replaces(h);
+ if (replaced_op) replaced_call = (LinphoneCall*)sal_op_get_user_pointer(replaced_op);
+ if ((call->broken && sal_call_compare_op(h, call->op))
+ || ((replaced_call == call) && (strcmp(sal_op_get_from(h), sal_op_get_from(replaced_op)) == 0) && (strcmp(sal_op_get_to(h), sal_op_get_to(replaced_op)) == 0))) {
+ return call;
+ }
+ it = bctbx_list_next(it);
+ }
+
+ return NULL;
+}
+
static void call_received(SalOp *h){
LinphoneCore *lc=(LinphoneCore *)sal_get_user_pointer(sal_op_get_sal(h));
LinphoneCall *call;
+ LinphoneCall *replaced_call;
char *alt_contact;
LinphoneAddress *from_addr=NULL;
LinphoneAddress *to_addr=NULL;
@@ -252,31 +269,30 @@ static void call_received(SalOp *h){
SalMediaDescription *md;
const char * p_asserted_id;
+ /* Look if this INVITE is for a call that has already been notified but broken because of network failure */
+ replaced_call = look_for_broken_call_to_replace(h, lc);
+ if (replaced_call != NULL) {
+ linphone_call_replace_op(replaced_call, h);
+ return;
+ }
+
/* first check if we can answer successfully to this invite */
if (linphone_presence_model_get_basic_status(lc->presence_model) == LinphonePresenceBasicStatusClosed) {
LinphonePresenceActivity *activity = linphone_presence_model_get_activity(lc->presence_model);
switch (linphone_presence_activity_get_type(activity)) {
- case LinphonePresenceActivityBusy:
- sal_call_decline(h,SalReasonBusy,NULL);
- break;
- case LinphonePresenceActivityAppointment:
- case LinphonePresenceActivityMeeting:
- case LinphonePresenceActivityOffline:
- case LinphonePresenceActivityWorship:
- sal_call_decline(h,SalReasonTemporarilyUnavailable,NULL);
- break;
case LinphonePresenceActivityPermanentAbsence:
alt_contact = linphone_presence_model_get_contact(lc->presence_model);
if (alt_contact != NULL) {
sal_call_decline(h,SalReasonRedirect,alt_contact);
ms_free(alt_contact);
+ sal_op_release(h);
+ return;
}
break;
default:
+ /*nothing special to be done*/
break;
}
- sal_op_release(h);
- return;
}
if (!linphone_core_can_we_add_call(lc)){/*busy*/
@@ -346,7 +362,7 @@ static void call_received(SalOp *h){
call->bg_task_id=sal_begin_background_task("liblinphone call notification", NULL, NULL);
- if ((linphone_core_get_firewall_policy(lc) == LinphonePolicyUseIce) && (call->ice_session != NULL)) {
+ if (call->defer_notify_incoming) {
/* Defer ringing until the end of the ICE candidates gathering process. */
ms_message("Defer ringing to gather ICE candidates");
return;
@@ -368,7 +384,7 @@ static void try_early_media_forking(LinphoneCall *call, SalMediaDescription *md)
SalStreamDescription *ref_stream,*new_stream;
ms_message("Early media response received from another branch, checking if media can be forked to this new destination.");
- for (i=0;inb_streams;++i){
+ for (i=0;istreams[i])) continue;
ref_stream=&cur_md->streams[i];
new_stream=&md->streams[i];
@@ -407,7 +423,7 @@ static void start_remote_ring(LinphoneCore *lc, LinphoneCall *call) {
if (call->audiostream)
audio_stream_unprepare_sound(call->audiostream);
if( lc->sound_conf.remote_ring ){
- lc->ringstream=ring_start(lc->sound_conf.remote_ring,2000,ringcard);
+ lc->ringstream=ring_start(lc->factory, lc->sound_conf.remote_ring,2000,ringcard);
}
}
}
@@ -431,12 +447,13 @@ static void call_ringing(SalOp *h){
/*already doing early media */
return;
}
- if (lc->ringstream!=NULL) return;/*already ringing !*/
- start_remote_ring(lc, call);
+ if (lc->ringstream == NULL) start_remote_ring(lc, call);
ms_message("Remote ringing...");
linphone_core_notify_display_status(lc,_("Remote ringing..."));
linphone_call_set_state(call,LinphoneCallOutgoingRinging,"Remote ringing");
}else{
+ /*initialize the remote call params by invoking linphone_call_get_remote_params(). This is useful as the SDP may not be present in the 200Ok*/
+ linphone_call_get_remote_params(call);
/*accept early media */
if ((call->audiostream && audio_stream_started(call->audiostream))
#ifdef VIDEO_ENABLED
@@ -476,7 +493,7 @@ static void process_call_accepted(LinphoneCore *lc, LinphoneCall *call, SalOp *o
LinphoneCallState next_state = LinphoneCallIdle;
const char *next_state_str = NULL;
LinphoneTaskList tl;
-
+
switch (call->state){/*immediately notify the connected state, even if errors occur after*/
case LinphoneCallOutgoingProgress:
case LinphoneCallOutgoingRinging:
@@ -494,7 +511,7 @@ static void process_call_accepted(LinphoneCore *lc, LinphoneCall *call, SalOp *o
default:
break;
}
-
+
linphone_task_list_init(&tl);
rmd=sal_call_get_remote_media_description(op);
/*set privacy*/
@@ -503,10 +520,7 @@ static void process_call_accepted(LinphoneCore *lc, LinphoneCall *call, SalOp *o
if (call->params->internal_call_update)
call->params->internal_call_update = FALSE;
- /* Handle remote ICE attributes if any. */
- if (call->ice_session != NULL && rmd) {
- linphone_call_update_ice_from_remote_media_description(call, rmd);
- }
+
#ifdef BUILD_UPNP
if (call->upnp_session != NULL && rmd) {
linphone_core_update_upnp_from_remote_media_description(call, rmd);
@@ -522,6 +536,12 @@ static void process_call_accepted(LinphoneCore *lc, LinphoneCall *call, SalOp *o
md = NULL;
}
if (md){ /*there is a valid SDP in the response, either offer or answer, and we're able to start/update the streams*/
+
+ /* Handle remote ICE attributes if any. */
+ if (call->ice_session != NULL && rmd) {
+ linphone_call_update_ice_from_remote_media_description(call, rmd, FALSE);
+ }
+
switch (call->state){
case LinphoneCallResuming:
linphone_core_notify_display_status(lc,_("Call resumed."));
@@ -537,7 +557,7 @@ static void process_call_accepted(LinphoneCore *lc, LinphoneCall *call, SalOp *o
next_state = LinphoneCallPausedByRemote;
next_state_str = "Call paused by remote";
}else{
- if (!call->current_params->in_conference)
+ if (!call->params->in_conference)
lc->current_call=call;
next_state = LinphoneCallStreamsRunning;
next_state_str = "Streams running";
@@ -559,12 +579,12 @@ static void process_call_accepted(LinphoneCore *lc, LinphoneCall *call, SalOp *o
ms_error("call_accepted(): don't know what to do in state [%s]", linphone_call_state_to_string(call->state));
break;
}
-
+
if (next_state != LinphoneCallIdle){
linphone_call_update_remote_session_id_and_ver(call);
linphone_core_update_ice_state_in_call_stats(call);
linphone_core_update_streams(lc, call, md, next_state);
- linphone_call_fix_call_parameters(call);
+ linphone_call_fix_call_parameters(call, rmd);
linphone_call_set_state(call, next_state, next_state_str);
}else{
ms_error("BUG: next_state is not set in call_accepted(), current state is %s", linphone_call_state_to_string(call->state));
@@ -600,7 +620,7 @@ static void process_call_accepted(LinphoneCore *lc, LinphoneCall *call, SalOp *o
static void call_accepted(SalOp *op){
LinphoneCore *lc=(LinphoneCore *)sal_get_user_pointer(sal_op_get_sal(op));
LinphoneCall *call=(LinphoneCall*)sal_op_get_user_pointer(op);
-
+
if (call == NULL){
ms_warning("call_accepted: call does no longer exist.");
return ;
@@ -626,23 +646,36 @@ static void call_paused_by_remote(LinphoneCore *lc, LinphoneCall *call){
linphone_call_params_unref(params);
}
+static void call_updated_by_remote(LinphoneCore *lc, LinphoneCall *call){
+ linphone_core_notify_display_status(lc,_("Call is updated by remote."));
+ linphone_call_set_state(call, LinphoneCallUpdatedByRemote,"Call updated by remote");
+ if (call->defer_update == FALSE){
+ if (call->state == LinphoneCallUpdatedByRemote){
+ linphone_core_accept_call_update(lc,call,NULL);
+ }else{
+ /*otherwise it means that the app responded by linphone_core_accept_call_update
+ * within the callback, so job is already done.*/
+ }
+ }else{
+ if (call->state == LinphoneCallUpdatedByRemote){
+ ms_message("LinphoneCall [%p]: UpdatedByRemoted was signaled but defered. LinphoneCore expects the application to call "
+ "linphone_core_accept_call_update() later.", call);
+ }
+ }
+}
+
/* this callback is called when an incoming re-INVITE/ SIP UPDATE modifies the session*/
static void call_updated(LinphoneCore *lc, LinphoneCall *call, SalOp *op, bool_t is_update){
SalMediaDescription *rmd=sal_call_get_remote_media_description(op);
-
+
call->defer_update = lp_config_get_int(lc->config, "sip", "defer_update_default", FALSE);
-
+
switch(call->state){
case LinphoneCallPausedByRemote:
if (sal_media_description_has_dir(rmd,SalStreamSendRecv) || sal_media_description_has_dir(rmd,SalStreamRecvOnly)){
call_resumed(lc,call);
}else{
- /*we are staying in PausedByRemote*/
- linphone_core_notify_display_status(lc,_("Call is updated by remote."));
- linphone_call_set_state(call, LinphoneCallUpdatedByRemote,"Call updated by remote");
- if (call->defer_update == FALSE){
- linphone_core_accept_call_update(lc,call,NULL);
- }
+ call_updated_by_remote(lc, call);
}
break;
/*SIP UPDATE CASE*/
@@ -656,14 +689,11 @@ static void call_updated(LinphoneCore *lc, LinphoneCall *call, SalOp *op, bool_t
break;
case LinphoneCallStreamsRunning:
case LinphoneCallConnected:
+ case LinphoneCallUpdatedByRemote: // Can happen on UAC connectivity loss
if (sal_media_description_has_dir(rmd,SalStreamSendOnly) || sal_media_description_has_dir(rmd,SalStreamInactive)){
call_paused_by_remote(lc,call);
}else{
- linphone_core_notify_display_status(lc,_("Call is updated by remote."));
- linphone_call_set_state(call, LinphoneCallUpdatedByRemote,"Call updated by remote");
- if (call->defer_update == FALSE){
- linphone_core_accept_call_update(lc,call,NULL);
- }
+ call_updated_by_remote(lc, call);
}
break;
case LinphoneCallPaused:
@@ -673,7 +703,6 @@ static void call_updated(LinphoneCore *lc, LinphoneCall *call, SalOp *op, bool_t
case LinphoneCallUpdating:
case LinphoneCallPausing:
case LinphoneCallResuming:
- case LinphoneCallUpdatedByRemote:
sal_call_decline(call->op,SalReasonInternalError,NULL);
/*no break*/
case LinphoneCallIdle:
@@ -701,8 +730,15 @@ static void call_updating(SalOp *op, bool_t is_update){
ms_error("call_updating(): call doesn't exist anymore");
return ;
}
+ linphone_call_fix_call_parameters(call, rmd);
if (call->state!=LinphoneCallPaused){
/*Refresh the local description, but in paused state, we don't change anything.*/
+ if (rmd == NULL && lp_config_get_int(call->core->config,"sip","sdp_200_ack_follow_video_policy",0)) {
+ LinphoneCallParams *p=linphone_core_create_call_params(lc, NULL);
+ ms_message("Applying default policy for offering SDP on call [%p]",call);
+ linphone_call_set_new_params(call, p);
+ linphone_call_params_destroy(p);
+ }
linphone_call_make_local_media_description(call);
sal_call_set_local_media_description(call->op,call->localdesc);
}
@@ -714,9 +750,9 @@ static void call_updating(SalOp *op, bool_t is_update){
}else {
SalMediaDescription *md;
SalMediaDescription *prev_result_desc=call->resultdesc;
-
+
call->expect_media_in_ack = FALSE;
-
+
md=sal_call_get_final_media_description(call->op);
if (md && (sal_media_description_empty(md) || linphone_core_incompatible_security(lc,md))){
sal_call_decline(call->op,SalReasonNotAcceptable,NULL);
@@ -738,7 +774,7 @@ static void call_updating(SalOp *op, bool_t is_update){
static void call_ack(SalOp *op){
LinphoneCore *lc=(LinphoneCore *)sal_get_user_pointer(sal_op_get_sal(op));
LinphoneCall *call=(LinphoneCall*)sal_op_get_user_pointer(op);
-
+
if (call == NULL){
ms_warning("call_ack(): no call for which an ack is expected");
return;
@@ -780,7 +816,7 @@ static void call_terminated(SalOp *op, const char *from){
linphone_core_start_refered_call(lc,call,NULL);
}
//we stop the call only if we have this current call or if we are in call
- if (lc->ringstream!=NULL && ( (ms_list_size(lc->calls) == 1) || linphone_core_in_call(lc) )) {
+ if ((bctbx_list_size(lc->calls) == 1) || linphone_core_in_call(lc)) {
linphone_core_stop_ringing(lc);
}
linphone_call_stop_media_streams(call);
@@ -921,9 +957,11 @@ static void call_failure(SalOp *op){
case LinphoneCallUpdating:
case LinphoneCallPausing:
case LinphoneCallResuming:
- ms_message("Call error on state [%s], restoring previous state",linphone_call_state_to_string(call->prevstate));
- linphone_call_set_state(call, call->prevstate,ei->full_string);
- return;
+ if (ei->reason != SalReasonNoMatch){
+ ms_message("Call error on state [%s], restoring previous state",linphone_call_state_to_string(call->prevstate));
+ linphone_call_set_state(call, call->prevstate,ei->full_string);
+ return;
+ }
default:
break; /*nothing to do*/
}
@@ -939,7 +977,11 @@ static void call_failure(SalOp *op){
if (ei->reason==SalReasonDeclined){
linphone_call_set_state(call,LinphoneCallEnd,"Call declined.");
}else{
- linphone_call_set_state(call,LinphoneCallError,ei->full_string);
+ if (linphone_call_state_is_early(call->state)){
+ linphone_call_set_state(call,LinphoneCallError,ei->full_string);
+ }else{
+ linphone_call_set_state(call, LinphoneCallEnd, ei->full_string);
+ }
}
if (ei->reason!=SalReasonNone) linphone_core_play_call_error_tone(lc,linphone_reason_from_sal(ei->reason));
}
@@ -963,20 +1005,31 @@ static void call_released(SalOp *op){
}
}
+static void call_cancel_done(SalOp *op) {
+ LinphoneCall *call = (LinphoneCall *)sal_op_get_user_pointer(op);
+ if (call->reinvite_on_cancel_response_requested == TRUE) {
+ call->reinvite_on_cancel_response_requested = FALSE;
+ linphone_call_reinvite_to_recover_from_connection_loss(call);
+ }
+}
+
static void auth_failure(SalOp *op, SalAuthInfo* info) {
- LinphoneCore *lc=(LinphoneCore *)sal_get_user_pointer(sal_op_get_sal(op));
- LinphoneAuthInfo *ai=NULL;
-
- if( info != NULL ){
- ai = (LinphoneAuthInfo*)linphone_core_find_auth_info(lc,info->realm,info->username,info->domain);
+ LinphoneCore *lc = (LinphoneCore *)sal_get_user_pointer(sal_op_get_sal(op));
+ LinphoneAuthInfo *ai = NULL;
+ if (info != NULL) {
+ ai = (LinphoneAuthInfo*)_linphone_core_find_auth_info(lc, info->realm, info->username, info->domain, TRUE);
if (ai){
- ms_message("%s/%s/%s authentication fails.",info->realm,info->username,info->domain);
+ 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");
/*ask again for password if auth info was already supplied but apparently not working*/
- linphone_core_notify_auth_info_requested(lc,info->realm,info->username,info->domain);
+ linphone_core_notify_authentication_requested(lc, auth_info, method);
+ linphone_auth_info_destroy(auth_info);
+ // Deprecated
+ linphone_core_notify_auth_info_requested(lc, info->realm, info->username, info->domain);
}
}
-
}
static void register_success(SalOp *op, bool_t registered){
@@ -1024,10 +1077,11 @@ static void register_failure(SalOp *op){
} else {
linphone_proxy_config_set_state(cfg,LinphoneRegistrationFailed,details);
}
- if (cfg->publish_op){
+ if (cfg->long_term_event){
/*prevent publish to be sent now until registration gets successful*/
- sal_op_release(cfg->publish_op);
- cfg->publish_op=NULL;
+ linphone_event_terminate(cfg->long_term_event);
+ linphone_event_unref(cfg->long_term_event);
+ cfg->long_term_event=NULL;
cfg->send_publish=cfg->publish;
}
}
@@ -1047,13 +1101,22 @@ static void vfu_request(SalOp *op){
static void dtmf_received(SalOp *op, char dtmf){
LinphoneCore *lc=(LinphoneCore *)sal_get_user_pointer(sal_op_get_sal(op));
LinphoneCall *call=(LinphoneCall*)sal_op_get_user_pointer(op);
+ if (!call) return;
linphone_core_notify_dtmf_received(lc, call, dtmf);
}
static void refer_received(Sal *sal, SalOp *op, const char *referto){
LinphoneCore *lc=(LinphoneCore *)sal_get_user_pointer(sal);
LinphoneCall *call=(LinphoneCall*)sal_op_get_user_pointer(op);
- if (call){
+ LinphoneAddress *refer_to_addr = linphone_address_new(referto);
+ char method[20] = "";
+
+ if(refer_to_addr) {
+ const char *tmp = linphone_address_get_method_param(refer_to_addr);
+ if(tmp) strncpy(method, tmp, sizeof(method));
+ linphone_address_destroy(refer_to_addr);
+ }
+ if (call && (strlen(method) == 0 || strcmp(method, "INVITE") == 0)) {
if (call->refer_to!=NULL){
ms_free(call->refer_to);
}
@@ -1072,8 +1135,8 @@ static void refer_received(Sal *sal, SalOp *op, const char *referto){
}
static bool_t is_duplicate_msg(LinphoneCore *lc, const char *msg_id){
- MSList *elem=lc->last_recv_msg_ids;
- MSList *tail=NULL;
+ bctbx_list_t *elem=lc->last_recv_msg_ids;
+ bctbx_list_t *tail=NULL;
int i;
bool_t is_duplicate=FALSE;
for(i=0;elem!=NULL;elem=elem->next,i++){
@@ -1083,11 +1146,11 @@ static bool_t is_duplicate_msg(LinphoneCore *lc, const char *msg_id){
tail=elem;
}
if (!is_duplicate){
- lc->last_recv_msg_ids=ms_list_prepend(lc->last_recv_msg_ids,ms_strdup(msg_id));
+ lc->last_recv_msg_ids=bctbx_list_prepend(lc->last_recv_msg_ids,ms_strdup(msg_id));
}
if (i>=10){
ms_free(tail->data);
- lc->last_recv_msg_ids=ms_list_remove_link(lc->last_recv_msg_ids,tail);
+ lc->last_recv_msg_ids=bctbx_list_erase_link(lc->last_recv_msg_ids,tail);
}
return is_duplicate;
}
@@ -1110,11 +1173,18 @@ static void is_composing_received(SalOp *op, const SalIsComposing *is_composing)
}
static void parse_presence_requested(SalOp *op, const char *content_type, const char *content_subtype, const char *body, SalPresenceModel **result) {
- linphone_notify_parse_presence(op, content_type, content_subtype, body, 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) {
- linphone_notify_convert_presence_to_xml(op, presence, contact, 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){
@@ -1148,57 +1218,82 @@ static void ping_reply(SalOp *op){
}
static bool_t fill_auth_info_with_client_certificate(LinphoneCore *lc, SalAuthInfo* sai) {
- const char *chain_file = lp_config_get_string(lc->config,"sip","client_cert_chain", 0);
- const char *key_file = lp_config_get_string(lc->config,"sip","client_cert_key", 0);;
+ 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;
- }
- }
+ // 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, "");
+ 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=(LinphoneAuthInfo*)linphone_core_find_auth_info(lc,sai->realm,sai->username,sai->domain);
+ 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, FALSE);
+ }
if (ai) {
- sai->userid=ms_strdup(ai->userid?ai->userid:ai->username);
- sai->password=ai->passwd?ms_strdup(ai->passwd):NULL;
- sai->ha1=ai->ha1?ms_strdup(ai->ha1):NULL;
+ if (sai->mode == SalAuthModeHttpDigest) {
+ sai->userid = ms_strdup(ai->userid ? ai->userid : ai->username);
+ sai->password = ai->passwd?ms_strdup(ai->passwd) : NULL;
+ sai->ha1 = ai->ha1 ? ms_strdup(ai->ha1) : NULL;
+ } else if (sai->mode == SalAuthModeTls) {
+ if (ai->tls_cert && ai->tls_key) {
+ sal_certificates_chain_parse(sai, ai->tls_cert, SAL_CERTIFICATE_RAW_FORMAT_PEM);
+ sal_signing_key_parse(sai, ai->tls_key, "");
+ } else if (ai->tls_cert_path && ai->tls_key_path) {
+ sal_certificates_chain_parse_file(sai, ai->tls_cert_path, SAL_CERTIFICATE_RAW_FORMAT_PEM);
+ sal_signing_key_parse_file(sai, ai->tls_key_path, "");
+ } else {
+ fill_auth_info_with_client_certificate(lc, sai);
+ }
+ }
+
+ if (sai->realm && !ai->realm){
+ /*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_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_get_user_pointer(sal);
- if (sai->mode == SalAuthModeHttpDigest) {
- if (fill_auth_info(lc,sai)) {
- return TRUE;
- } else {
- {
- linphone_core_notify_auth_info_requested(lc,sai->realm,sai->username,sai->domain);
- if (fill_auth_info(lc,sai)) {
- return TRUE;
- }
- }
- return FALSE;
- }
- } else if (sai->mode == SalAuthModeTls) {
- return fill_auth_info_with_client_certificate(lc,sai);
+ LinphoneCore *lc = (LinphoneCore *)sal_get_user_pointer(sal);
+ 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);
+ linphone_core_notify_authentication_requested(lc, ai, method);
+ linphone_auth_info_destroy(ai);
+ // Deprecated
+ linphone_core_notify_auth_info_requested(lc, sai->realm, sai->username, sai->domain);
+ if (fill_auth_info(lc, sai)) {
+ return TRUE;
+ }
return FALSE;
}
}
@@ -1250,32 +1345,22 @@ static void text_delivery_update(SalOp *op, SalTextDeliveryStatus status){
// Do not handle delivery status for isComposing messages.
return;
}
-
- chat_msg->state=chatStatusSal2Linphone(status);
- linphone_chat_message_update_state(chat_msg);
-
- if (chat_msg && (chat_msg->cb || (chat_msg->callbacks && linphone_chat_message_cbs_get_msg_state_changed(chat_msg->callbacks)))) {
- ms_message("Notifying text delivery with status %s",linphone_chat_message_state_to_string(chat_msg->state));
- if (chat_msg->callbacks && linphone_chat_message_cbs_get_msg_state_changed(chat_msg->callbacks)) {
- linphone_chat_message_cbs_get_msg_state_changed(chat_msg->callbacks)(chat_msg, chat_msg->state);
- } else {
- /* Legacy */
- chat_msg->cb(chat_msg,chat_msg->state,chat_msg->cb_ud);
- }
+ // check that the message does not belong to an already destroyed chat room - if so, do not invoke callbacks
+ if (chat_msg->chat_room != NULL) {
+ linphone_chat_message_update_state(chat_msg, chatStatusSal2Linphone(status));
}
if (status != SalTextDeliveryInProgress) { /*only release op if not in progress*/
linphone_chat_message_destroy(chat_msg);
}
}
-static void info_received(SalOp *op, const SalBody *body){
+static void info_received(SalOp *op, SalBodyHandler *body_handler){
LinphoneCore *lc=(LinphoneCore *)sal_get_user_pointer(sal_op_get_sal(op));
- linphone_core_notify_info_message(lc,op,body);
+ linphone_core_notify_info_message(lc,op,body_handler);
}
-static void subscribe_response(SalOp *op, SalSubscribeStatus status){
+static void subscribe_response(SalOp *op, SalSubscribeStatus status, int will_retry){
LinphoneEvent *lev=(LinphoneEvent*)sal_op_get_user_pointer(op);
- const SalErrorInfo *ei=sal_op_get_error_info(op);
if (lev==NULL) return;
@@ -1284,31 +1369,37 @@ static void subscribe_response(SalOp *op, SalSubscribeStatus status){
}else if (status==SalSubscribePending){
linphone_event_set_state(lev,LinphoneSubscriptionPending);
}else{
- if (lev->subscription_state==LinphoneSubscriptionActive && ei->reason==SalReasonIOError){
+ if (will_retry){
linphone_event_set_state(lev,LinphoneSubscriptionOutgoingProgress);
}
else linphone_event_set_state(lev,LinphoneSubscriptionError);
}
}
-static void notify(SalOp *op, SalSubscribeStatus st, const char *eventname, const SalBody *body){
+static void notify(SalOp *op, SalSubscribeStatus st, const char *eventname, SalBodyHandler *body_handler){
LinphoneEvent *lev=(LinphoneEvent*)sal_op_get_user_pointer(op);
LinphoneCore *lc=(LinphoneCore *)sal_get_user_pointer(sal_op_get_sal(op));
-
- if (lev==NULL) {
- /*out of subscribe notify */
- lev=linphone_event_new_with_out_of_dialog_op(lc,op,LinphoneSubscriptionOutgoing,eventname);
+ 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(body);
- if (ct) linphone_core_notify_notify_received(lc,lev,eventname,ct);
+ 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 (st!=SalSubscribeNone){
+ 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(SalOp *op, const char *eventname, const SalBody *body){
+static void subscribe_received(SalOp *op, const char *eventname, const SalBodyHandler *body_handler){
LinphoneEvent *lev=(LinphoneEvent*)sal_op_get_user_pointer(op);
LinphoneCore *lc=(LinphoneCore *)sal_get_user_pointer(sal_op_get_sal(op));
@@ -1321,7 +1412,7 @@ static void subscribe_received(SalOp *op, const char *eventname, const SalBody *
}
-static void subscribe_closed(SalOp *op){
+static void incoming_subscribe_closed(SalOp *op){
LinphoneEvent *lev=(LinphoneEvent*)sal_op_get_user_pointer(op);
linphone_event_set_state(lev,LinphoneSubscriptionTerminated);
@@ -1346,6 +1437,7 @@ static void on_publish_response(SalOp* op){
}
}
+
static void on_expire(SalOp *op){
LinphoneEvent *lev=(LinphoneEvent*)sal_op_get_user_pointer(op);
@@ -1358,6 +1450,25 @@ static void on_expire(SalOp *op){
}
}
+static void on_notify_response(SalOp *op){
+ LinphoneEvent *lev=(LinphoneEvent*)sal_op_get_user_pointer(op);
+
+ if (lev==NULL) return;
+ /*this is actually handling out of dialogs notify - for the moment*/
+ if (!lev->is_out_of_dialog_op) return;
+ switch (linphone_event_get_subscription_state(lev)){
+ case LinphoneSubscriptionIncomingReceived:
+ if (sal_op_get_error_info(op)->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)));
+ }
+}
+
SalCallbacks linphone_sal_callbacks={
call_received,
call_ringing,
@@ -1367,6 +1478,7 @@ SalCallbacks linphone_sal_callbacks={
call_terminated,
call_failure,
call_released,
+ call_cancel_done,
auth_failure,
register_success,
register_failure,
@@ -1378,7 +1490,7 @@ SalCallbacks linphone_sal_callbacks={
is_composing_received,
notify_refer,
subscribe_received,
- subscribe_closed,
+ incoming_subscribe_closed,
subscribe_response,
notify,
subscribe_presence_received,
@@ -1390,7 +1502,8 @@ SalCallbacks linphone_sal_callbacks={
auth_requested,
info_received,
on_publish_response,
- on_expire
+ on_expire,
+ on_notify_response
};
diff --git a/coreapi/carddav.c b/coreapi/carddav.c
new file mode 100644
index 000000000..b07bb60ba
--- /dev/null
+++ b/coreapi/carddav.c
@@ -0,0 +1,767 @@
+/*
+carddav.c
+Copyright (C) 2015 Belledonne Communications SARL
+
+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 "linphonecore.h"
+#include "private.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_destroy(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);
+ 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) {
+ belle_sip_auth_event_set_username(event, cdc->auth_info->username);
+ belle_sip_auth_event_set_passwd(event, cdc->auth_info->passwd);
+ belle_sip_auth_event_set_ha1(event, cdc->auth_info->ha1);
+ } 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 (auth_info->domain && strcmp(domain, auth_info->domain) == 0) {
+ if (!auth_info->realm || strcmp(realm, auth_info->realm) == 0) {
+ belle_sip_auth_event_set_username(event, auth_info->username);
+ belle_sip_auth_event_set_passwd(event, auth_info->passwd);
+ belle_sip_auth_event_set_ha1(event, auth_info->ha1);
+ 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 = ms_malloc(64);
+ if (sal_generate_uuid(uuid, 64) == 0) {
+ char *url = 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);
+}
\ No newline at end of file
diff --git a/coreapi/carddav.h b/coreapi/carddav.h
new file mode 100644
index 000000000..79237632c
--- /dev/null
+++ b/coreapi/carddav.h
@@ -0,0 +1,170 @@
+/*
+carddav.h
+Copyright (C) 2015 Belledonne Communications SARL
+
+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.
+*/
+
+#ifndef LINPHONE_CARDDAV_H
+#define LINPHONE_CARDDAV_H
+
+#include "linphonecore.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
\ No newline at end of file
diff --git a/coreapi/chat.c b/coreapi/chat.c
index 990f06998..933909121 100644
--- a/coreapi/chat.c
+++ b/coreapi/chat.c
@@ -26,8 +26,8 @@
#include "private.h"
#include "lpconfig.h"
#include "belle-sip/belle-sip.h"
-#include "lime.h"
#include "ortp/b64.h"
+#include "lime.h"
#include
#include
@@ -37,25 +37,25 @@
#define COMPOSING_DEFAULT_REFRESH_TIMEOUT 60
#define COMPOSING_DEFAULT_REMOTE_REFRESH_TIMEOUT 120
-#define FILE_TRANSFER_KEY_SIZE 32
-
-
static void linphone_chat_message_release(LinphoneChatMessage *msg);
-
-static LinphoneChatMessageCbs * linphone_chat_message_cbs_new(void) {
- return belle_sip_object_new(LinphoneChatMessageCbs);
-}
+static void linphone_chat_room_delete_composing_idle_timer(LinphoneChatRoom *cr);
+static void linphone_chat_room_delete_composing_refresh_timer(LinphoneChatRoom *cr);
+static void linphone_chat_room_delete_remote_composing_refresh_timer(LinphoneChatRoom *cr);
+static void _linphone_chat_message_destroy(LinphoneChatMessage *msg);
BELLE_SIP_DECLARE_NO_IMPLEMENTED_INTERFACES(LinphoneChatMessageCbs);
BELLE_SIP_INSTANCIATE_VPTR(LinphoneChatMessageCbs, belle_sip_object_t,
- NULL, // destroy
- NULL, // clone
- NULL, // marshal
- FALSE
-);
+ NULL, // destroy
+ NULL, // clone
+ NULL, // marshal
+ FALSE);
-LinphoneChatMessageCbs * linphone_chat_message_cbs_ref(LinphoneChatMessageCbs *cbs) {
+LinphoneChatMessageCbs *linphone_chat_message_cbs_new(void) {
+ return belle_sip_object_new(LinphoneChatMessageCbs);
+}
+
+LinphoneChatMessageCbs *linphone_chat_message_cbs_ref(LinphoneChatMessageCbs *cbs) {
belle_sip_object_ref(cbs);
return cbs;
}
@@ -72,11 +72,13 @@ void linphone_chat_message_cbs_set_user_data(LinphoneChatMessageCbs *cbs, void *
cbs->user_data = ud;
}
-LinphoneChatMessageCbsMsgStateChangedCb linphone_chat_message_cbs_get_msg_state_changed(const LinphoneChatMessageCbs *cbs) {
+LinphoneChatMessageCbsMsgStateChangedCb
+linphone_chat_message_cbs_get_msg_state_changed(const LinphoneChatMessageCbs *cbs) {
return cbs->msg_state_changed;
}
-void linphone_chat_message_cbs_set_msg_state_changed(LinphoneChatMessageCbs *cbs, LinphoneChatMessageCbsMsgStateChangedCb cb) {
+void linphone_chat_message_cbs_set_msg_state_changed(LinphoneChatMessageCbs *cbs,
+ LinphoneChatMessageCbsMsgStateChangedCb cb) {
cbs->msg_state_changed = cb;
}
@@ -84,7 +86,8 @@ LinphoneChatMessageCbsFileTransferRecvCb linphone_chat_message_cbs_get_file_tran
return cbs->file_transfer_recv;
}
-void linphone_chat_message_cbs_set_file_transfer_recv(LinphoneChatMessageCbs *cbs, LinphoneChatMessageCbsFileTransferRecvCb cb) {
+void linphone_chat_message_cbs_set_file_transfer_recv(LinphoneChatMessageCbs *cbs,
+ LinphoneChatMessageCbsFileTransferRecvCb cb) {
cbs->file_transfer_recv = cb;
}
@@ -92,311 +95,118 @@ LinphoneChatMessageCbsFileTransferSendCb linphone_chat_message_cbs_get_file_tran
return cbs->file_transfer_send;
}
-void linphone_chat_message_cbs_set_file_transfer_send(LinphoneChatMessageCbs *cbs, LinphoneChatMessageCbsFileTransferSendCb cb) {
+void linphone_chat_message_cbs_set_file_transfer_send(LinphoneChatMessageCbs *cbs,
+ LinphoneChatMessageCbsFileTransferSendCb cb) {
cbs->file_transfer_send = cb;
}
-LinphoneChatMessageCbsFileTransferProgressIndicationCb linphone_chat_message_cbs_get_file_transfer_progress_indication(const LinphoneChatMessageCbs *cbs) {
+LinphoneChatMessageCbsFileTransferProgressIndicationCb
+linphone_chat_message_cbs_get_file_transfer_progress_indication(const LinphoneChatMessageCbs *cbs) {
return cbs->file_transfer_progress_indication;
}
-void linphone_chat_message_cbs_set_file_transfer_progress_indication(LinphoneChatMessageCbs *cbs, LinphoneChatMessageCbsFileTransferProgressIndicationCb cb) {
+void linphone_chat_message_cbs_set_file_transfer_progress_indication(
+ LinphoneChatMessageCbs *cbs, LinphoneChatMessageCbsFileTransferProgressIndicationCb cb) {
cbs->file_transfer_progress_indication = cb;
}
-static void _linphone_chat_room_send_message(LinphoneChatRoom *cr, LinphoneChatMessage* msg);
-
-static void process_io_error_upload(void *data, const belle_sip_io_error_event_t *event){
- LinphoneChatMessage* msg=(LinphoneChatMessage *)data;
- ms_error("I/O Error during file upload to %s - msg [%p] chat room[%p]", linphone_core_get_file_transfer_server(msg->chat_room->lc), msg, msg->chat_room);
- linphone_chat_message_cancel_file_transfer(msg);
-}
-static void process_auth_requested_upload(void *data, belle_sip_auth_event_t *event){
- LinphoneChatMessage* msg=(LinphoneChatMessage *)data;
- ms_error("Error during file upload: auth requested to connect %s - msg [%p] chat room[%p]", linphone_core_get_file_transfer_server(msg->chat_room->lc), msg, msg->chat_room);
- linphone_chat_message_cancel_file_transfer(msg);
-}
-
-static void process_io_error_download(void *data, const belle_sip_io_error_event_t *event){
- LinphoneChatMessage* msg=(LinphoneChatMessage *)data;
- ms_error("I/O Error during file download %s - msg [%p] chat room[%p]", msg->external_body_url, msg, msg->chat_room);
- msg->state = LinphoneChatMessageStateFileTransferError;
- if (msg->cb) {
- msg->cb(msg, msg->state, msg->cb_ud);
- }
- if (linphone_chat_message_cbs_get_msg_state_changed(msg->callbacks)) {
- linphone_chat_message_cbs_get_msg_state_changed(msg->callbacks)(msg, msg->state);
- }
-}
-static void process_auth_requested_download(void *data, belle_sip_auth_event_t *event){
- LinphoneChatMessage* msg=(LinphoneChatMessage *)data;
- msg->state = LinphoneChatMessageStateFileTransferError;
- ms_error("Error during file download : auth requested to get %s - msg [%p] chat room[%p]", msg->external_body_url, msg, msg->chat_room);
- if (msg->cb) {
- msg->cb(msg, msg->state, msg->cb_ud);
- }
- if (linphone_chat_message_cbs_get_msg_state_changed(msg->callbacks)) {
- linphone_chat_message_cbs_get_msg_state_changed(msg->callbacks)(msg, msg->state);
- }
-}
-
-static void linphone_chat_message_file_transfer_on_progress(belle_sip_body_handler_t *bh, belle_sip_message_t *msg, void *data, size_t offset, size_t total){
- LinphoneChatMessage* chatMsg=(LinphoneChatMessage *)data;
- if (!chatMsg->http_request || belle_http_request_is_cancelled(chatMsg->http_request)) {
- ms_warning("Cancelled request for msg [%p], ignoring %s", chatMsg, __FUNCTION__);
- return;
- }
- if (linphone_chat_message_cbs_get_file_transfer_progress_indication(chatMsg->callbacks)) {
- linphone_chat_message_cbs_get_file_transfer_progress_indication(chatMsg->callbacks)(chatMsg, chatMsg->file_transfer_information, offset, total);
- } else {
- /* Legacy: call back given by application level */
- linphone_core_notify_file_transfer_progress_indication(chatMsg->chat_room->lc, chatMsg, chatMsg->file_transfer_information, offset, total);
- }
-}
-
-static int linphone_chat_message_file_transfer_on_send_body(belle_sip_user_body_handler_t *bh, belle_sip_message_t *msg, void *data, size_t offset, uint8_t *buffer, size_t *size){
- LinphoneChatMessage* chatMsg=(LinphoneChatMessage *)data;
- LinphoneCore *lc = chatMsg->chat_room->lc;
- char *buf = (char *)buffer;
-
- if (!chatMsg->http_request || belle_http_request_is_cancelled(chatMsg->http_request)) {
- ms_warning("Cancelled request for msg [%p], ignoring %s", chatMsg, __FUNCTION__);
- return BELLE_SIP_STOP;
- }
-
- /* if we've not reach the end of file yet, ask for more data*/
- if (offsetfile_transfer_information)){
- char *plainBuffer = NULL;
-
- if (linphone_content_get_key(chatMsg->file_transfer_information) != NULL) { /* if we have a key to cipher the message, use it! */
- /* if this chunk is not the last one, the lenght must be a multiple of block cipher size(16 bytes)*/
- if (offset+*size < linphone_content_get_size(chatMsg->file_transfer_information)) {
- *size -=(*size%16);
- }
- plainBuffer = (char *)malloc(*size);
- }
-
- /* get data from call back */
- if (linphone_chat_message_cbs_get_file_transfer_send(chatMsg->callbacks)) {
- LinphoneBuffer *lb = linphone_chat_message_cbs_get_file_transfer_send(chatMsg->callbacks)(chatMsg, chatMsg->file_transfer_information, offset, *size);
- if (lb == NULL) {
- *size = 0;
- } else {
- *size = linphone_buffer_get_size(lb);
- memcpy(plainBuffer?plainBuffer:buf, linphone_buffer_get_content(lb), *size);
- linphone_buffer_unref(lb);
- }
- } else {
- /* Legacy */
- linphone_core_notify_file_transfer_send(lc, chatMsg, chatMsg->file_transfer_information, plainBuffer?plainBuffer:buf, size);
- }
-
- if (linphone_content_get_key(chatMsg->file_transfer_information) != NULL) { /* if we have a key to cipher the message, use it! */
- lime_encryptFile(linphone_content_get_cryptoContext_address(chatMsg->file_transfer_information), (unsigned char *)linphone_content_get_key(chatMsg->file_transfer_information), *size, plainBuffer, (char*)buffer);
- free(plainBuffer);
- /* check if we reach the end of file */
- if (offset+*size >= linphone_content_get_size(chatMsg->file_transfer_information)) {
- /* conclude file ciphering by calling it context with a zero size */
- lime_encryptFile(linphone_content_get_cryptoContext_address(chatMsg->file_transfer_information), NULL, 0, NULL, NULL);
- }
- }
- }
-
- return BELLE_SIP_CONTINUE;
-}
-
-static void linphone_chat_message_process_response_from_post_file(void *data, const belle_http_response_event_t *event){
- LinphoneChatMessage* msg=(LinphoneChatMessage *)data;
-
- /* check the answer code */
- if (event->response){
- int code=belle_http_response_get_status_code(event->response);
- if (code == 204) { /* this is the reply to the first post to the server - an empty message */
- /* start uploading the file */
- belle_http_request_listener_callbacks_t cbs={0};
- belle_http_request_listener_t *l;
- belle_generic_uri_t *uri;
- belle_sip_multipart_body_handler_t *bh;
- char* ua;
- char *first_part_header;
- belle_sip_body_handler_t *first_part_bh;
-
- /* shall we encrypt the file */
- if (linphone_core_lime_for_file_sharing_enabled(msg->chat_room->lc)) {
- char keyBuffer[FILE_TRANSFER_KEY_SIZE]; /* temporary storage of generated key: 192 bits of key + 64 bits of initial vector */
- /* generate a random 192 bits key + 64 bits of initial vector and store it into the file_transfer_information->key field of the message */
- sal_get_random_bytes((unsigned char *)keyBuffer, FILE_TRANSFER_KEY_SIZE);
- linphone_content_set_key(msg->file_transfer_information, keyBuffer, FILE_TRANSFER_KEY_SIZE); /* key is duplicated in the content private structure */
- /* temporary storage for the Content-disposition header value : use a generic filename to not leak it
- * Actual filename stored in msg->file_transfer_information->name will be set in encrypted message sended to the */
- first_part_header = belle_sip_strdup_printf("form-data; name=\"File\"; filename=\"filename.txt\"");
- } else {
- /* temporary storage for the Content-disposition header value */
- first_part_header = belle_sip_strdup_printf("form-data; name=\"File\"; filename=\"%s\"", linphone_content_get_name(msg->file_transfer_information));
- }
-
- /* create a user body handler to take care of the file and add the content disposition and content-type headers */
- if (msg->file_transfer_filepath != NULL) {
- first_part_bh=(belle_sip_body_handler_t *)belle_sip_file_body_handler_new(msg->file_transfer_filepath,NULL,msg);
- } else if (linphone_content_get_buffer(msg->file_transfer_information) != NULL) {
- first_part_bh=(belle_sip_body_handler_t *)belle_sip_memory_body_handler_new_from_buffer(
- linphone_content_get_buffer(msg->file_transfer_information),
- linphone_content_get_size(msg->file_transfer_information),
- NULL,msg);
- } else {
- first_part_bh=(belle_sip_body_handler_t *)belle_sip_user_body_handler_new(linphone_content_get_size(msg->file_transfer_information),NULL,NULL,linphone_chat_message_file_transfer_on_send_body,msg);
- }
- belle_sip_body_handler_add_header(first_part_bh, belle_sip_header_create("Content-disposition", first_part_header));
- belle_sip_free(first_part_header);
- belle_sip_body_handler_add_header(first_part_bh, (belle_sip_header_t *)belle_sip_header_content_type_create(linphone_content_get_type(msg->file_transfer_information), linphone_content_get_subtype(msg->file_transfer_information)));
-
- /* insert it in a multipart body handler which will manage the boundaries of multipart message */
- bh=belle_sip_multipart_body_handler_new(linphone_chat_message_file_transfer_on_progress, msg, first_part_bh);
-
- /* create the http request: do not include the message header at this point, it is done by bellesip when setting the multipart body handler in the message */
- ua = ms_strdup_printf("%s/%s", linphone_core_get_user_agent_name(), linphone_core_get_user_agent_version());
- uri=belle_generic_uri_parse(linphone_core_get_file_transfer_server(msg->chat_room->lc));
- if (msg->http_request) belle_sip_object_unref(msg->http_request);
- msg->http_request=belle_http_request_create("POST",
- uri,
- belle_sip_header_create("User-Agent",ua),
- NULL);
- belle_sip_object_ref(msg->http_request); /* keep a reference to the http request to be able to cancel it during upload */
- ms_free(ua);
- belle_sip_message_set_body_handler(BELLE_SIP_MESSAGE(msg->http_request),BELLE_SIP_BODY_HANDLER(bh));
- cbs.process_response=linphone_chat_message_process_response_from_post_file;
- cbs.process_io_error=process_io_error_upload;
- cbs.process_auth_requested=process_auth_requested_upload;
- l=belle_http_request_listener_create_from_callbacks(&cbs,msg);
- belle_http_provider_send_request(msg->chat_room->lc->http_provider,msg->http_request,l);
- }
-
- if (code == 200 ) { /* file has been uplaoded correctly, get server reply and send it */
- const char *body = belle_sip_message_get_body((belle_sip_message_t *)event->response);
- belle_sip_object_unref(msg->http_request);
- msg->http_request = NULL;
-
- /* if we have an encryption key for the file, we must insert it into the message and restore the correct filename */
- if (linphone_content_get_key(msg->file_transfer_information) != NULL) {
- /* parse the message body */
- xmlDocPtr xmlMessageBody = xmlParseDoc((const xmlChar *)body);
-
- xmlNodePtr cur = xmlDocGetRootElement(xmlMessageBody);
- if (cur != NULL) {
- cur = cur->xmlChildrenNode;
- while (cur!=NULL) {
- if (!xmlStrcmp(cur->name, (const xmlChar *)"file-info")) { /* we found a file info node, check it has a type="file" attribute */
- xmlChar *typeAttribute = xmlGetProp(cur, (const xmlChar *)"type");
- if(!xmlStrcmp(typeAttribute, (const xmlChar *)"file")) { /* this is the node we are looking for : add a file-key children node */
- xmlNodePtr fileInfoNodeChildren = cur->xmlChildrenNode; /* need to parse the children node to update the file-name one */
- /* convert key to base64 */
- int b64Size = b64_encode(NULL, FILE_TRANSFER_KEY_SIZE, NULL, 0);
- char *keyb64 = (char *)malloc(b64Size+1);
- int xmlStringLength;
-
- b64Size = b64_encode(linphone_content_get_key(msg->file_transfer_information), FILE_TRANSFER_KEY_SIZE, keyb64, b64Size);
- keyb64[b64Size] = '\0'; /* libxml need a null terminated string */
-
- /* add the node containing the key to the file-info node */
- xmlNewTextChild(cur, NULL, (const xmlChar *)"file-key", (const xmlChar *)keyb64);
- xmlFree(typeAttribute);
- free(keyb64);
-
- /* look for the file-name node and update its content */
- while (fileInfoNodeChildren!=NULL) {
- if (!xmlStrcmp(fileInfoNodeChildren->name, (const xmlChar *)"file-name")) { /* we found a the file-name node, update its content with the real filename */
- /* update node content */
- xmlNodeSetContent(fileInfoNodeChildren, (const xmlChar *)(linphone_content_get_name(msg->file_transfer_information)));
- break;
- }
- fileInfoNodeChildren = fileInfoNodeChildren->next;
- }
-
-
- /* dump the xml into msg->message */
- xmlDocDumpFormatMemoryEnc(xmlMessageBody, (xmlChar **)&msg->message, &xmlStringLength, "UTF-8", 0);
-
- break;
- }
- xmlFree(typeAttribute);
- }
- cur = cur->next;
- }
- }
- xmlFreeDoc(xmlMessageBody);
-
- } else { /* no encryption key, transfer in plain, just copy the message sent by server */
- msg->message = ms_strdup(body);
- }
-
- msg->content_type = ms_strdup("application/vnd.gsma.rcs-ft-http+xml");
- msg->state = LinphoneChatMessageStateFileTransferDone;
- if (msg->cb) {
- msg->cb(msg, msg->state, msg->cb_ud);
- }
- if (linphone_chat_message_cbs_get_msg_state_changed(msg->callbacks)) {
- linphone_chat_message_cbs_get_msg_state_changed(msg->callbacks)(msg, msg->state);
- }
- _linphone_chat_room_send_message(msg->chat_room, msg);
- }
- }
-}
-
-
-static void _linphone_chat_message_destroy(LinphoneChatMessage* msg);
-
BELLE_SIP_DECLARE_NO_IMPLEMENTED_INTERFACES(LinphoneChatMessage);
-BELLE_SIP_INSTANCIATE_VPTR(LinphoneChatMessage,belle_sip_object_t,
- (belle_sip_object_destroy_t)_linphone_chat_message_destroy,
- NULL, // clone
- NULL, // marshal
- FALSE
-);
-
-void linphone_core_disable_chat(LinphoneCore *lc, LinphoneReason deny_reason){
- lc->chat_deny_code=deny_reason;
+static void _linphone_chat_room_destroy(LinphoneChatRoom *cr) {
+ bctbx_list_free_with_data(cr->transient_messages, (void (*)(void *))linphone_chat_message_release);
+ linphone_chat_room_delete_composing_idle_timer(cr);
+ linphone_chat_room_delete_composing_refresh_timer(cr);
+ linphone_chat_room_delete_remote_composing_refresh_timer(cr);
+ if (cr->lc != NULL) {
+ if (bctbx_list_find(cr->lc->chatrooms, cr)) {
+ ms_error("LinphoneChatRoom[%p] is destroyed while still being used by the LinphoneCore. This is abnormal."
+ " linphone_core_get_chat_room() doesn't give a reference, there is no need to call "
+ "linphone_chat_room_unref(). "
+ "In order to remove a chat room from the core, use linphone_core_delete_chat_room().",
+ cr);
+ cr->lc->chatrooms = bctbx_list_remove(cr->lc->chatrooms, cr);
+ }
+ }
+ linphone_address_destroy(cr->peer_url);
+ if (cr->pending_message)
+ linphone_chat_message_destroy(cr->pending_message);
+ ms_free(cr->peer);
}
-void linphone_core_enable_chat(LinphoneCore *lc){
- lc->chat_deny_code=LinphoneReasonNone;
+void linphone_chat_message_set_state(LinphoneChatMessage *msg, LinphoneChatMessageState state) {
+ /* do not invoke callbacks on orphan messages */
+ if (state != msg->state && msg->chat_room != NULL) {
+ ms_message("Chat message %p: moving from state %s to %s", msg, linphone_chat_message_state_to_string(msg->state),
+ linphone_chat_message_state_to_string(state));
+ msg->state = state;
+ if (msg->message_state_changed_cb) {
+ msg->message_state_changed_cb(msg, msg->state, msg->message_state_changed_user_data);
+ }
+ if (linphone_chat_message_cbs_get_msg_state_changed(msg->callbacks)) {
+ linphone_chat_message_cbs_get_msg_state_changed(msg->callbacks)(msg, msg->state);
+ }
+ }
}
-bool_t linphone_core_chat_enabled(const LinphoneCore *lc){
- return lc->chat_deny_code!=LinphoneReasonNone;
+BELLE_SIP_INSTANCIATE_VPTR(LinphoneChatMessage, belle_sip_object_t,
+ (belle_sip_object_destroy_t)_linphone_chat_message_destroy,
+ NULL, // clone
+ NULL, // marshal
+ FALSE);
+
+void linphone_core_disable_chat(LinphoneCore *lc, LinphoneReason deny_reason) {
+ lc->chat_deny_code = deny_reason;
}
-const MSList* linphone_core_get_chat_rooms(LinphoneCore *lc) {
+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) {
return lc->chatrooms;
}
-static bool_t linphone_chat_room_matches(LinphoneChatRoom *cr, const LinphoneAddress *from){
- return linphone_address_weak_equal(cr->peer_url,from);
+static bool_t linphone_chat_room_matches(LinphoneChatRoom *cr, const LinphoneAddress *from) {
+ return linphone_address_weak_equal(cr->peer_url, from);
}
-static void _linphone_chat_room_destroy(LinphoneChatRoom *obj);
-
BELLE_SIP_DECLARE_NO_IMPLEMENTED_INTERFACES(LinphoneChatRoom);
BELLE_SIP_INSTANCIATE_VPTR(LinphoneChatRoom, belle_sip_object_t,
- (belle_sip_object_destroy_t)_linphone_chat_room_destroy,
- NULL, // clone
- NULL, // marshal
- FALSE
-);
+ (belle_sip_object_destroy_t)_linphone_chat_room_destroy,
+ NULL, // clone
+ NULL, // marshal
+ FALSE);
-static LinphoneChatRoom * _linphone_core_create_chat_room(LinphoneCore *lc, LinphoneAddress *addr) {
+static LinphoneChatRoom *_linphone_core_create_chat_room_base(LinphoneCore *lc, LinphoneAddress *addr){
LinphoneChatRoom *cr = belle_sip_object_new(LinphoneChatRoom);
cr->lc = lc;
cr->peer = linphone_address_as_string(addr);
cr->peer_url = addr;
cr->unread_count = -1;
- lc->chatrooms = ms_list_append(lc->chatrooms, (void *)cr);
+ cr->received_rtt_characters = NULL;
return cr;
}
-static LinphoneChatRoom * _linphone_core_create_chat_room_from_url(LinphoneCore *lc, const char *to) {
+static LinphoneChatRoom *_linphone_core_create_chat_room(LinphoneCore *lc, LinphoneAddress *addr) {
+ LinphoneChatRoom *cr = _linphone_core_create_chat_room_base(lc, addr);
+ lc->chatrooms = bctbx_list_append(lc->chatrooms, (void *)cr);
+ return cr;
+}
+
+LinphoneChatRoom *_linphone_core_create_chat_room_from_call(LinphoneCall *call){
+ LinphoneChatRoom *cr = _linphone_core_create_chat_room_base(call->core,
+ linphone_address_clone(linphone_call_get_remote_address(call)));
+ cr->call = call;
+ return cr;
+}
+
+static LinphoneChatRoom *_linphone_core_create_chat_room_from_url(LinphoneCore *lc, const char *to) {
LinphoneAddress *parsed_url = NULL;
if ((parsed_url = linphone_core_interpret_url(lc, to)) != NULL) {
return _linphone_core_create_chat_room(lc, parsed_url);
@@ -404,36 +214,36 @@ static LinphoneChatRoom * _linphone_core_create_chat_room_from_url(LinphoneCore
return NULL;
}
-LinphoneChatRoom * _linphone_core_get_chat_room(LinphoneCore *lc, const LinphoneAddress *addr){
- LinphoneChatRoom *cr=NULL;
- MSList *elem;
- for(elem=lc->chatrooms;elem!=NULL;elem=ms_list_next(elem)){
- cr=(LinphoneChatRoom*)elem->data;
- if (linphone_chat_room_matches(cr,addr)){
+LinphoneChatRoom *_linphone_core_get_chat_room(LinphoneCore *lc, const LinphoneAddress *addr) {
+ LinphoneChatRoom *cr = NULL;
+ bctbx_list_t *elem;
+ for (elem = lc->chatrooms; elem != NULL; elem = bctbx_list_next(elem)) {
+ cr = (LinphoneChatRoom *)elem->data;
+ if (linphone_chat_room_matches(cr, addr)) {
break;
}
- cr=NULL;
+ cr = NULL;
}
return cr;
}
-static LinphoneChatRoom * _linphone_core_get_or_create_chat_room(LinphoneCore* lc, const char* to) {
- LinphoneAddress *to_addr=linphone_core_interpret_url(lc,to);
+static LinphoneChatRoom *_linphone_core_get_or_create_chat_room(LinphoneCore *lc, const char *to) {
+ LinphoneAddress *to_addr = linphone_core_interpret_url(lc, to);
LinphoneChatRoom *ret;
- if (to_addr==NULL){
- ms_error("linphone_core_get_or_create_chat_room(): Cannot make a valid address with %s",to);
+ if (to_addr == NULL) {
+ ms_error("linphone_core_get_or_create_chat_room(): Cannot make a valid address with %s", to);
return NULL;
}
- ret=_linphone_core_get_chat_room(lc,to_addr);
+ ret = _linphone_core_get_chat_room(lc, to_addr);
linphone_address_destroy(to_addr);
- if (!ret){
- ret=_linphone_core_create_chat_room_from_url(lc,to);
+ if (!ret) {
+ ret = _linphone_core_create_chat_room_from_url(lc, to);
}
return ret;
}
-LinphoneChatRoom *linphone_core_get_chat_room(LinphoneCore *lc, const LinphoneAddress *addr){
+LinphoneChatRoom *linphone_core_get_chat_room(LinphoneCore *lc, const LinphoneAddress *addr) {
LinphoneChatRoom *ret = _linphone_core_get_chat_room(lc, addr);
if (!ret) {
ret = _linphone_core_create_chat_room(lc, linphone_address_clone(addr));
@@ -441,25 +251,67 @@ LinphoneChatRoom *linphone_core_get_chat_room(LinphoneCore *lc, const LinphoneAd
return ret;
}
-void linphone_core_delete_chat_room(LinphoneCore *lc, LinphoneChatRoom *cr){
- if (ms_list_find(lc->chatrooms, cr)){
- lc->chatrooms = ms_list_remove(cr->lc->chatrooms, cr);
+void linphone_core_delete_chat_room(LinphoneCore *lc, LinphoneChatRoom *cr) {
+ if (bctbx_list_find(lc->chatrooms, cr)) {
+ lc->chatrooms = bctbx_list_remove(cr->lc->chatrooms, cr);
linphone_chat_room_delete_history(cr);
linphone_chat_room_unref(cr);
- }else{
- ms_error("linphone_core_delete_chat_room(): chatroom [%p] isn't part of LinphoneCore.",
- cr);
+ } else {
+ ms_error("linphone_core_delete_chat_room(): chatroom [%p] isn't part of LinphoneCore.", cr);
}
-
}
-LinphoneChatRoom * linphone_core_get_chat_room_from_uri(LinphoneCore *lc, const char *to) {
+LinphoneChatRoom *linphone_core_get_chat_room_from_uri(LinphoneCore *lc, const char *to) {
return _linphone_core_get_or_create_chat_room(lc, to);
}
+bool_t linphone_chat_room_lime_available(LinphoneChatRoom *cr) {
+ if (cr) {
+ switch (linphone_core_lime_enabled(cr->lc)) {
+ case LinphoneLimeDisabled: return FALSE;
+ case LinphoneLimeMandatory: return TRUE;
+ case LinphoneLimePreferred: {
+ FILE *CACHEFD = NULL;
+ if (cr->lc->zrtp_secrets_cache != NULL) {
+ CACHEFD = fopen(cr->lc->zrtp_secrets_cache, "rb+");
+ if (CACHEFD) {
+ size_t cacheSize;
+ xmlDocPtr cacheXml;
+ char *cacheString=ms_load_file_content(CACHEFD, &cacheSize);
+ if (!cacheString){
+ ms_warning("Unable to load content of ZRTP ZID cache to decrypt message");
+ return FALSE;
+ }
+ cacheString[cacheSize] = '\0';
+ cacheSize += 1;
+ fclose(CACHEFD);
+ cacheXml = xmlParseDoc((xmlChar*)cacheString);
+ ms_free(cacheString);
+ if (cacheXml) {
+ bool_t res;
+ limeURIKeys_t associatedKeys;
+ /* retrieve keys associated to the peer URI */
+ associatedKeys.peerURI = (uint8_t *)malloc(strlen(cr->peer)+1);
+ strcpy((char *)(associatedKeys.peerURI), cr->peer);
+ associatedKeys.associatedZIDNumber = 0;
+ associatedKeys.peerKeys = NULL;
+
+ res = (lime_getCachedSndKeysByURI(cacheXml, &associatedKeys) == 0 && associatedKeys.associatedZIDNumber != 0);
+ lime_freeKeys(&associatedKeys);
+ xmlFreeDoc(cacheXml);
+ return res;
+ }
+ }
+ }
+ }
+ }
+ }
+ return FALSE;
+}
+
static void linphone_chat_room_delete_composing_idle_timer(LinphoneChatRoom *cr) {
if (cr->composing_idle_timer) {
- if(cr-> lc && cr->lc->sal)
+ if (cr->lc && cr->lc->sal)
sal_cancel_timer(cr->lc->sal, cr->composing_idle_timer);
belle_sip_object_unref(cr->composing_idle_timer);
cr->composing_idle_timer = NULL;
@@ -468,7 +320,7 @@ static void linphone_chat_room_delete_composing_idle_timer(LinphoneChatRoom *cr)
static void linphone_chat_room_delete_composing_refresh_timer(LinphoneChatRoom *cr) {
if (cr->composing_refresh_timer) {
- if(cr->lc && cr->lc->sal)
+ if (cr->lc && cr->lc->sal)
sal_cancel_timer(cr->lc->sal, cr->composing_refresh_timer);
belle_sip_object_unref(cr->composing_refresh_timer);
cr->composing_refresh_timer = NULL;
@@ -477,32 +329,17 @@ static void linphone_chat_room_delete_composing_refresh_timer(LinphoneChatRoom *
static void linphone_chat_room_delete_remote_composing_refresh_timer(LinphoneChatRoom *cr) {
if (cr->remote_composing_refresh_timer) {
- if(cr->lc && cr->lc->sal)
+ if (cr->lc && cr->lc->sal)
sal_cancel_timer(cr->lc->sal, cr->remote_composing_refresh_timer);
belle_sip_object_unref(cr->remote_composing_refresh_timer);
cr->remote_composing_refresh_timer = NULL;
}
}
-static void _linphone_chat_room_destroy(LinphoneChatRoom *cr){
- ms_list_free_with_data(cr->transient_messages, (void (*)(void*))linphone_chat_message_release);
- linphone_chat_room_delete_composing_idle_timer(cr);
- linphone_chat_room_delete_composing_refresh_timer(cr);
- linphone_chat_room_delete_remote_composing_refresh_timer(cr);
- if (cr->lc != NULL) {
- if (ms_list_find(cr->lc->chatrooms, cr)){
- ms_error("LinphoneChatRoom[%p] is destroyed while still being used by the LinphoneCore. This is abnormal."
- " linphone_core_get_chat_room() doesn't give a reference, there is no need to call linphone_chat_room_unref(). "
- "In order to remove a chat room from the core, use linphone_core_delete_chat_room().",
- cr);
- }
- cr->lc->chatrooms=ms_list_remove(cr->lc->chatrooms, cr);
- }
- linphone_address_destroy(cr->peer_url);
- ms_free(cr->peer);
-}
-
void linphone_chat_room_destroy(LinphoneChatRoom *cr) {
+ if (cr->received_rtt_characters) {
+ cr->received_rtt_characters = bctbx_list_free(cr->received_rtt_characters);
+ }
linphone_chat_room_unref(cr);
}
@@ -511,7 +348,7 @@ void linphone_chat_room_release(LinphoneChatRoom *cr) {
linphone_chat_room_unref(cr);
}
-LinphoneChatRoom * linphone_chat_room_ref(LinphoneChatRoom *cr) {
+LinphoneChatRoom *linphone_chat_room_ref(LinphoneChatRoom *cr) {
belle_sip_object_ref(cr);
return cr;
}
@@ -520,7 +357,7 @@ void linphone_chat_room_unref(LinphoneChatRoom *cr) {
belle_sip_object_unref(cr);
}
-void * linphone_chat_room_get_user_data(const LinphoneChatRoom *cr) {
+void *linphone_chat_room_get_user_data(const LinphoneChatRoom *cr) {
return cr->user_data;
}
@@ -528,194 +365,210 @@ void linphone_chat_room_set_user_data(LinphoneChatRoom *cr, void *ud) {
cr->user_data = ud;
}
-
-static void _linphone_chat_room_send_message(LinphoneChatRoom *cr, LinphoneChatMessage* msg){
- SalOp *op=NULL;
- LinphoneCall *call;
- char* content_type;
- const char *identity=NULL;
- time_t t=time(NULL);
- linphone_chat_message_ref(msg);
- /* Check if we shall upload a file to a server */
- if (msg->file_transfer_information != NULL && msg->content_type == NULL) {
- /* open a transaction with the server and send an empty request(RCS5.1 section 3.5.4.8.3.1) */
- belle_http_request_listener_callbacks_t cbs={0};
- belle_http_request_listener_t *l;
- belle_generic_uri_t *uri;
- const char *transfer_server = linphone_core_get_file_transfer_server(cr->lc);
-
- if (transfer_server == NULL) {
- ms_warning("Cannot send file transfer message: no file transfer server configured.");
- return;
- }
- uri=belle_generic_uri_parse(transfer_server);
-
- msg->http_request=belle_http_request_create("POST",
- uri,
- NULL,
- NULL,
- NULL);
- belle_sip_object_ref(msg->http_request); /* keep a reference on the request to be able to cancel it */
- cbs.process_response=linphone_chat_message_process_response_from_post_file;
- cbs.process_io_error=process_io_error_upload;
- cbs.process_auth_requested=process_auth_requested_upload;
- l=belle_http_request_listener_create_from_callbacks(&cbs,msg); /* give msg to listener to be able to start the actual file upload when server answer a 204 No content */
- belle_http_provider_send_request(cr->lc->http_provider,msg->http_request,l);
+void _linphone_chat_room_send_message(LinphoneChatRoom *cr, LinphoneChatMessage *msg) {
+ /*stubed rtt text*/
+ if (cr->call && linphone_call_params_realtime_text_enabled(linphone_call_get_current_params(cr->call))) {
+ uint32_t new_line = 0x2028;
+ linphone_chat_message_put_char(msg, new_line); // New Line
linphone_chat_message_unref(msg);
return;
}
- if (lp_config_get_int(cr->lc->config,"sip","chat_use_call_dialogs",0)){
- if((call = linphone_core_get_call_by_remote_address(cr->lc,cr->peer))!=NULL){
- if (call->state==LinphoneCallConnected ||
- call->state==LinphoneCallStreamsRunning ||
- call->state==LinphoneCallPaused ||
- call->state==LinphoneCallPausing ||
- call->state==LinphoneCallPausedByRemote){
- ms_message("send SIP message through the existing call.");
- op = call->op;
- identity=linphone_core_find_best_identity(cr->lc,linphone_call_get_remote_address(call));
- }
+ msg->dir = LinphoneChatMessageOutgoing;
+
+
+ /* Check if we shall upload a file to a server */
+ if (msg->file_transfer_information != NULL && msg->content_type == NULL) {
+ /* open a transaction with the server and send an empty request(RCS5.1 section 3.5.4.8.3.1) */
+ if (linphone_chat_room_upload_file(msg) == 0) {
+ // add to transient list only if message is going out
+ cr->transient_messages = bctbx_list_append(cr->transient_messages, linphone_chat_message_ref(msg));
+ } else {
+ linphone_chat_message_unref(msg);
+ return;
}
- }
- msg->time=t;
- if (op==NULL){
- LinphoneProxyConfig *proxy=linphone_core_lookup_known_proxy(cr->lc,cr->peer_url);
- if (proxy){
- identity=linphone_proxy_config_get_identity(proxy);
- }else identity=linphone_core_get_primary_contact(cr->lc);
- /*sending out of calls*/
- msg->op = op = sal_op_new(cr->lc->sal);
- linphone_configure_op(cr->lc,op,cr->peer_url,msg->custom_headers,lp_config_get_int(cr->lc->config,"sip","chat_msg_with_contact",0));
- sal_op_set_user_pointer(op, msg); /*if out of call, directly store msg*/
- }
-
-
- if (msg->external_body_url) {
- content_type=ms_strdup_printf("message/external-body; access-type=URL; URL=\"%s\"",msg->external_body_url);
- sal_message_send(op,identity,cr->peer,content_type, NULL, NULL);
- ms_free(content_type);
} else {
- char *peer_uri = linphone_address_as_string_uri_only(linphone_chat_room_get_peer_address(cr));
- const char * content_type;
-
- if (linphone_core_lime_enabled(cr->lc)) {
- linphone_chat_message_ref(msg); /* ref the message or it may be destroyed by callback if the encryption failed */
- if (msg->content_type && strcmp(msg->content_type, "application/vnd.gsma.rcs-ft-http+xml") == 0) {
- content_type = "application/cipher.vnd.gsma.rcs-ft-http+xml"; /* it's a file transfer, content type shall be set to application/cipher.vnd.gsma.rcs-ft-http+xml*/
- } else {
- content_type = "xml/cipher";
+ SalOp *op = NULL;
+ LinphoneCall *call=NULL;
+ char *content_type;
+ const char *identity = NULL;
+ // add to transient list
+ cr->transient_messages = bctbx_list_append(cr->transient_messages, linphone_chat_message_ref(msg));
+ msg->time = ms_time(0);
+ if (lp_config_get_int(cr->lc->config, "sip", "chat_use_call_dialogs", 0) != 0) {
+ if ((call = linphone_core_get_call_by_remote_address(cr->lc, cr->peer)) != NULL) {
+ if (call->state == LinphoneCallConnected || call->state == LinphoneCallStreamsRunning ||
+ call->state == LinphoneCallPaused || call->state == LinphoneCallPausing ||
+ call->state == LinphoneCallPausedByRemote) {
+ ms_message("send SIP msg through the existing call.");
+ op = call->op;
+ identity = linphone_core_find_best_identity(cr->lc, linphone_call_get_remote_address(call));
+ }
}
- } else {
- content_type = msg->content_type;
+ }
+ if (op == NULL) {
+ LinphoneProxyConfig *proxy = linphone_core_lookup_known_proxy(cr->lc, cr->peer_url);
+ if (proxy) {
+ identity = linphone_proxy_config_get_identity(proxy);
+ } else
+ identity = linphone_core_get_primary_contact(cr->lc);
+ /*sending out of calls*/
+ msg->op = op = sal_op_new(cr->lc->sal);
+ linphone_configure_op(cr->lc, op, cr->peer_url, msg->custom_headers,
+ lp_config_get_int(cr->lc->config, "sip", "chat_msg_with_contact", 0));
+ sal_op_set_user_pointer(op, msg); /*if out of call, directly store msg*/
}
- if (content_type == NULL) {
- sal_text_send(op, identity, cr->peer,msg->message);
+ if (msg->external_body_url) {
+ content_type = ms_strdup_printf("message/external-body; access-type=URL; URL=\"%s\"", msg->external_body_url);
+ sal_message_send(op, identity, cr->peer, content_type, NULL, NULL);
+ ms_free(content_type);
} else {
- sal_message_send(op, identity, cr->peer, content_type, msg->message, peer_uri);
+ char *peer_uri = linphone_address_as_string_uri_only(linphone_chat_room_get_peer_address(cr));
+ const char *content_type;
+
+ if (linphone_chat_room_lime_available(cr)) {
+ /* ref the msg or it may be destroyed by callback if the encryption failed */
+ if (msg->content_type && strcmp(msg->content_type, "application/vnd.gsma.rcs-ft-http+xml") == 0) {
+ /* it's a file transfer, content type shall be set to
+ application/cipher.vnd.gsma.rcs-ft-http+xml*/
+ content_type = "application/cipher.vnd.gsma.rcs-ft-http+xml";
+ } else {
+ content_type = "xml/cipher";
+ }
+ } else {
+ content_type = msg->content_type;
+ }
+
+ if (content_type == NULL) {
+ sal_text_send(op, identity, cr->peer, msg->message);
+ } else {
+ sal_message_send(op, identity, cr->peer, content_type, msg->message, peer_uri);
+ }
+ ms_free(peer_uri);
}
- ms_free(peer_uri);
+
+ if (msg->from){
+ /*
+ * BUG
+ * the file transfer message constructor sets the from, but doesn't do it as well as here.
+ */
+ linphone_address_destroy(msg->from);
+ }
+ msg->from = linphone_address_new(identity);
+ msg->storage_id = linphone_chat_message_store(msg);
+
+ if (cr->unread_count >= 0 && !msg->is_read)
+ cr->unread_count++;
+
+ if (cr->is_composing == LinphoneIsComposingActive) {
+ cr->is_composing = LinphoneIsComposingIdle;
+ }
+ linphone_chat_room_delete_composing_idle_timer(cr);
+ linphone_chat_room_delete_composing_refresh_timer(cr);
+
+ if (call && call->op == op) {
+ /*In this case, chat delivery status is not notified, so unrefing chat message right now*/
+ /*Might be better fixed by delivering status, but too costly for now*/
+ msg->chat_room->transient_messages = bctbx_list_remove(msg->chat_room->transient_messages, msg);
+ linphone_chat_message_unref(msg);
+ linphone_chat_message_unref(msg);
+ return;
+ }
+
+
}
-
- msg->dir=LinphoneChatMessageOutgoing;
- msg->from=linphone_address_new(identity);
- msg->storage_id=linphone_chat_message_store(msg);
-
- if(cr->unread_count >= 0 && !msg->is_read) cr->unread_count++;
-
- // add to transient list
- cr->transient_messages = ms_list_append(cr->transient_messages, linphone_chat_message_ref(msg));
-
- if (cr->is_composing == LinphoneIsComposingActive) {
- cr->is_composing = LinphoneIsComposingIdle;
- }
- linphone_chat_room_delete_composing_idle_timer(cr);
- linphone_chat_room_delete_composing_refresh_timer(cr);
- linphone_chat_message_unref(msg);
-}
-
-void linphone_chat_message_update_state(LinphoneChatMessage* chat_msg ) {
- linphone_chat_message_store_state(chat_msg);
-
- if( chat_msg->state == LinphoneChatMessageStateDelivered
- || chat_msg->state == LinphoneChatMessageStateNotDelivered ){
- // message is not transient anymore, we can remove it from our transient list and unref it :
- chat_msg->chat_room->transient_messages = ms_list_remove(chat_msg->chat_room->transient_messages, chat_msg);
- linphone_chat_message_unref(chat_msg);
+ // if operation failed, we should not change message state
+ if (msg->dir == LinphoneChatMessageOutgoing) {
+ linphone_chat_message_set_state(msg, LinphoneChatMessageStateInProgress);
+ }
+}
+
+void linphone_chat_message_update_state(LinphoneChatMessage *msg, LinphoneChatMessageState new_state) {
+ linphone_chat_message_set_state(msg, new_state);
+ linphone_chat_message_store_state(msg);
+
+ if (msg->state == LinphoneChatMessageStateDelivered || msg->state == LinphoneChatMessageStateNotDelivered) {
+ // msg is not transient anymore, we can remove it from our transient list and unref it
+ msg->chat_room->transient_messages = bctbx_list_remove(msg->chat_room->transient_messages, msg);
+ linphone_chat_message_unref(msg);
}
}
-/**
- * Send a message to peer member of this chat room.
- * @deprecated linphone_chat_room_send_message2() gives more control on the message expedition.
- * @param cr #LinphoneChatRoom object
- * @param msg message to be sent
- */
void linphone_chat_room_send_message(LinphoneChatRoom *cr, const char *msg) {
- _linphone_chat_room_send_message(cr,linphone_chat_room_create_message(cr,msg));
+ _linphone_chat_room_send_message(cr, linphone_chat_room_create_message(cr, msg));
}
-void linphone_chat_room_message_received(LinphoneChatRoom *cr, LinphoneCore *lc, LinphoneChatMessage *msg){
- if (msg->message){
+void linphone_chat_room_message_received(LinphoneChatRoom *cr, LinphoneCore *lc, LinphoneChatMessage *msg) {
+ if (msg->message) {
/*legacy API*/
linphone_core_notify_text_message_received(lc, cr, msg->from, msg->message);
}
- linphone_core_notify_message_received(lc, cr,msg);
+ linphone_core_notify_message_received(lc, cr, msg);
cr->remote_is_composing = LinphoneIsComposingIdle;
linphone_core_notify_is_composing_received(cr->lc, cr);
}
-void linphone_core_message_received(LinphoneCore *lc, SalOp *op, const SalMessage *sal_msg){
- LinphoneChatRoom *cr=NULL;
+void linphone_core_message_received(LinphoneCore *lc, SalOp *op, const SalMessage *sal_msg) {
+ LinphoneChatRoom *cr = NULL;
LinphoneAddress *addr;
- LinphoneChatMessage* msg;
+ LinphoneChatMessage *msg;
const SalCustomHeader *ch;
- addr=linphone_address_new(sal_msg->from);
+ addr = linphone_address_new(sal_msg->from);
linphone_address_clean(addr);
- cr=linphone_core_get_chat_room(lc,addr);
+ cr = linphone_core_get_chat_room(lc, addr);
- if (sal_msg->content_type != NULL) { /* content_type field is, for now, used only for rcs file transfer but we shall strcmp it with "application/vnd.gsma.rcs-ft-http+xml" */
+ if (sal_msg->content_type !=
+ NULL) { /* content_type field is, for now, used only for rcs file transfer but we shall strcmp it with
+ "application/vnd.gsma.rcs-ft-http+xml" */
xmlChar *file_url = NULL;
xmlDocPtr xmlMessageBody;
xmlNodePtr cur;
- msg = linphone_chat_room_create_message(cr, NULL); /* create a message with empty body */
- msg->content_type = ms_strdup(sal_msg->content_type); /* add the content_type "application/vnd.gsma.rcs-ft-http+xml" */
+ msg = linphone_chat_room_create_message(cr, NULL); /* create a msg with empty body */
+ msg->content_type =
+ ms_strdup(sal_msg->content_type); /* add the content_type "application/vnd.gsma.rcs-ft-http+xml" */
msg->file_transfer_information = linphone_content_new();
- /* parse the message body to get all informations from it */
+ /* parse the msg body to get all informations from it */
xmlMessageBody = xmlParseDoc((const xmlChar *)sal_msg->text);
cur = xmlDocGetRootElement(xmlMessageBody);
if (cur != NULL) {
cur = cur->xmlChildrenNode;
- while (cur!=NULL) {
- if (!xmlStrcmp(cur->name, (const xmlChar *)"file-info")) { /* we found a file info node, check it has a type="file" attribute */
+ while (cur != NULL) {
+ if (!xmlStrcmp(
+ cur->name, (const xmlChar *)"file-info")) { /* we found a file info node, check it has a
+ type="file" attribute */
xmlChar *typeAttribute = xmlGetProp(cur, (const xmlChar *)"type");
- if(!xmlStrcmp(typeAttribute, (const xmlChar *)"file")) { /* this is the node we are looking for */
+ if (!xmlStrcmp(typeAttribute, (const xmlChar *)"file")) { /* this is the node we are looking for */
cur = cur->xmlChildrenNode; /* now loop on the content of the file-info node */
- while (cur!=NULL) {
+ while (cur != NULL) {
if (!xmlStrcmp(cur->name, (const xmlChar *)"file-size")) {
xmlChar *fileSizeString = xmlNodeListGetString(xmlMessageBody, cur->xmlChildrenNode, 1);
- linphone_content_set_size(msg->file_transfer_information, strtol((const char*)fileSizeString, NULL, 10));
+ linphone_content_set_size(msg->file_transfer_information,
+ strtol((const char *)fileSizeString, NULL, 10));
xmlFree(fileSizeString);
}
if (!xmlStrcmp(cur->name, (const xmlChar *)"file-name")) {
- linphone_content_set_name(msg->file_transfer_information, (const char *)xmlNodeListGetString(xmlMessageBody, cur->xmlChildrenNode, 1));
+ xmlChar *filename = xmlNodeListGetString(xmlMessageBody, cur->xmlChildrenNode, 1);
+ linphone_content_set_name(
+ msg->file_transfer_information,
+ (char *)filename);
+ xmlFree(filename);
}
if (!xmlStrcmp(cur->name, (const xmlChar *)"content-type")) {
xmlChar *contentType = xmlNodeListGetString(xmlMessageBody, cur->xmlChildrenNode, 1);
int contentTypeIndex = 0;
char *type;
char *subtype;
- while (contentType[contentTypeIndex]!='/' && contentType[contentTypeIndex]!='\0') {
+ while (contentType[contentTypeIndex] != '/' && contentType[contentTypeIndex] != '\0') {
contentTypeIndex++;
}
type = ms_strndup((char *)contentType, contentTypeIndex);
- subtype = ms_strdup(((char *)contentType+contentTypeIndex+1));
+ subtype = ms_strdup(((char *)contentType + contentTypeIndex + 1));
linphone_content_set_type(msg->file_transfer_information, type);
linphone_content_set_subtype(msg->file_transfer_information, subtype);
ms_free(subtype);
@@ -723,22 +576,26 @@ void linphone_core_message_received(LinphoneCore *lc, SalOp *op, const SalMessag
xmlFree(contentType);
}
if (!xmlStrcmp(cur->name, (const xmlChar *)"data")) {
- file_url = xmlGetProp(cur, (const xmlChar *)"url");
+ file_url = xmlGetProp(cur, (const xmlChar *)"url");
}
- if (!xmlStrcmp(cur->name, (const xmlChar *)"file-key")) { /* there is a key in the message: file has been encrypted */
+ if (!xmlStrcmp(cur->name,
+ (const xmlChar *)"file-key")) { /* there is a key in the msg: file has
+ been encrypted */
/* convert the key from base 64 */
xmlChar *keyb64 = xmlNodeListGetString(xmlMessageBody, cur->xmlChildrenNode, 1);
- int keyLength = b64_decode((char *)keyb64, strlen((char *)keyb64), NULL, 0);
+ size_t keyLength = b64_decode((char *)keyb64, strlen((char *)keyb64), NULL, 0);
uint8_t *keyBuffer = (uint8_t *)malloc(keyLength);
/* decode the key into local key buffer */
b64_decode((char *)keyb64, strlen((char *)keyb64), keyBuffer, keyLength);
- linphone_content_set_key(msg->file_transfer_information, (char *)keyBuffer, keyLength); /* duplicate key value into the linphone content private structure */
+ linphone_content_set_key(
+ msg->file_transfer_information, (char *)keyBuffer,
+ strlen((char *)keyBuffer)); /* duplicate key value into the linphone content private structure */
xmlFree(keyb64);
free(keyBuffer);
}
- cur=cur->next;
+ cur = cur->next;
}
xmlFree(typeAttribute);
break;
@@ -752,35 +609,39 @@ void linphone_core_message_received(LinphoneCore *lc, SalOp *op, const SalMessag
linphone_chat_message_set_external_body_url(msg, (const char *)file_url);
xmlFree(file_url);
- } else { /* message is not rcs file transfer, create it with provided sal_msg->text as ->message */
+ } else { /* msg is not rcs file transfer, create it with provided sal_msg->text as ->msg */
msg = linphone_chat_room_create_message(cr, sal_msg->text);
}
linphone_chat_message_set_from(msg, cr->peer_url);
{
LinphoneAddress *to;
- to=sal_op_get_to(op) ? linphone_address_new(sal_op_get_to(op)) : linphone_address_new(linphone_core_get_identity(lc));
- msg->to=to;
+ to = sal_op_get_to(op) ? linphone_address_new(sal_op_get_to(op))
+ : linphone_address_new(linphone_core_get_identity(lc));
+ msg->to = to;
}
- msg->time=sal_msg->time;
- msg->state=LinphoneChatMessageStateDelivered;
- msg->is_read=FALSE;
- msg->dir=LinphoneChatMessageIncoming;
- ch=sal_op_get_recv_custom_header(op);
- if (ch) msg->custom_headers=sal_custom_header_clone(ch);
+ msg->time = sal_msg->time;
+ msg->state = LinphoneChatMessageStateDelivered;
+ msg->is_read = FALSE;
+ msg->dir = LinphoneChatMessageIncoming;
+ ch = sal_op_get_recv_custom_header(op);
+ if (ch)
+ msg->custom_headers = sal_custom_header_clone(ch);
if (sal_msg->url) {
linphone_chat_message_set_external_body_url(msg, sal_msg->url);
}
linphone_address_destroy(addr);
- msg->storage_id=linphone_chat_message_store(msg);
+ msg->storage_id = linphone_chat_message_store(msg);
- if(cr->unread_count < 0) cr->unread_count = 1;
- else cr->unread_count++;
+ if (cr->unread_count < 0)
+ cr->unread_count = 1;
+ else
+ cr->unread_count++;
- linphone_chat_room_message_received(cr,lc,msg);
+ linphone_chat_room_message_received(cr, lc, msg);
linphone_chat_message_unref(msg);
}
@@ -800,20 +661,24 @@ static void process_im_is_composing_notification(LinphoneChatRoom *cr, xmlparsin
xmlXPathObjectPtr iscomposing_object;
const char *state_str = NULL;
const char *refresh_str = NULL;
- int refresh_duration = lp_config_get_int(cr->lc->config, "sip", "composing_remote_refresh_timeout", COMPOSING_DEFAULT_REMOTE_REFRESH_TIMEOUT);
+ int refresh_duration = lp_config_get_int(cr->lc->config, "sip", "composing_remote_refresh_timeout",
+ COMPOSING_DEFAULT_REMOTE_REFRESH_TIMEOUT);
int i;
LinphoneIsComposingState state = LinphoneIsComposingIdle;
- if (linphone_create_xml_xpath_context(xml_ctx) < 0) return;
+ if (linphone_create_xml_xpath_context(xml_ctx) < 0)
+ return;
- xmlXPathRegisterNs(xml_ctx->xpath_ctx, (const xmlChar *)"xsi", (const xmlChar *)"urn:ietf:params:xml:ns:im-iscomposing");
+ xmlXPathRegisterNs(xml_ctx->xpath_ctx, (const xmlChar *)"xsi",
+ (const xmlChar *)"urn:ietf:params:xml:ns:im-iscomposing");
iscomposing_object = linphone_get_xml_xpath_object_for_node_list(xml_ctx, iscomposing_prefix);
- if (iscomposing_object != NULL){
- if(iscomposing_object->nodesetval != NULL) {
+ if (iscomposing_object != NULL) {
+ if (iscomposing_object->nodesetval != NULL) {
for (i = 1; i <= iscomposing_object->nodesetval->nodeNr; i++) {
snprintf(xpath_str, sizeof(xpath_str), "%s[%i]/xsi:state", iscomposing_prefix, i);
state_str = linphone_get_xml_text_content(xml_ctx, xpath_str);
- if (state_str == NULL) continue;
+ if (state_str == NULL)
+ continue;
snprintf(xpath_str, sizeof(xpath_str), "%s[%i]/xsi:refresh", iscomposing_prefix, i);
refresh_str = linphone_get_xml_text_content(xml_ctx, xpath_str);
}
@@ -828,7 +693,9 @@ static void process_im_is_composing_notification(LinphoneChatRoom *cr, xmlparsin
refresh_duration = atoi(refresh_str);
}
if (!cr->remote_composing_refresh_timer) {
- cr->remote_composing_refresh_timer = sal_create_timer(cr->lc->sal, linphone_chat_room_remote_refresh_composing_expired, cr, refresh_duration * 1000, "composing remote refresh timeout");
+ cr->remote_composing_refresh_timer =
+ sal_create_timer(cr->lc->sal, linphone_chat_room_remote_refresh_composing_expired, cr,
+ refresh_duration * 1000, "composing remote refresh timeout");
} else {
belle_sip_source_set_timeout(cr->remote_composing_refresh_timer, refresh_duration * 1000);
}
@@ -848,7 +715,7 @@ static void process_im_is_composing_notification(LinphoneChatRoom *cr, xmlparsin
static void linphone_chat_room_notify_is_composing(LinphoneChatRoom *cr, const char *text) {
xmlparsing_context_t *xml_ctx = linphone_xmlparsing_context_new();
xmlSetGenericErrorFunc(xml_ctx, linphone_xmlparsing_genericxml_error);
- xml_ctx->doc = xmlReadDoc((const unsigned char*)text, 0, NULL, 0);
+ xml_ctx->doc = xmlReadDoc((const unsigned char *)text, 0, NULL, 0);
if (xml_ctx->doc != NULL) {
process_im_is_composing_notification(cr, xml_ctx);
} else {
@@ -870,70 +737,65 @@ bool_t linphone_chat_room_is_remote_composing(const LinphoneChatRoom *cr) {
return (cr->remote_is_composing == LinphoneIsComposingActive) ? TRUE : FALSE;
}
-LinphoneCore* linphone_chat_room_get_lc(LinphoneChatRoom *cr){
+LinphoneCore *linphone_chat_room_get_lc(LinphoneChatRoom *cr) {
+ return linphone_chat_room_get_core(cr);
+}
+
+LinphoneCore *linphone_chat_room_get_core(LinphoneChatRoom *cr) {
return cr->lc;
}
-LinphoneCore* linphone_chat_room_get_core(LinphoneChatRoom *cr){
- return cr->lc;
-}
-
-const LinphoneAddress* linphone_chat_room_get_peer_address(LinphoneChatRoom *cr) {
+const LinphoneAddress *linphone_chat_room_get_peer_address(LinphoneChatRoom *cr) {
return cr->peer_url;
}
-LinphoneChatMessage* linphone_chat_room_create_message(LinphoneChatRoom *cr, const char* message) {
- LinphoneChatMessage* msg = belle_sip_object_new(LinphoneChatMessage);
- msg->callbacks=linphone_chat_message_cbs_new();
- msg->chat_room=(LinphoneChatRoom*)cr;
- msg->message=message?ms_strdup(message):NULL;
- msg->is_read=TRUE;
- msg->content_type = NULL; /* this property is used only when transfering file */
+LinphoneChatMessage *linphone_chat_room_create_message(LinphoneChatRoom *cr, const char *message) {
+ LinphoneChatMessage *msg = belle_sip_object_new(LinphoneChatMessage);
+ msg->state = LinphoneChatMessageStateIdle;
+ msg->callbacks = linphone_chat_message_cbs_new();
+ msg->chat_room = (LinphoneChatRoom *)cr;
+ msg->message = message ? ms_strdup(message) : NULL;
+ msg->is_read = TRUE;
+ msg->content_type = NULL; /* this property is used only when transfering file */
msg->file_transfer_information = NULL; /* this property is used only when transfering file */
msg->http_request = NULL;
+ msg->time = ms_time(0);
return msg;
}
-LinphoneChatMessage* linphone_chat_room_create_message_2(
- LinphoneChatRoom *cr, const char* message, const char* external_body_url,
- LinphoneChatMessageState state, time_t time, bool_t is_read, bool_t is_incoming) {
- LinphoneCore *lc=linphone_chat_room_get_lc(cr);
-
- LinphoneChatMessage* msg = belle_sip_object_new(LinphoneChatMessage);
- msg->callbacks=linphone_chat_message_cbs_new();
- msg->chat_room=(LinphoneChatRoom*)cr;
- msg->message=message?ms_strdup(message):NULL;
- msg->external_body_url=external_body_url?ms_strdup(external_body_url):NULL;
- msg->time=time;
- msg->state=state;
- msg->is_read=is_read;
- msg->content_type = NULL; /* this property is used only when transfering file */
- msg->file_transfer_information = NULL; /* this property is used only when transfering file */
+LinphoneChatMessage *linphone_chat_room_create_message_2(LinphoneChatRoom *cr, const char *message,
+ const char *external_body_url, LinphoneChatMessageState state,
+ time_t time, bool_t is_read, bool_t is_incoming) {
+ LinphoneChatMessage *msg = linphone_chat_room_create_message(cr, message);
+ LinphoneCore *lc = linphone_chat_room_get_core(cr);
+ msg->external_body_url = external_body_url ? ms_strdup(external_body_url) : NULL;
+ msg->time = time;
+ msg->is_read = is_read;
+ linphone_chat_message_set_state(msg, state);
if (is_incoming) {
- msg->dir=LinphoneChatMessageIncoming;
+ msg->dir = LinphoneChatMessageIncoming;
linphone_chat_message_set_from(msg, linphone_chat_room_get_peer_address(cr));
- linphone_chat_message_set_to(msg, linphone_address_new(linphone_core_get_identity(lc)));
+ msg->to = linphone_address_new(linphone_core_get_identity(lc)); /*direct assignment*/
} else {
- msg->dir=LinphoneChatMessageOutgoing;
+ msg->dir = LinphoneChatMessageOutgoing;
linphone_chat_message_set_to(msg, linphone_chat_room_get_peer_address(cr));
- linphone_chat_message_set_from(msg, linphone_address_new(linphone_core_get_identity(lc)));
+ msg->from = linphone_address_new(linphone_core_get_identity(lc));/*direct assignment*/
}
return msg;
}
-void linphone_chat_room_send_message2(LinphoneChatRoom *cr, LinphoneChatMessage* msg,LinphoneChatMessageStateChangedCb status_cb, void* ud) {
- msg->cb=status_cb;
- msg->cb_ud=ud;
- msg->state=LinphoneChatMessageStateInProgress;
+void linphone_chat_room_send_message2(LinphoneChatRoom *cr, LinphoneChatMessage *msg,
+ LinphoneChatMessageStateChangedCb status_cb, void *ud) {
+ msg->message_state_changed_cb = status_cb;
+ msg->message_state_changed_user_data = ud;
_linphone_chat_room_send_message(cr, msg);
}
void linphone_chat_room_send_chat_message(LinphoneChatRoom *cr, LinphoneChatMessage *msg) {
- msg->state = LinphoneChatMessageStateInProgress;
_linphone_chat_room_send_message(cr, msg);
}
-static char * linphone_chat_room_create_is_composing_xml(LinphoneChatRoom *cr) {
+static char *linphone_chat_room_create_is_composing_xml(LinphoneChatRoom *cr) {
xmlBufferPtr buf;
xmlTextWriterPtr writer;
int err;
@@ -952,23 +814,26 @@ static char * linphone_chat_room_create_is_composing_xml(LinphoneChatRoom *cr) {
err = xmlTextWriterStartDocument(writer, "1.0", "UTF-8", NULL);
if (err >= 0) {
- err = xmlTextWriterStartElementNS(writer, NULL, (const xmlChar *)"isComposing", (const xmlChar *)"urn:ietf:params:xml:ns:im-iscomposing");
+ err = xmlTextWriterStartElementNS(writer, NULL, (const xmlChar *)"isComposing",
+ (const xmlChar *)"urn:ietf:params:xml:ns:im-iscomposing");
}
if (err >= 0) {
- err = xmlTextWriterWriteAttributeNS(writer, (const xmlChar *)"xmlns", (const xmlChar *)"xsi",
- NULL, (const xmlChar *)"http://www.w3.org/2001/XMLSchema-instance");
+ err = xmlTextWriterWriteAttributeNS(writer, (const xmlChar *)"xmlns", (const xmlChar *)"xsi", NULL,
+ (const xmlChar *)"http://www.w3.org/2001/XMLSchema-instance");
}
if (err >= 0) {
- err = xmlTextWriterWriteAttributeNS(writer, (const xmlChar *)"xsi", (const xmlChar *)"schemaLocation",
- NULL, (const xmlChar *)"urn:ietf:params:xml:ns:im-composing iscomposing.xsd");
+ err = xmlTextWriterWriteAttributeNS(writer, (const xmlChar *)"xsi", (const xmlChar *)"schemaLocation", NULL,
+ (const xmlChar *)"urn:ietf:params:xml:ns:im-composing iscomposing.xsd");
}
if (err >= 0) {
err = xmlTextWriterWriteElement(writer, (const xmlChar *)"state",
- (cr->is_composing == LinphoneIsComposingActive) ? (const xmlChar *)"active" : (const xmlChar *)"idle");
+ (cr->is_composing == LinphoneIsComposingActive) ? (const xmlChar *)"active"
+ : (const xmlChar *)"idle");
}
if ((err >= 0) && (cr->is_composing == LinphoneIsComposingActive)) {
- char refresh_str[4] = { 0 };
- int refresh_timeout = lp_config_get_int(cr->lc->config, "sip", "composing_refresh_timeout", COMPOSING_DEFAULT_REFRESH_TIMEOUT);
+ char refresh_str[4] = {0};
+ int refresh_timeout =
+ lp_config_get_int(cr->lc->config, "sip", "composing_refresh_timeout", COMPOSING_DEFAULT_REFRESH_TIMEOUT);
snprintf(refresh_str, sizeof(refresh_str), "%u", refresh_timeout);
err = xmlTextWriterWriteElement(writer, (const xmlChar *)"refresh", (const xmlChar *)refresh_str);
}
@@ -999,7 +864,8 @@ static void linphone_chat_room_send_is_composing_notification(LinphoneChatRoom *
identity = linphone_core_get_primary_contact(cr->lc);
/*sending out of calls*/
op = sal_op_new(cr->lc->sal);
- linphone_configure_op(cr->lc, op, cr->peer_url, NULL, lp_config_get_int(cr->lc->config, "sip", "chat_msg_with_contact", 0));
+ linphone_configure_op(cr->lc, op, cr->peer_url, NULL,
+ lp_config_get_int(cr->lc->config, "sip", "chat_msg_with_contact", 0));
content = linphone_chat_room_create_is_composing_xml(cr);
if (content != NULL) {
@@ -1009,6 +875,137 @@ static void linphone_chat_room_send_is_composing_notification(LinphoneChatRoom *
}
}
+static char* utf8_to_char(uint32_t ic) {
+ char *result = ms_malloc(sizeof(char) * 5);
+ int size = 0;
+ if (ic < 0x80) {
+ result[0] = ic;
+ size = 1;
+ } else if (ic < 0x800) {
+ result[1] = 0x80 + ((ic & 0x3F));
+ result[0] = 0xC0 + ((ic >> 6) & 0x1F);
+ size = 2;
+ } else if (ic < 0x100000) {
+ result[2] = 0x80 + (ic & 0x3F);
+ result[1] = 0x80 + ((ic >> 6) & 0x3F);
+ result[0] = 0xE0 + ((ic >> 12) & 0xF);
+ size = 3;
+ } else if (ic < 0x110000) {
+ result[3] = 0x80 + (ic & 0x3F);
+ result[2] = 0x80 + ((ic >> 6) & 0x3F);
+ result[1] = 0x80 + ((ic >> 12) & 0x3F);
+ result[0] = 0xF0 + ((ic >> 18) & 0x7);
+ size = 4;
+ }
+ result[size] = '\0';
+ return result;
+}
+
+void linphone_core_real_time_text_received(LinphoneCore *lc, LinphoneChatRoom *cr, uint32_t character, LinphoneCall *call) {
+ uint32_t new_line = 0x2028;
+ uint32_t crlf = 0x0D0A;
+ uint32_t lf = 0x0A;
+
+ if (call && linphone_call_params_realtime_text_enabled(linphone_call_get_current_params(call))) {
+ LinphoneChatMessageCharacter *cmc = ms_new0(LinphoneChatMessageCharacter, 1);
+
+ if (cr->pending_message == NULL) {
+ cr->pending_message = linphone_chat_room_create_message(cr, "");
+ }
+
+ cmc->value = character;
+ cmc->has_been_read = FALSE;
+ cr->received_rtt_characters = bctbx_list_append(cr->received_rtt_characters, (void *)cmc);
+
+ cr->remote_is_composing = LinphoneIsComposingActive;
+ linphone_core_notify_is_composing_received(cr->lc, cr);
+
+ if (character == new_line || character == crlf || character == lf) {
+ // End of message
+ LinphoneChatMessage *msg = cr->pending_message;
+ ms_debug("New line received, forge a message with content %s", cr->pending_message->message);
+
+ linphone_chat_message_set_from(msg, cr->peer_url);
+ if (msg->to)
+ linphone_address_destroy(msg->to);
+ msg->to = call->dest_proxy ? linphone_address_clone(call->dest_proxy->identity_address) :
+ linphone_address_new(linphone_core_get_identity(lc));
+ msg->time = ms_time(0);
+ msg->state = LinphoneChatMessageStateDelivered;
+ msg->is_read = FALSE;
+ msg->dir = LinphoneChatMessageIncoming;
+
+ if (lp_config_get_int(lc->config, "misc", "store_rtt_messages", 1) == 1) {
+ msg->storage_id = linphone_chat_message_store(msg);
+ }
+
+ if (cr->unread_count < 0) cr->unread_count = 1;
+ else cr->unread_count++;
+
+ linphone_chat_room_message_received(cr, lc, msg);
+ linphone_chat_message_unref(msg);
+ cr->pending_message = NULL;
+ cr->received_rtt_characters = bctbx_list_free(cr->received_rtt_characters);
+ } else {
+ char *value = utf8_to_char(character);
+ cr->pending_message->message = ms_strcat_printf(cr->pending_message->message, value);
+ ms_debug("Received RTT character: %s (%lu), pending text is %s", value, (unsigned long)character, cr->pending_message->message);
+ ms_free(value);
+ }
+ }
+}
+
+uint32_t linphone_chat_room_get_char(const LinphoneChatRoom *cr) {
+ if (cr && cr->received_rtt_characters) {
+ bctbx_list_t *characters = cr->received_rtt_characters;
+ while (characters != NULL) {
+ LinphoneChatMessageCharacter *cmc = (LinphoneChatMessageCharacter *)characters->data;
+ if (!cmc->has_been_read) {
+ cmc->has_been_read = TRUE;
+ return cmc->value;
+ }
+ characters = bctbx_list_next(characters);
+ }
+ }
+ return 0;
+}
+
+int linphone_chat_message_put_char(LinphoneChatMessage *msg, uint32_t character) {
+ LinphoneChatRoom *cr = linphone_chat_message_get_chat_room(msg);
+ LinphoneCall *call = cr->call;
+ LinphoneCore *lc = cr->lc;
+ uint32_t new_line = 0x2028;
+ uint32_t crlf = 0x0D0A;
+ uint32_t lf = 0x0A;
+
+ if (!call || !call->textstream) {
+ return -1;
+ }
+
+ if (character == new_line || character == crlf || character == lf) {
+ if (lc && lp_config_get_int(lc->config, "misc", "store_rtt_messages", 1) == 1) {
+ ms_debug("New line sent, forge a message with content %s", msg->message);
+ msg->time = ms_time(0);
+ msg->state = LinphoneChatMessageStateDelivered;
+ msg->is_read = TRUE;
+ msg->dir = LinphoneChatMessageOutgoing;
+ if (msg->from) linphone_address_destroy(msg->from);
+ msg->from = linphone_address_new(linphone_core_get_identity(lc));
+ msg->storage_id = linphone_chat_message_store(msg);
+ ms_free(msg->message);
+ msg->message = NULL;
+ }
+ } else {
+ char *value = utf8_to_char(character);
+ msg->message = ms_strcat_printf(msg->message, value);
+ ms_debug("Sent RTT character: %s (%lu), pending text is %s", value, (unsigned long)character, msg->message);
+ ms_free(value);
+ }
+
+ text_stream_putchar32(call->textstream, character);
+ return 0;
+}
+
static int linphone_chat_room_stop_composing(void *data, unsigned int revents) {
LinphoneChatRoom *cr = (LinphoneChatRoom *)data;
cr->is_composing = LinphoneIsComposingIdle;
@@ -1026,329 +1023,149 @@ static int linphone_chat_room_refresh_composing(void *data, unsigned int revents
}
void linphone_chat_room_compose(LinphoneChatRoom *cr) {
- int idle_timeout = lp_config_get_int(cr->lc->config, "sip", "composing_idle_timeout", COMPOSING_DEFAULT_IDLE_TIMEOUT);
- int refresh_timeout = lp_config_get_int(cr->lc->config, "sip", "composing_refresh_timeout", COMPOSING_DEFAULT_REFRESH_TIMEOUT);
+ int idle_timeout =
+ lp_config_get_int(cr->lc->config, "sip", "composing_idle_timeout", COMPOSING_DEFAULT_IDLE_TIMEOUT);
+ int refresh_timeout =
+ lp_config_get_int(cr->lc->config, "sip", "composing_refresh_timeout", COMPOSING_DEFAULT_REFRESH_TIMEOUT);
if (cr->is_composing == LinphoneIsComposingIdle) {
cr->is_composing = LinphoneIsComposingActive;
linphone_chat_room_send_is_composing_notification(cr);
if (!cr->composing_refresh_timer) {
- cr->composing_refresh_timer = sal_create_timer(cr->lc->sal, linphone_chat_room_refresh_composing, cr, refresh_timeout * 1000, "composing refresh timeout");
+ cr->composing_refresh_timer = sal_create_timer(cr->lc->sal, linphone_chat_room_refresh_composing, cr,
+ refresh_timeout * 1000, "composing refresh timeout");
} else {
belle_sip_source_set_timeout(cr->composing_refresh_timer, refresh_timeout * 1000);
}
if (!cr->composing_idle_timer) {
- cr->composing_idle_timer = sal_create_timer(cr->lc->sal, linphone_chat_room_stop_composing, cr, idle_timeout * 1000, "composing idle timeout");
+ cr->composing_idle_timer = sal_create_timer(cr->lc->sal, linphone_chat_room_stop_composing, cr,
+ idle_timeout * 1000, "composing idle timeout");
}
}
belle_sip_source_set_timeout(cr->composing_idle_timer, idle_timeout * 1000);
}
-const char* linphone_chat_message_state_to_string(const LinphoneChatMessageState state) {
+const char *linphone_chat_message_state_to_string(const LinphoneChatMessageState state) {
switch (state) {
- case LinphoneChatMessageStateIdle:return "LinphoneChatMessageStateIdle";
- case LinphoneChatMessageStateInProgress:return "LinphoneChatMessageStateInProgress";
- case LinphoneChatMessageStateDelivered:return "LinphoneChatMessageStateDelivered";
- case LinphoneChatMessageStateNotDelivered:return "LinphoneChatMessageStateNotDelivered";
- case LinphoneChatMessageStateFileTransferError:return "LinphoneChatMessageStateFileTransferError";
- case LinphoneChatMessageStateFileTransferDone: return "LinphoneChatMessageStateFileTransferDone ";
+ case LinphoneChatMessageStateIdle:
+ return "LinphoneChatMessageStateIdle";
+ case LinphoneChatMessageStateInProgress:
+ return "LinphoneChatMessageStateInProgress";
+ case LinphoneChatMessageStateDelivered:
+ return "LinphoneChatMessageStateDelivered";
+ case LinphoneChatMessageStateNotDelivered:
+ return "LinphoneChatMessageStateNotDelivered";
+ case LinphoneChatMessageStateFileTransferError:
+ return "LinphoneChatMessageStateFileTransferError";
+ case LinphoneChatMessageStateFileTransferDone:
+ return "LinphoneChatMessageStateFileTransferDone ";
}
return NULL;
}
-LinphoneChatRoom* linphone_chat_message_get_chat_room(LinphoneChatMessage *msg){
+LinphoneChatRoom *linphone_chat_message_get_chat_room(LinphoneChatMessage *msg) {
return msg->chat_room;
}
-const LinphoneAddress* linphone_chat_message_get_peer_address(LinphoneChatMessage *msg) {
+const LinphoneAddress *linphone_chat_message_get_peer_address(LinphoneChatMessage *msg) {
return linphone_chat_room_get_peer_address(msg->chat_room);
}
-void linphone_chat_message_set_user_data(LinphoneChatMessage* message,void* ud) {
- message->message_userdata=ud;
+void linphone_chat_message_set_user_data(LinphoneChatMessage *msg, void *ud) {
+ msg->message_userdata = ud;
}
-void* linphone_chat_message_get_user_data(const LinphoneChatMessage* message) {
- return message->message_userdata;
+void *linphone_chat_message_get_user_data(const LinphoneChatMessage *msg) {
+ return msg->message_userdata;
}
-const char* linphone_chat_message_get_external_body_url(const LinphoneChatMessage* message) {
- return message->external_body_url;
+const char *linphone_chat_message_get_external_body_url(const LinphoneChatMessage *msg) {
+ return msg->external_body_url;
}
-void linphone_chat_message_set_external_body_url(LinphoneChatMessage* message, const char* url) {
- if (message->external_body_url) {
- ms_free(message->external_body_url);
+void linphone_chat_message_set_external_body_url(LinphoneChatMessage *msg, const char *url) {
+ if (msg->external_body_url) {
+ ms_free(msg->external_body_url);
}
- message->external_body_url=url?ms_strdup(url):NULL;
+ msg->external_body_url = url ? ms_strdup(url) : NULL;
}
-const char* linphone_chat_message_get_appdata(const LinphoneChatMessage* message){
- return message->appdata;
+const char *linphone_chat_message_get_appdata(const LinphoneChatMessage *msg) {
+ return msg->appdata;
}
-void linphone_chat_message_set_appdata(LinphoneChatMessage* message, const char* data){
- if ( message->appdata ){
- ms_free(message->appdata);
+void linphone_chat_message_set_appdata(LinphoneChatMessage *msg, const char *data) {
+ if (msg->appdata) {
+ ms_free(msg->appdata);
}
- message->appdata = data? ms_strdup(data) : NULL;
- linphone_chat_message_store_appdata(message);
+ msg->appdata = data ? ms_strdup(data) : NULL;
+ linphone_chat_message_store_appdata(msg);
}
-
-const LinphoneContent *linphone_chat_message_get_file_transfer_information(const LinphoneChatMessage*message) {
- return message->file_transfer_information;
+void linphone_chat_message_set_from_address(LinphoneChatMessage *msg, const LinphoneAddress *from) {
+ if (msg->from)
+ linphone_address_destroy(msg->from);
+ msg->from = linphone_address_clone(from);
}
-static void on_recv_body(belle_sip_user_body_handler_t *bh, belle_sip_message_t *msg, void *data, size_t offset, const uint8_t *buffer, size_t size){
- LinphoneChatMessage* chatMsg=(LinphoneChatMessage *)data;
- LinphoneCore *lc = chatMsg->chat_room->lc;
-
- if (!chatMsg->http_request || belle_http_request_is_cancelled(chatMsg->http_request)) {
- ms_warning("Cancelled request for msg [%p], ignoring %s", chatMsg, __FUNCTION__);
- return;
- }
-
- /* first call may be with a zero size, ignore it */
- if (size == 0) {
- return;
- }
-
- if (linphone_content_get_key(chatMsg->file_transfer_information) != NULL) { /* we have a key, we must decrypt the file */
- /* get data from callback to a plainBuffer */
- char *plainBuffer = (char *)malloc(size);
- lime_decryptFile(linphone_content_get_cryptoContext_address(chatMsg->file_transfer_information), (unsigned char *)linphone_content_get_key(chatMsg->file_transfer_information), size, plainBuffer, (char *)buffer);
- if (linphone_chat_message_cbs_get_file_transfer_recv(chatMsg->callbacks)) {
- LinphoneBuffer *lb = linphone_buffer_new_from_data((unsigned char *)plainBuffer, size);
- linphone_chat_message_cbs_get_file_transfer_recv(chatMsg->callbacks)(chatMsg, chatMsg->file_transfer_information, lb);
- linphone_buffer_unref(lb);
- } else {
- /* legacy: call back given by application level */
- linphone_core_notify_file_transfer_recv(lc, chatMsg, chatMsg->file_transfer_information, plainBuffer, size);
- }
- free(plainBuffer);
- } else { /* regular file, no deciphering */
- if (linphone_chat_message_cbs_get_file_transfer_recv(chatMsg->callbacks)) {
- LinphoneBuffer *lb = linphone_buffer_new_from_data(buffer, size);
- linphone_chat_message_cbs_get_file_transfer_recv(chatMsg->callbacks)(chatMsg, chatMsg->file_transfer_information, lb);
- linphone_buffer_unref(lb);
- } else {
- /* Legacy: call back given by application level */
- linphone_core_notify_file_transfer_recv(lc, chatMsg, chatMsg->file_transfer_information, (char *)buffer, size);
- }
- }
-
- return;
+const LinphoneAddress *linphone_chat_message_get_from_address(const LinphoneChatMessage *msg) {
+ return msg->from;
}
-
-static LinphoneContent* linphone_chat_create_file_transfer_information_from_headers(const belle_sip_message_t* message ){
- LinphoneContent *content = linphone_content_new();
-
- belle_sip_header_content_length_t* content_length_hdr = BELLE_SIP_HEADER_CONTENT_LENGTH(belle_sip_message_get_header(message, "Content-Length"));
- belle_sip_header_content_type_t* content_type_hdr = BELLE_SIP_HEADER_CONTENT_TYPE(belle_sip_message_get_header(message, "Content-Type"));
- const char* type = NULL,*subtype = NULL;
-
- linphone_content_set_name(content, "");
-
- if( content_type_hdr ){
- type = belle_sip_header_content_type_get_type(content_type_hdr);
- subtype = belle_sip_header_content_type_get_subtype(content_type_hdr);
- ms_message("Extracted content type %s / %s from header", type?type:"", subtype?subtype:"");
- if( type ) linphone_content_set_type(content, type);
- if( subtype ) linphone_content_set_subtype(content, subtype);
- }
-
- if( content_length_hdr ){
- linphone_content_set_size(content, belle_sip_header_content_length_get_content_length(content_length_hdr));
- ms_message("Extracted content length %i from header", (int)linphone_content_get_size(content));
- }
-
- return content;
+void linphone_chat_message_set_to_address(LinphoneChatMessage *msg, const LinphoneAddress *to) {
+ if (msg->to)
+ linphone_address_destroy(msg->to);
+ msg->to = linphone_address_clone(to);
}
-static void linphone_chat_process_response_headers_from_get_file(void *data, const belle_http_response_event_t *event){
- if (event->response){
- /*we are receiving a response, set a specific body handler to acquire the response.
- * if not done, belle-sip will create a memory body handler, the default*/
- LinphoneChatMessage *message=(LinphoneChatMessage *)belle_sip_object_data_get(BELLE_SIP_OBJECT(event->request),"message");
- belle_sip_message_t* response = BELLE_SIP_MESSAGE(event->response);
- size_t body_size = 0;
-
- if( message->file_transfer_information == NULL ){
- ms_warning("No file transfer information for message %p: creating...", message);
- message->file_transfer_information = linphone_chat_create_file_transfer_information_from_headers(response);
- }
-
- if( message->file_transfer_information ){
- body_size = linphone_content_get_size(message->file_transfer_information);
- }
-
- if (message->file_transfer_filepath == NULL) {
- belle_sip_message_set_body_handler(
- (belle_sip_message_t*)event->response,
- (belle_sip_body_handler_t*)belle_sip_user_body_handler_new(body_size, linphone_chat_message_file_transfer_on_progress,on_recv_body,NULL,message)
- );
- } else {
- belle_sip_body_handler_t *bh = (belle_sip_body_handler_t *)belle_sip_file_body_handler_new(message->file_transfer_filepath, linphone_chat_message_file_transfer_on_progress, message);
- if (belle_sip_body_handler_get_size(bh) == 0) {
- /* If the size of the body has not been initialized from the file stat, use the one from the file_transfer_information. */
- belle_sip_body_handler_set_size(bh, body_size);
- }
- belle_sip_message_set_body_handler((belle_sip_message_t *)event->response, bh);
- }
- }
-}
-
-static void linphone_chat_process_response_from_get_file(void *data, const belle_http_response_event_t *event){
- /* check the answer code */
- if (event->response){
- int code=belle_http_response_get_status_code(event->response);
- if (code==200) {
- LinphoneChatMessage* chatMsg=(LinphoneChatMessage *)data;
- LinphoneCore *lc = chatMsg->chat_room->lc;
- /* if the file was encrypted, finish the decryption and free context */
- if (linphone_content_get_key(chatMsg->file_transfer_information) != NULL) {
- lime_decryptFile(linphone_content_get_cryptoContext_address(chatMsg->file_transfer_information), NULL, 0, NULL, NULL);
- }
- /* file downloaded succesfully, call again the callback with size at zero */
- if (linphone_chat_message_cbs_get_file_transfer_recv(chatMsg->callbacks)) {
- LinphoneBuffer *lb = linphone_buffer_new();
- linphone_chat_message_cbs_get_file_transfer_recv(chatMsg->callbacks)(chatMsg, chatMsg->file_transfer_information, lb);
- linphone_buffer_unref(lb);
- } else {
- linphone_core_notify_file_transfer_recv(lc, chatMsg, chatMsg->file_transfer_information, NULL, 0);
- }
- chatMsg->state = LinphoneChatMessageStateFileTransferDone;
- if (chatMsg->cb) {
- chatMsg->cb(chatMsg, chatMsg->state, chatMsg->cb_ud);
- }
- if (linphone_chat_message_cbs_get_msg_state_changed(chatMsg->callbacks)) {
- linphone_chat_message_cbs_get_msg_state_changed(chatMsg->callbacks)(chatMsg, chatMsg->state);
- }
- }
- }
-}
-
-void linphone_chat_message_download_file(LinphoneChatMessage *message) {
- belle_http_request_listener_callbacks_t cbs={0};
- belle_http_request_listener_t *l;
- belle_generic_uri_t *uri;
- const char *url=message->external_body_url;
- char* ua;
-
- if (url == NULL) {
- ms_error("Cannot download file from chat message [%p] because url is NULL",message);
- return;
- }
- ua = ms_strdup_printf("%s/%s", linphone_core_get_user_agent_name(), linphone_core_get_user_agent_version());
- uri=belle_generic_uri_parse(url);
-
- message->http_request=belle_http_request_create("GET",
- uri,
- belle_sip_header_create("User-Agent",ua),
- NULL);
- belle_sip_object_ref(message->http_request); /* keep a reference on the request to be able to cancel the download */
- ms_free(ua);
-
- cbs.process_response_headers=linphone_chat_process_response_headers_from_get_file;
- cbs.process_response=linphone_chat_process_response_from_get_file;
- cbs.process_io_error=process_io_error_download;
- cbs.process_auth_requested=process_auth_requested_download;
- l=belle_http_request_listener_create_from_callbacks(&cbs, (void *)message);
- belle_sip_object_data_set(BELLE_SIP_OBJECT(message->http_request),"message",(void *)message,NULL);
- message->state = LinphoneChatMessageStateInProgress; /* start the download, status is In Progress */
- belle_http_provider_send_request(message->chat_room->lc->http_provider,message->http_request,l);
-}
-
-void linphone_chat_message_start_file_download(LinphoneChatMessage *message, LinphoneChatMessageStateChangedCb status_cb, void *ud) {
- message->cb = status_cb;
- message->cb_ud = ud;
- linphone_chat_message_download_file(message);
-}
-
-void linphone_chat_message_cancel_file_transfer(LinphoneChatMessage *msg) {
- if (msg->http_request) {
- if (!belle_http_request_is_cancelled(msg->http_request)) {
- ms_message("Cancelling file transfer %s - msg [%p] chat room[%p]", (msg->external_body_url==NULL)?linphone_core_get_file_transfer_server(msg->chat_room->lc):msg->external_body_url, msg, msg->chat_room);
-
- belle_http_provider_cancel_request(msg->chat_room->lc->http_provider, msg->http_request);
- belle_sip_object_unref(msg->http_request);
- msg->http_request = NULL;
- msg->state = LinphoneChatMessageStateNotDelivered;
- if (msg->cb) {
- msg->cb(msg, msg->state, msg->cb_ud);
- }
- if (linphone_chat_message_cbs_get_msg_state_changed(msg->callbacks)) {
- linphone_chat_message_cbs_get_msg_state_changed(msg->callbacks)(msg, msg->state);
- }
- }
- } else {
- ms_message("No existing file transfer - nothing to cancel");
- }
-}
-
-
-void linphone_chat_message_set_from_address(LinphoneChatMessage* message, const LinphoneAddress* from) {
- if(message->from) linphone_address_destroy(message->from);
- message->from=linphone_address_clone(from);
-}
-
-const LinphoneAddress* linphone_chat_message_get_from_address(const LinphoneChatMessage* message) {
- return message->from;
-}
-
-void linphone_chat_message_set_to_address(LinphoneChatMessage* message, const LinphoneAddress* to) {
- if(message->to) linphone_address_destroy(message->to);
- message->to=linphone_address_clone(to);
-}
-
-const LinphoneAddress* linphone_chat_message_get_to_address(const LinphoneChatMessage* message){
- if (message->to) return message->to;
- if (message->dir==LinphoneChatMessageOutgoing){
- return message->chat_room->peer_url;
+const LinphoneAddress *linphone_chat_message_get_to_address(const LinphoneChatMessage *msg) {
+ if (msg->to)
+ return msg->to;
+ if (msg->dir == LinphoneChatMessageOutgoing) {
+ return msg->chat_room->peer_url;
}
return NULL;
}
-LinphoneAddress *linphone_chat_message_get_local_address(const LinphoneChatMessage* message){
- return message->dir==LinphoneChatMessageOutgoing ? message->from : message->to;
+LinphoneAddress *linphone_chat_message_get_local_address(const LinphoneChatMessage *msg) {
+ return msg->dir == LinphoneChatMessageOutgoing ? msg->from : msg->to;
}
-time_t linphone_chat_message_get_time(const LinphoneChatMessage* message) {
- return message->time;
+time_t linphone_chat_message_get_time(const LinphoneChatMessage *msg) {
+ return msg->time;
}
-LinphoneChatMessageState linphone_chat_message_get_state(const LinphoneChatMessage* message) {
- return message->state;
+LinphoneChatMessageState linphone_chat_message_get_state(const LinphoneChatMessage *msg) {
+ return msg->state;
}
-const char * linphone_chat_message_get_text(const LinphoneChatMessage* message) {
- return message->message;
+const char *linphone_chat_message_get_text(const LinphoneChatMessage *msg) {
+ return msg->message;
}
-void linphone_chat_message_add_custom_header(LinphoneChatMessage* message, const char *header_name, const char *header_value){
- message->custom_headers=sal_custom_header_append(message->custom_headers,header_name,header_value);
+void linphone_chat_message_add_custom_header(LinphoneChatMessage *msg, const char *header_name,
+ const char *header_value) {
+ msg->custom_headers = sal_custom_header_append(msg->custom_headers, header_name, header_value);
}
-const char * linphone_chat_message_get_custom_header(LinphoneChatMessage* message, const char *header_name){
- return sal_custom_header_find(message->custom_headers,header_name);
+const char *linphone_chat_message_get_custom_header(LinphoneChatMessage *msg, const char *header_name) {
+ return sal_custom_header_find(msg->custom_headers, header_name);
}
-bool_t linphone_chat_message_is_read(LinphoneChatMessage* message) {
- return message->is_read;
+bool_t linphone_chat_message_is_read(LinphoneChatMessage *msg) {
+ return msg->is_read;
}
-bool_t linphone_chat_message_is_outgoing(LinphoneChatMessage* message) {
- return message->dir == LinphoneChatMessageOutgoing;
+bool_t linphone_chat_message_is_outgoing(LinphoneChatMessage *msg) {
+ return msg->dir == LinphoneChatMessageOutgoing;
}
-unsigned int linphone_chat_message_get_storage_id(LinphoneChatMessage* message) {
- return message->storage_id;
+unsigned int linphone_chat_message_get_storage_id(LinphoneChatMessage *msg) {
+ return msg->storage_id;
}
-LinphoneChatMessage* linphone_chat_message_clone(const LinphoneChatMessage* msg) {
+LinphoneChatMessage *linphone_chat_message_clone(const LinphoneChatMessage *msg) {
/*struct _LinphoneChatMessage {
- char* message;
+ char* msg;
LinphoneChatRoom* chat_room;
LinphoneChatMessageStateChangeCb cb;
void* cb_ud;
@@ -1359,94 +1176,88 @@ LinphoneChatMessage* linphone_chat_message_clone(const LinphoneChatMessage* msg)
SalCustomHeader *custom_headers;
LinphoneChatMessageState state;
};*/
- LinphoneChatMessage* new_message = linphone_chat_room_create_message(msg->chat_room,msg->message);
- if (msg->external_body_url) new_message->external_body_url=ms_strdup(msg->external_body_url);
- if (msg->appdata) new_message->appdata = ms_strdup(msg->appdata);
- new_message->cb=msg->cb;
- new_message->cb_ud=msg->cb_ud;
- new_message->message_userdata=msg->message_userdata;
- new_message->cb=msg->cb;
- new_message->time=msg->time;
- new_message->state=msg->state;
- new_message->storage_id=msg->storage_id;
- if (msg->from) new_message->from=linphone_address_clone(msg->from);
- if (msg->file_transfer_filepath) new_message->file_transfer_filepath=ms_strdup(msg->file_transfer_filepath);
- if (msg->file_transfer_information) new_message->file_transfer_information=linphone_content_copy(msg->file_transfer_information);
+ LinphoneChatMessage *new_message = linphone_chat_room_create_message(msg->chat_room, msg->message);
+ if (msg->external_body_url)
+ new_message->external_body_url = ms_strdup(msg->external_body_url);
+ if (msg->appdata)
+ new_message->appdata = ms_strdup(msg->appdata);
+ new_message->message_state_changed_cb = msg->message_state_changed_cb;
+ new_message->message_state_changed_user_data = msg->message_state_changed_user_data;
+ new_message->message_userdata = msg->message_userdata;
+ new_message->time = msg->time;
+ new_message->state = msg->state;
+ new_message->storage_id = msg->storage_id;
+ if (msg->from)
+ new_message->from = linphone_address_clone(msg->from);
+ if (msg->file_transfer_filepath)
+ new_message->file_transfer_filepath = ms_strdup(msg->file_transfer_filepath);
+ if (msg->file_transfer_information)
+ new_message->file_transfer_information = linphone_content_copy(msg->file_transfer_information);
return new_message;
}
-void linphone_chat_message_destroy(LinphoneChatMessage* msg){
+void linphone_chat_message_destroy(LinphoneChatMessage *msg) {
belle_sip_object_unref(msg);
}
-static void _linphone_chat_message_destroy(LinphoneChatMessage* msg) {
- if (msg->op) sal_op_release(msg->op);
- if (msg->message) ms_free(msg->message);
- if (msg->external_body_url) ms_free(msg->external_body_url);
- if (msg->appdata) ms_free(msg->appdata);
- if (msg->from) linphone_address_destroy(msg->from);
- if (msg->to) linphone_address_destroy(msg->to);
- if (msg->custom_headers) sal_custom_header_free(msg->custom_headers);
- if (msg->content_type) ms_free(msg->content_type);
+static void _linphone_chat_message_destroy(LinphoneChatMessage *msg) {
+ if (msg->op)
+ sal_op_release(msg->op);
+ if (msg->message)
+ ms_free(msg->message);
+ if (msg->external_body_url)
+ ms_free(msg->external_body_url);
+ if (msg->appdata)
+ ms_free(msg->appdata);
+ if (msg->from)
+ linphone_address_destroy(msg->from);
+ if (msg->to)
+ linphone_address_destroy(msg->to);
+ if (msg->custom_headers)
+ sal_custom_header_free(msg->custom_headers);
+ if (msg->content_type)
+ ms_free(msg->content_type);
if (msg->file_transfer_information) {
linphone_content_unref(msg->file_transfer_information);
}
if (msg->file_transfer_filepath != NULL) {
ms_free(msg->file_transfer_filepath);
}
- linphone_chat_message_cbs_unref(msg->callbacks);
+ if (msg->callbacks) {
+ linphone_chat_message_cbs_unref(msg->callbacks);
+ }
}
-LinphoneChatMessage * linphone_chat_message_ref(LinphoneChatMessage *msg){
+LinphoneChatMessage *linphone_chat_message_ref(LinphoneChatMessage *msg) {
belle_sip_object_ref(msg);
return msg;
}
-void linphone_chat_message_unref(LinphoneChatMessage *msg){
+void linphone_chat_message_unref(LinphoneChatMessage *msg) {
belle_sip_object_unref(msg);
}
-static void linphone_chat_message_release(LinphoneChatMessage *msg){
- /*mark the chat message as orphan (it has no chat room anymore), and unref it*/
+static void linphone_chat_message_release(LinphoneChatMessage *msg) {
+ /*mark the chat msg as orphan (it has no chat room anymore), and unref it*/
msg->chat_room = NULL;
+ if (msg->file_transfer_information != NULL) {
+ linphone_chat_message_cancel_file_transfer(msg);
+ }
linphone_chat_message_unref(msg);
}
-const LinphoneErrorInfo *linphone_chat_message_get_error_info(const LinphoneChatMessage *msg){
+const LinphoneErrorInfo *linphone_chat_message_get_error_info(const LinphoneChatMessage *msg) {
return linphone_error_info_from_sal_op(msg->op);
}
-LinphoneReason linphone_chat_message_get_reason(LinphoneChatMessage* msg) {
+LinphoneReason linphone_chat_message_get_reason(LinphoneChatMessage *msg) {
return linphone_error_info_get_reason(linphone_chat_message_get_error_info(msg));
}
-
-void linphone_chat_message_set_file_transfer_filepath(LinphoneChatMessage *msg, const char *filepath) {
- if (msg->file_transfer_filepath != NULL) {
- ms_free(msg->file_transfer_filepath);
- }
- msg->file_transfer_filepath = ms_strdup(filepath);
-}
-
-const char * linphone_chat_message_get_file_transfer_filepath(LinphoneChatMessage *msg) {
- return msg->file_transfer_filepath;
-}
-
-LinphoneChatMessageCbs * linphone_chat_message_get_callbacks(const LinphoneChatMessage *msg) {
+LinphoneChatMessageCbs *linphone_chat_message_get_callbacks(const LinphoneChatMessage *msg) {
return msg->callbacks;
}
-LinphoneChatMessage* linphone_chat_room_create_file_transfer_message(LinphoneChatRoom *cr, const LinphoneContent* initial_content) {
- LinphoneChatMessage* msg = belle_sip_object_new(LinphoneChatMessage);
- msg->callbacks=linphone_chat_message_cbs_new();
- msg->chat_room=(LinphoneChatRoom*)cr;
- msg->message = NULL;
- msg->is_read=TRUE;
- msg->file_transfer_information = linphone_content_copy(initial_content);
- msg->dir=LinphoneChatMessageOutgoing;
- linphone_chat_message_set_to(msg, linphone_chat_room_get_peer_address(cr));
- linphone_chat_message_set_from(msg, linphone_address_new(linphone_core_get_identity(cr->lc)));
- msg->content_type=NULL; /* this will be set to application/vnd.gsma.rcs-ft-http+xml when we will transfer the xml reply from server to the peers */
- msg->http_request=NULL; /* this will store the http request during file upload to the server */
- return msg;
+LinphoneCall *linphone_chat_room_get_call(const LinphoneChatRoom *room) {
+ return room->call;
}
diff --git a/coreapi/chat_file_transfer.c b/coreapi/chat_file_transfer.c
new file mode 100644
index 000000000..6669257d4
--- /dev/null
+++ b/coreapi/chat_file_transfer.c
@@ -0,0 +1,627 @@
+/***************************************************************************
+ * chat_file_transfer.c
+ *
+ * Sun Jun 5 19:34:18 2005
+ * Copyright 2005 Simon Morlat
+ * Email simon dot morlat at linphone dot org
+ ****************************************************************************/
+
+/*
+ * 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 "linphonecore.h"
+#include "private.h"
+#include "lime.h"
+#include "ortp/b64.h"
+
+#define FILE_TRANSFER_KEY_SIZE 32
+
+static bool_t file_transfer_in_progress_and_valid(LinphoneChatMessage* msg) {
+ return (msg->chat_room && msg->chat_room->lc && msg->http_request && !belle_http_request_is_cancelled(msg->http_request));
+}
+
+static void _release_http_request(LinphoneChatMessage* msg) {
+ if (msg->http_request) {
+ belle_sip_object_unref(msg->http_request);
+ msg->http_request = NULL;
+ if (msg->http_listener){
+ belle_sip_object_unref(msg->http_listener);
+ msg->http_listener = NULL;
+ // unhold the reference that the listener was holding on the message
+ linphone_chat_message_unref(msg);
+ }
+ }
+}
+
+static void linphone_chat_message_process_io_error_upload(void *data, const belle_sip_io_error_event_t *event) {
+ LinphoneChatMessage *msg = (LinphoneChatMessage *)data;
+ ms_error("I/O Error during file upload of msg [%p]", msg);
+ linphone_chat_message_update_state(msg, LinphoneChatMessageStateNotDelivered);
+ _release_http_request(msg);
+ linphone_chat_message_unref(msg);
+}
+
+static void linphone_chat_message_process_auth_requested_upload(void *data, belle_sip_auth_event_t *event) {
+ LinphoneChatMessage *msg = (LinphoneChatMessage *)data;
+ ms_error("Error during file upload: auth requested for msg [%p]", msg);
+ linphone_chat_message_update_state(msg, LinphoneChatMessageStateNotDelivered);
+ _release_http_request(msg);
+ linphone_chat_message_unref(msg);
+}
+
+static void linphone_chat_message_process_io_error_download(void *data, const belle_sip_io_error_event_t *event) {
+ LinphoneChatMessage *msg = (LinphoneChatMessage *)data;
+ ms_error("I/O Error during file download msg [%p]", msg);
+ linphone_chat_message_update_state(msg, LinphoneChatMessageStateFileTransferError);
+ _release_http_request(msg);
+}
+
+static void linphone_chat_message_process_auth_requested_download(void *data, belle_sip_auth_event_t *event) {
+ LinphoneChatMessage *msg = (LinphoneChatMessage *)data;
+ ms_error("Error during file download : auth requested for msg [%p]", msg);
+ linphone_chat_message_update_state(msg, LinphoneChatMessageStateFileTransferError);
+ _release_http_request(msg);
+}
+
+static void linphone_chat_message_file_transfer_on_progress(belle_sip_body_handler_t *bh, belle_sip_message_t *m,
+ void *data, size_t offset, size_t total) {
+ LinphoneChatMessage *msg = (LinphoneChatMessage *)data;
+ if (msg->http_request && !file_transfer_in_progress_and_valid(msg)) {
+ ms_warning("Cancelled request for %s msg [%p], ignoring %s", msg->chat_room?"":"ORPHAN", msg, __FUNCTION__);
+ _release_http_request(msg);
+ return;
+ }
+ if (linphone_chat_message_cbs_get_file_transfer_progress_indication(msg->callbacks)) {
+ linphone_chat_message_cbs_get_file_transfer_progress_indication(msg->callbacks)(
+ msg, msg->file_transfer_information, offset, total);
+ } else {
+ /* Legacy: call back given by application level */
+ linphone_core_notify_file_transfer_progress_indication(msg->chat_room->lc, msg, msg->file_transfer_information,
+ offset, total);
+ }
+}
+
+static int linphone_chat_message_file_transfer_on_send_body(belle_sip_user_body_handler_t *bh, belle_sip_message_t *m,
+ void *data, size_t offset, uint8_t *buffer, size_t *size) {
+ LinphoneChatMessage *msg = (LinphoneChatMessage *)data;
+ LinphoneCore *lc = NULL;
+ char *buf = (char *)buffer;
+
+ if (!file_transfer_in_progress_and_valid(msg)) {
+ if (msg->http_request) {
+ ms_warning("Cancelled request for %s msg [%p], ignoring %s", msg->chat_room?"":"ORPHAN", msg, __FUNCTION__);
+ _release_http_request(msg);
+ }
+ return BELLE_SIP_STOP;
+ }
+
+ lc = msg->chat_room->lc;
+ /* if we've not reach the end of file yet, ask for more data*/
+ if (offset < linphone_content_get_size(msg->file_transfer_information)) {
+ char *plainBuffer = NULL;
+
+ if (linphone_content_get_key(msg->file_transfer_information) !=
+ NULL) { /* if we have a key to cipher the msg, use it! */
+ /* if this chunk is not the last one, the lenght must be a multiple of block cipher size(16 bytes)*/
+ if (offset + *size < linphone_content_get_size(msg->file_transfer_information)) {
+ *size -= (*size % 16);
+ }
+ plainBuffer = (char *)ms_malloc0(*size);
+ }
+
+ /* get data from call back */
+ if (linphone_chat_message_cbs_get_file_transfer_send(msg->callbacks)) {
+ LinphoneBuffer *lb = linphone_chat_message_cbs_get_file_transfer_send(msg->callbacks)(
+ msg, msg->file_transfer_information, offset, *size);
+ if (lb == NULL) {
+ *size = 0;
+ } else {
+ *size = linphone_buffer_get_size(lb);
+ memcpy(plainBuffer ? plainBuffer : buf, linphone_buffer_get_content(lb), *size);
+ linphone_buffer_unref(lb);
+ }
+ } else {
+ /* Legacy */
+ linphone_core_notify_file_transfer_send(lc, msg, msg->file_transfer_information,
+ plainBuffer ? plainBuffer : buf, size);
+ }
+
+ if (linphone_content_get_key(msg->file_transfer_information) !=
+ NULL) { /* if we have a key to cipher the msg, use it! */
+ lime_encryptFile(linphone_content_get_cryptoContext_address(msg->file_transfer_information),
+ (unsigned char *)linphone_content_get_key(msg->file_transfer_information), *size,
+ plainBuffer, (char *)buffer);
+ ms_free(plainBuffer);
+ /* check if we reach the end of file */
+ if (offset + *size >= linphone_content_get_size(msg->file_transfer_information)) {
+ /* conclude file ciphering by calling it context with a zero size */
+ lime_encryptFile(linphone_content_get_cryptoContext_address(msg->file_transfer_information), NULL, 0,
+ NULL, NULL);
+ }
+ }
+ }
+
+ return BELLE_SIP_CONTINUE;
+}
+
+static void linphone_chat_message_process_response_from_post_file(void *data,
+ const belle_http_response_event_t *event) {
+ LinphoneChatMessage *msg = (LinphoneChatMessage *)data;
+
+ if (msg->http_request && !file_transfer_in_progress_and_valid(msg)) {
+ ms_warning("Cancelled request for %s msg [%p], ignoring %s", msg->chat_room?"":"ORPHAN", msg, __FUNCTION__);
+ _release_http_request(msg);
+ return;
+ }
+
+ /* check the answer code */
+ if (event->response) {
+ int code = belle_http_response_get_status_code(event->response);
+ if (code == 204) { /* this is the reply to the first post to the server - an empty msg */
+ /* start uploading the file */
+ belle_sip_multipart_body_handler_t *bh;
+ char *first_part_header;
+ belle_sip_body_handler_t *first_part_bh;
+
+ /* shall we encrypt the file */
+ if (linphone_chat_room_lime_available(msg->chat_room) &&
+ linphone_core_lime_for_file_sharing_enabled(msg->chat_room->lc)) {
+ char keyBuffer
+ [FILE_TRANSFER_KEY_SIZE]; /* temporary storage of generated key: 192 bits of key + 64 bits of
+ initial vector */
+ /* generate a random 192 bits key + 64 bits of initial vector and store it into the
+ * file_transfer_information->key field of the msg */
+ sal_get_random_bytes((unsigned char *)keyBuffer, FILE_TRANSFER_KEY_SIZE);
+ linphone_content_set_key(
+ msg->file_transfer_information, keyBuffer,
+ FILE_TRANSFER_KEY_SIZE); /* key is duplicated in the content private structure */
+ /* temporary storage for the Content-disposition header value : use a generic filename to not leak it
+ * Actual filename stored in msg->file_transfer_information->name will be set in encrypted msg
+ * sended to the */
+ first_part_header = belle_sip_strdup_printf("form-data; name=\"File\"; filename=\"filename.txt\"");
+ } else {
+ /* temporary storage for the Content-disposition header value */
+ first_part_header = belle_sip_strdup_printf("form-data; name=\"File\"; filename=\"%s\"",
+ linphone_content_get_name(msg->file_transfer_information));
+ }
+
+ /* create a user body handler to take care of the file and add the content disposition and content-type
+ * headers */
+ if (msg->file_transfer_filepath != NULL) {
+ first_part_bh =
+ (belle_sip_body_handler_t *)belle_sip_file_body_handler_new(msg->file_transfer_filepath, NULL, msg);
+ } else if (linphone_content_get_buffer(msg->file_transfer_information) != NULL) {
+ first_part_bh = (belle_sip_body_handler_t *)belle_sip_memory_body_handler_new_from_buffer(
+ linphone_content_get_buffer(msg->file_transfer_information),
+ linphone_content_get_size(msg->file_transfer_information), NULL, msg);
+ } else {
+ first_part_bh = (belle_sip_body_handler_t *)belle_sip_user_body_handler_new(
+ linphone_content_get_size(msg->file_transfer_information), NULL, NULL,
+ linphone_chat_message_file_transfer_on_send_body, msg);
+ }
+ belle_sip_body_handler_add_header(first_part_bh,
+ belle_sip_header_create("Content-disposition", first_part_header));
+ belle_sip_free(first_part_header);
+ belle_sip_body_handler_add_header(first_part_bh,
+ (belle_sip_header_t *)belle_sip_header_content_type_create(
+ linphone_content_get_type(msg->file_transfer_information),
+ linphone_content_get_subtype(msg->file_transfer_information)));
+
+ /* insert it in a multipart body handler which will manage the boundaries of multipart msg */
+ bh = belle_sip_multipart_body_handler_new(linphone_chat_message_file_transfer_on_progress, msg, first_part_bh, NULL);
+
+ linphone_chat_message_ref(msg);
+ _release_http_request(msg);
+ linphone_chat_room_upload_file(msg);
+ belle_sip_message_set_body_handler(BELLE_SIP_MESSAGE(msg->http_request), BELLE_SIP_BODY_HANDLER(bh));
+ linphone_chat_message_unref(msg);
+ } else if (code == 200) { /* file has been uplaoded correctly, get server reply and send it */
+ const char *body = belle_sip_message_get_body((belle_sip_message_t *)event->response);
+ if (body && strlen(body) > 0) {
+ /* if we have an encryption key for the file, we must insert it into the msg and restore the correct
+ * filename */
+ if (linphone_content_get_key(msg->file_transfer_information) != NULL) {
+ /* parse the msg body */
+ xmlDocPtr xmlMessageBody = xmlParseDoc((const xmlChar *)body);
+
+ xmlNodePtr cur = xmlDocGetRootElement(xmlMessageBody);
+ if (cur != NULL) {
+ cur = cur->xmlChildrenNode;
+ while (cur != NULL) {
+ if (!xmlStrcmp(cur->name, (const xmlChar *)"file-info")) { /* we found a file info node, check
+ it has a type="file" attribute */
+ xmlChar *typeAttribute = xmlGetProp(cur, (const xmlChar *)"type");
+ if (!xmlStrcmp(typeAttribute,
+ (const xmlChar *)"file")) { /* this is the node we are looking for : add a
+ file-key children node */
+ xmlNodePtr fileInfoNodeChildren =
+ cur
+ ->xmlChildrenNode; /* need to parse the children node to update the file-name
+ one */
+ /* convert key to base64 */
+ size_t b64Size = b64_encode(NULL, FILE_TRANSFER_KEY_SIZE, NULL, 0);
+ char *keyb64 = (char *)ms_malloc0(b64Size + 1);
+ int xmlStringLength;
+
+ b64Size = b64_encode(linphone_content_get_key(msg->file_transfer_information),
+ FILE_TRANSFER_KEY_SIZE, keyb64, b64Size);
+ keyb64[b64Size] = '\0'; /* libxml need a null terminated string */
+
+ /* add the node containing the key to the file-info node */
+ xmlNewTextChild(cur, NULL, (const xmlChar *)"file-key", (const xmlChar *)keyb64);
+ xmlFree(typeAttribute);
+ ms_free(keyb64);
+
+ /* look for the file-name node and update its content */
+ while (fileInfoNodeChildren != NULL) {
+ if (!xmlStrcmp(
+ fileInfoNodeChildren->name,
+ (const xmlChar *)"file-name")) { /* we found a the file-name node, update
+ its content with the real filename */
+ /* update node content */
+ xmlNodeSetContent(fileInfoNodeChildren,
+ (const xmlChar *)(linphone_content_get_name(
+ msg->file_transfer_information)));
+ break;
+ }
+ fileInfoNodeChildren = fileInfoNodeChildren->next;
+ }
+
+ /* dump the xml into msg->message */
+ xmlDocDumpFormatMemoryEnc(xmlMessageBody, (xmlChar **)&msg->message, &xmlStringLength,
+ "UTF-8", 0);
+
+ break;
+ }
+ xmlFree(typeAttribute);
+ }
+ cur = cur->next;
+ }
+ }
+ xmlFreeDoc(xmlMessageBody);
+ } else { /* no encryption key, transfer in plain, just copy the msg sent by server */
+ msg->message = ms_strdup(body);
+ }
+ msg->content_type = ms_strdup("application/vnd.gsma.rcs-ft-http+xml");
+ linphone_chat_message_ref(msg);
+ linphone_chat_message_set_state(msg, LinphoneChatMessageStateFileTransferDone);
+ _release_http_request(msg);
+ _linphone_chat_room_send_message(msg->chat_room, msg);
+ linphone_chat_message_unref(msg);
+ } else {
+ ms_warning("Received empty response from server, file transfer failed");
+ linphone_chat_message_update_state(msg, LinphoneChatMessageStateNotDelivered);
+ _release_http_request(msg);
+ linphone_chat_message_unref(msg);
+ }
+ } else {
+ ms_warning("Unhandled HTTP code response %d for file transfer", code);
+ linphone_chat_message_update_state(msg, LinphoneChatMessageStateNotDelivered);
+ _release_http_request(msg);
+ linphone_chat_message_unref(msg);
+ }
+ }
+}
+
+const LinphoneContent *linphone_chat_message_get_file_transfer_information(const LinphoneChatMessage *msg) {
+ return msg->file_transfer_information;
+}
+
+static void on_recv_body(belle_sip_user_body_handler_t *bh, belle_sip_message_t *m, void *data, size_t offset,
+ const uint8_t *buffer, size_t size) {
+ LinphoneChatMessage *msg = (LinphoneChatMessage *)data;
+ LinphoneCore *lc;
+
+ if (!msg->chat_room) {
+ linphone_chat_message_cancel_file_transfer(msg);
+ return;
+ }
+ lc = msg->chat_room->lc;
+
+ if (lc == NULL){
+ return; /*might happen during linphone_core_destroy()*/
+ }
+
+ if (!msg->http_request || belle_http_request_is_cancelled(msg->http_request)) {
+ ms_warning("Cancelled request for msg [%p], ignoring %s", msg, __FUNCTION__);
+ return;
+ }
+
+ /* first call may be with a zero size, ignore it */
+ if (size == 0) {
+ return;
+ }
+
+ if (linphone_content_get_key(msg->file_transfer_information) !=
+ NULL) { /* we have a key, we must decrypt the file */
+ /* get data from callback to a plainBuffer */
+ char *plainBuffer = (char *)ms_malloc0(size);
+ lime_decryptFile(linphone_content_get_cryptoContext_address(msg->file_transfer_information),
+ (unsigned char *)linphone_content_get_key(msg->file_transfer_information), size, plainBuffer,
+ (char *)buffer);
+ if (linphone_chat_message_cbs_get_file_transfer_recv(msg->callbacks)) {
+ LinphoneBuffer *lb = linphone_buffer_new_from_data((unsigned char *)plainBuffer, size);
+ linphone_chat_message_cbs_get_file_transfer_recv(msg->callbacks)(msg, msg->file_transfer_information, lb);
+ linphone_buffer_unref(lb);
+ } else {
+ /* legacy: call back given by application level */
+ linphone_core_notify_file_transfer_recv(lc, msg, msg->file_transfer_information, plainBuffer, size);
+ }
+ ms_free(plainBuffer);
+ } else { /* regular file, no deciphering */
+ if (linphone_chat_message_cbs_get_file_transfer_recv(msg->callbacks)) {
+ LinphoneBuffer *lb = linphone_buffer_new_from_data(buffer, size);
+ linphone_chat_message_cbs_get_file_transfer_recv(msg->callbacks)(msg, msg->file_transfer_information, lb);
+ linphone_buffer_unref(lb);
+ } else {
+ /* Legacy: call back given by application level */
+ linphone_core_notify_file_transfer_recv(lc, msg, msg->file_transfer_information, (char *)buffer, size);
+ }
+ }
+
+ return;
+}
+
+static LinphoneContent *linphone_chat_create_file_transfer_information_from_headers(const belle_sip_message_t *m) {
+ LinphoneContent *content = linphone_content_new();
+
+ belle_sip_header_content_length_t *content_length_hdr =
+ BELLE_SIP_HEADER_CONTENT_LENGTH(belle_sip_message_get_header(m, "Content-Length"));
+ belle_sip_header_content_type_t *content_type_hdr =
+ BELLE_SIP_HEADER_CONTENT_TYPE(belle_sip_message_get_header(m, "Content-Type"));
+ const char *type = NULL, *subtype = NULL;
+
+ linphone_content_set_name(content, "");
+
+ if (content_type_hdr) {
+ type = belle_sip_header_content_type_get_type(content_type_hdr);
+ subtype = belle_sip_header_content_type_get_subtype(content_type_hdr);
+ ms_message("Extracted content type %s / %s from header", type ? type : "", subtype ? subtype : "");
+ if (type)
+ linphone_content_set_type(content, type);
+ if (subtype)
+ linphone_content_set_subtype(content, subtype);
+ }
+
+ if (content_length_hdr) {
+ linphone_content_set_size(content, belle_sip_header_content_length_get_content_length(content_length_hdr));
+ ms_message("Extracted content length %i from header", (int)linphone_content_get_size(content));
+ }
+
+ return content;
+}
+
+static void linphone_chat_process_response_headers_from_get_file(void *data, const belle_http_response_event_t *event) {
+ LinphoneChatMessage *msg = (LinphoneChatMessage *)data;
+ if (event->response) {
+ /*we are receiving a response, set a specific body handler to acquire the response.
+ * if not done, belle-sip will create a memory body handler, the default*/
+ belle_sip_message_t *response = BELLE_SIP_MESSAGE(event->response);
+ size_t body_size = 0;
+
+ if (msg->file_transfer_information == NULL) {
+ ms_warning("No file transfer information for msg %p: creating...", msg);
+ msg->file_transfer_information = linphone_chat_create_file_transfer_information_from_headers(response);
+ }
+
+ if (msg->file_transfer_information) {
+ body_size = linphone_content_get_size(msg->file_transfer_information);
+ }
+
+ if (msg->file_transfer_filepath == NULL) {
+ belle_sip_message_set_body_handler(
+ (belle_sip_message_t *)event->response,
+ (belle_sip_body_handler_t *)belle_sip_user_body_handler_new(
+ body_size, linphone_chat_message_file_transfer_on_progress, on_recv_body, NULL, msg));
+ } else {
+ belle_sip_body_handler_t *bh = (belle_sip_body_handler_t *)belle_sip_file_body_handler_new(
+ msg->file_transfer_filepath, linphone_chat_message_file_transfer_on_progress, msg);
+ if (belle_sip_body_handler_get_size(bh) == 0) {
+ /* If the size of the body has not been initialized from the file stat, use the one from the
+ * file_transfer_information. */
+ belle_sip_body_handler_set_size(bh, body_size);
+ }
+ belle_sip_message_set_body_handler((belle_sip_message_t *)event->response, bh);
+ }
+ }
+}
+
+static void linphone_chat_process_response_from_get_file(void *data, const belle_http_response_event_t *event) {
+ LinphoneChatMessage *msg = (LinphoneChatMessage *)data;
+ /* check the answer code */
+ if (event->response) {
+ int code = belle_http_response_get_status_code(event->response);
+ if (code == 200) {
+ LinphoneCore *lc = msg->chat_room->lc;
+ if (msg->file_transfer_filepath == NULL) {
+ /* if the file was encrypted, finish the decryption and free context */
+ if (linphone_content_get_key(msg->file_transfer_information) != NULL) {
+ lime_decryptFile(linphone_content_get_cryptoContext_address(msg->file_transfer_information), NULL, 0, NULL, NULL);
+ }
+ if (linphone_chat_message_cbs_get_file_transfer_recv(msg->callbacks)) {
+ LinphoneBuffer *lb = linphone_buffer_new();
+ linphone_chat_message_cbs_get_file_transfer_recv(msg->callbacks)(msg, msg->file_transfer_information, lb);
+ linphone_buffer_unref(lb);
+ } else {
+ linphone_core_notify_file_transfer_recv(lc, msg, msg->file_transfer_information, NULL, 0);
+ }
+ } else {
+ if (linphone_content_get_key(msg->file_transfer_information) != NULL) {
+ bctbx_vfs_t *vfs = bctbx_vfs_get_default();
+ bctbx_vfs_file_t *decrypted_file;
+ bctbx_vfs_file_t *encrypted_file = bctbx_file_open(vfs, msg->file_transfer_filepath, "r");
+ size_t encrypted_file_size = (size_t)bctbx_file_size(encrypted_file);
+ char *encrypted_content = bctbx_malloc(encrypted_file_size);
+ char *decrypted_content = bctbx_malloc(encrypted_file_size);
+ bctbx_file_read(encrypted_file, encrypted_content, encrypted_file_size, 0);
+ bctbx_file_close(encrypted_file);
+ lime_decryptFile(linphone_content_get_cryptoContext_address(msg->file_transfer_information),
+ (unsigned char *)linphone_content_get_key(msg->file_transfer_information),
+ encrypted_file_size, decrypted_content, encrypted_content);
+ lime_decryptFile(linphone_content_get_cryptoContext_address(msg->file_transfer_information), NULL, 0, NULL, NULL);
+ decrypted_file = bctbx_file_open(vfs, msg->file_transfer_filepath, "w");
+ bctbx_file_write(decrypted_file, decrypted_content, encrypted_file_size, 0);
+ bctbx_file_close(decrypted_file);
+ bctbx_free(encrypted_content);
+ bctbx_free(decrypted_content);
+ }
+ linphone_core_notify_file_transfer_recv(lc, msg, msg->file_transfer_information, NULL, 0);
+ }
+ linphone_chat_message_set_state(msg, LinphoneChatMessageStateFileTransferDone);
+ } else if (code >= 400 && code < 500) {
+ ms_warning("File transfer failed with code %d", code);
+ linphone_chat_message_set_state(msg, LinphoneChatMessageStateFileTransferError);
+ } else {
+ ms_warning("Unhandled HTTP code response %d for file transfer", code);
+ }
+ _release_http_request(msg);
+ }
+}
+
+int _linphone_chat_room_start_http_transfer(LinphoneChatMessage *msg, const char* url, const char* action, const belle_http_request_listener_callbacks_t *cbs) {
+ belle_generic_uri_t *uri = NULL;
+ const char* ua = linphone_core_get_user_agent(msg->chat_room->lc);
+
+ if (url == NULL) {
+ ms_warning("Cannot process file transfer msg: no file remote URI configured.");
+ goto error;
+ }
+ uri = belle_generic_uri_parse(url);
+ if (uri == NULL || belle_generic_uri_get_host(uri)==NULL) {
+ ms_warning("Cannot process file transfer msg: incorrect file remote URI configured '%s'.", url);
+ goto error;
+ }
+
+ msg->http_request = belle_http_request_create(action, uri, belle_sip_header_create("User-Agent", ua), NULL);
+
+ if (msg->http_request == NULL) {
+ ms_warning("Could not create http request for uri %s", url);
+ goto error;
+ }
+ /* keep a reference to the http request to be able to cancel it during upload */
+ belle_sip_object_ref(msg->http_request);
+
+ /* give msg to listener to be able to start the actual file upload when server answer a 204 No content */
+ msg->http_listener = belle_http_request_listener_create_from_callbacks(cbs, linphone_chat_message_ref(msg));
+ belle_http_provider_send_request(msg->chat_room->lc->http_provider, msg->http_request, msg->http_listener);
+ return 0;
+error:
+ if (uri) {
+ belle_sip_object_unref(uri);
+ }
+ return -1;
+}
+
+int linphone_chat_room_upload_file(LinphoneChatMessage *msg) {
+ belle_http_request_listener_callbacks_t cbs = {0};
+ int err;
+
+ if (msg->http_request){
+ ms_error("linphone_chat_room_upload_file(): there is already an upload in progress.");
+ return -1;
+ }
+
+ cbs.process_response = linphone_chat_message_process_response_from_post_file;
+ cbs.process_io_error = linphone_chat_message_process_io_error_upload;
+ cbs.process_auth_requested = linphone_chat_message_process_auth_requested_upload;
+ err = _linphone_chat_room_start_http_transfer(msg, linphone_core_get_file_transfer_server(msg->chat_room->lc), "POST", &cbs);
+ if (err == -1){
+ linphone_chat_message_set_state(msg, LinphoneChatMessageStateNotDelivered);
+ }
+ return err;
+}
+
+int linphone_chat_message_download_file(LinphoneChatMessage *msg) {
+ belle_http_request_listener_callbacks_t cbs = {0};
+ int err;
+
+ if (msg->http_request){
+ ms_error("linphone_chat_message_download_file(): there is already a download in progress");
+ return -1;
+ }
+ cbs.process_response_headers = linphone_chat_process_response_headers_from_get_file;
+ cbs.process_response = linphone_chat_process_response_from_get_file;
+ cbs.process_io_error = linphone_chat_message_process_io_error_download;
+ cbs.process_auth_requested = linphone_chat_message_process_auth_requested_download;
+ err = _linphone_chat_room_start_http_transfer(msg, msg->external_body_url, "GET", &cbs);
+ if (err == -1) return -1;
+ /* start the download, status is In Progress */
+ linphone_chat_message_set_state(msg, LinphoneChatMessageStateInProgress);
+ return 0;
+}
+
+void linphone_chat_message_start_file_download(LinphoneChatMessage *msg,
+ LinphoneChatMessageStateChangedCb status_cb, void *ud) {
+ msg->message_state_changed_cb = status_cb;
+ msg->message_state_changed_user_data = ud;
+ linphone_chat_message_download_file(msg);
+}
+
+void linphone_chat_message_cancel_file_transfer(LinphoneChatMessage *msg) {
+ if (msg->http_request) {
+ if (msg->state == LinphoneChatMessageStateInProgress) {
+ linphone_chat_message_set_state(msg, LinphoneChatMessageStateNotDelivered);
+ }
+ if (!belle_http_request_is_cancelled(msg->http_request)) {
+ if (msg->chat_room) {
+ ms_message("Canceling file transfer %s - msg [%p] chat room[%p]"
+ , (msg->external_body_url == NULL) ? linphone_core_get_file_transfer_server(msg->chat_room->lc) : msg->external_body_url
+ , msg
+ , msg->chat_room);
+ belle_http_provider_cancel_request(msg->chat_room->lc->http_provider, msg->http_request);
+ if (msg->dir == LinphoneChatMessageOutgoing) {
+ // must release it
+ linphone_chat_message_unref(msg);
+ }
+ } else {
+ ms_message("Warning: http request still running for ORPHAN msg [%p]: this is a memory leak", msg);
+ }
+ }
+ _release_http_request(msg);
+ } else {
+ ms_message("No existing file transfer - nothing to cancel");
+ }
+}
+
+void linphone_chat_message_set_file_transfer_filepath(LinphoneChatMessage *msg, const char *filepath) {
+ if (msg->file_transfer_filepath != NULL) {
+ ms_free(msg->file_transfer_filepath);
+ }
+ msg->file_transfer_filepath = ms_strdup(filepath);
+}
+
+const char *linphone_chat_message_get_file_transfer_filepath(LinphoneChatMessage *msg) {
+ return msg->file_transfer_filepath;
+}
+
+LinphoneChatMessage *linphone_chat_room_create_file_transfer_message(LinphoneChatRoom *cr,
+ const LinphoneContent *initial_content) {
+ LinphoneChatMessage *msg = belle_sip_object_new(LinphoneChatMessage);
+ msg->callbacks = linphone_chat_message_cbs_new();
+ msg->chat_room = (LinphoneChatRoom *)cr;
+ msg->message = NULL;
+ msg->is_read = TRUE;
+ msg->file_transfer_information = linphone_content_copy(initial_content);
+ msg->dir = LinphoneChatMessageOutgoing;
+ linphone_chat_message_set_to(msg, linphone_chat_room_get_peer_address(cr));
+ msg->from = linphone_address_new(linphone_core_get_identity(cr->lc)); /*direct assignment*/
+ /* this will be set to application/vnd.gsma.rcs-ft-http+xml when we will transfer the xml reply from server to the peers */
+ msg->content_type = NULL;
+ /* this will store the http request during file upload to the server */
+ msg->http_request = NULL;
+ msg->time = ms_time(0);
+ return msg;
+}
diff --git a/coreapi/conference.c b/coreapi/conference.c
deleted file mode 100644
index 5479c8315..000000000
--- a/coreapi/conference.c
+++ /dev/null
@@ -1,445 +0,0 @@
-/***************************************************************************
- * conference.c
- *
- * Mon Sep 12, 2011
- * Copyright 2011 Belledonne Communications
- * Author: Simon Morlat
- * Email simon dot morlat at linphone dot org
- ****************************************************************************/
-
-/*
- * 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 "private.h"
-#include "lpconfig.h"
-
-#include "mediastreamer2/msvolume.h"
-
-/**
- * @addtogroup conferencing
- * @{
-**/
-
-
-static int convert_conference_to_call(LinphoneCore *lc);
-
-static void conference_check_init(LinphoneConference *ctx, int samplerate){
- if (ctx->conf==NULL){
- MSAudioConferenceParams params;
- params.samplerate=samplerate;
- ctx->conf=ms_audio_conference_new(¶ms);
- ctx->terminated=FALSE;
- }
-}
-
-static void remove_local_endpoint(LinphoneConference *ctx){
- if (ctx->local_endpoint){
- ms_audio_conference_remove_member(ctx->conf,ctx->local_endpoint);
- ms_audio_endpoint_release_from_stream(ctx->local_endpoint);
- ctx->local_endpoint=NULL;
- audio_stream_stop(ctx->local_participant);
- ctx->local_participant=NULL;
- rtp_profile_destroy(ctx->local_dummy_profile);
- }
-}
-
-static int linphone_conference_get_size(LinphoneConference *conf){
- if (conf->conf == NULL) {
- return 0;
- }
- return ms_audio_conference_get_size(conf->conf) - (conf->record_endpoint ? 1 : 0);
-}
-
-static int remote_participants_count(LinphoneConference *ctx) {
- int count=linphone_conference_get_size(ctx);
- if (count==0) return 0;
- if (!ctx->local_participant) return count;
- return count -1;
-}
-
-void linphone_core_conference_check_uninit(LinphoneCore *lc){
- LinphoneConference *ctx=&lc->conf_ctx;
- if (ctx->conf){
- int remote_count=remote_participants_count(ctx);
- ms_message("conference_check_uninit(): size=%i",linphone_conference_get_size(ctx));
- if (remote_count==1 && !ctx->terminated){
- convert_conference_to_call(lc);
- }
- if (remote_count==0){
- if (ctx->local_participant!=NULL)
- remove_local_endpoint(ctx);
- if (ctx->record_endpoint){
- ms_audio_conference_remove_member(ctx->conf,ctx->record_endpoint);
- ms_audio_endpoint_destroy(ctx->record_endpoint);
- ctx->record_endpoint=NULL;
- }
- }
-
- if (ms_audio_conference_get_size(ctx->conf)==0){
- ms_audio_conference_destroy(ctx->conf);
- ctx->conf=NULL;
- }
- }
-}
-
-void linphone_call_add_to_conf(LinphoneCall *call, bool_t muted){
- LinphoneCore *lc=call->core;
- LinphoneConference *conf=&lc->conf_ctx;
- MSAudioEndpoint *ep;
- call->params->has_video = FALSE;
- call->camera_enabled = FALSE;
- ep=ms_audio_endpoint_get_from_stream(call->audiostream,TRUE);
- ms_audio_conference_add_member(conf->conf,ep);
- ms_audio_conference_mute_member(conf->conf,ep,muted);
- call->endpoint=ep;
-}
-
-void linphone_call_remove_from_conf(LinphoneCall *call){
- LinphoneCore *lc=call->core;
- LinphoneConference *conf=&lc->conf_ctx;
-
- ms_audio_conference_remove_member(conf->conf,call->endpoint);
- ms_audio_endpoint_release_from_stream(call->endpoint);
- call->endpoint=NULL;
-}
-
-static RtpProfile *make_dummy_profile(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;
-}
-
-static void add_local_endpoint(LinphoneConference *conf,LinphoneCore *lc){
- /*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(65000,65001,FALSE);
- MSSndCard *playcard=lc->sound_conf.lsd_card ?
- lc->sound_conf.lsd_card : lc->sound_conf.play_sndcard;
- MSSndCard *captcard=lc->sound_conf.capt_sndcard;
- const MSAudioConferenceParams *params=ms_audio_conference_get_params(conf->conf);
- conf->local_dummy_profile=make_dummy_profile(params->samplerate);
-
- audio_stream_start_full(st, conf->local_dummy_profile,
- "127.0.0.1",
- 65000,
- "127.0.0.1",
- 65001,
- 0,
- 40,
- NULL,
- NULL,
- playcard,
- captcard,
- linphone_core_echo_cancellation_enabled(lc)
- );
- _post_configure_audio_stream(st,lc,FALSE);
- conf->local_participant=st;
- conf->local_endpoint=ms_audio_endpoint_get_from_stream(st,FALSE);
- ms_audio_conference_add_member(conf->conf,conf->local_endpoint);
-
-}
-
-/**
- * Returns the sound volume (mic input) of the local participant of the conference.
- * @param lc the linphone core
- * @return the measured input volume expressed in dbm0.
- **/
-float linphone_core_get_conference_local_input_volume(LinphoneCore *lc){
- LinphoneConference *conf=&lc->conf_ctx;
- AudioStream *st=conf->local_participant;
- if (st && st->volsend && !conf->local_muted){
- float vol=0;
- ms_filter_call_method(st->volsend,MS_VOLUME_GET,&vol);
- return vol;
-
- }
- return LINPHONE_VOLUME_DB_LOWEST;
-}
-
-/**
- * Merge a call into a conference.
- * @param lc the linphone core
- * @param call an established call, either in LinphoneCallStreamsRunning or LinphoneCallPaused state.
- *
- * If this is the first call that enters the conference, the virtual conference will be created automatically.
- * If the local user was actively part of the call (ie not in paused state), then the local user is automatically entered into the conference.
- * If the call was in paused state, then it is automatically resumed when entering into the conference.
- *
- * @return 0 if successful, -1 otherwise.
-**/
-int linphone_core_add_to_conference(LinphoneCore *lc, LinphoneCall *call){
- LinphoneConference *conf=&lc->conf_ctx;
-
- if (call->current_params->in_conference){
- ms_error("Already in conference");
- return -1;
- }
- conference_check_init(&lc->conf_ctx, lp_config_get_int(lc->config, "sound","conference_rate",16000));
-
- if (call->state==LinphoneCallPaused){
- call->params->in_conference=TRUE;
- call->params->has_video=FALSE;
- linphone_core_resume_call(lc,call);
- }else if (call->state==LinphoneCallStreamsRunning){
- LinphoneCallParams *params=linphone_call_params_copy(linphone_call_get_current_params(call));
- params->in_conference=TRUE;
- params->has_video=FALSE;
-
- if (call->audiostream || call->videostream){
- linphone_call_stop_media_streams(call); /*free the audio & video local resources*/
- linphone_call_init_media_streams(call);
- }
- if (call==lc->current_call){
- lc->current_call=NULL;
- }
- /*this will trigger a reINVITE that will later redraw the streams */
- /*FIXME probably a bit too much to just redraw streams !*/
- linphone_core_update_call(lc,call,params);
- linphone_call_params_destroy(params);
- add_local_endpoint(conf,lc);
- }else{
- ms_error("Call is in state %s, it cannot be added to the conference.",linphone_call_state_to_string(call->state));
- return -1;
- }
- return 0;
-}
-
-static int remove_from_conference(LinphoneCore *lc, LinphoneCall *call, bool_t active){
- int err=0;
- char *str;
-
- if (!call->current_params->in_conference){
- if (call->params->in_conference){
- ms_warning("Not (yet) in conference, be patient");
- return -1;
- }else{
- ms_error("Not in a conference.");
- return -1;
- }
- }
- call->params->in_conference=FALSE;
-
- str=linphone_call_get_remote_address_as_string(call);
- ms_message("%s will be removed from conference", str);
- ms_free(str);
- if (active){
- LinphoneCallParams *params=linphone_call_params_copy(linphone_call_get_current_params(call));
- params->in_conference=FALSE;
- // reconnect local audio with this call
- if (linphone_core_is_in_conference(lc)){
- ms_message("Leaving conference for reconnecting with unique call.");
- linphone_core_leave_conference(lc);
- }
- ms_message("Updating call to actually remove from conference");
- err=linphone_core_update_call(lc,call,params);
- linphone_call_params_destroy(params);
- } else{
- ms_message("Pausing call to actually remove from conference");
- err=_linphone_core_pause_call(lc,call);
- }
- return err;
-}
-
-static int convert_conference_to_call(LinphoneCore *lc){
- int err=0;
- MSList *calls=lc->calls;
-
- if (remote_participants_count(&lc->conf_ctx)!=1){
- ms_error("No unique call remaining in conference.");
- return -1;
- }
-
- while (calls) {
- LinphoneCall *rc=(LinphoneCall*)calls->data;
- calls=calls->next;
- if (rc->params->in_conference) { // not using current_param
- bool_t active_after_removed=linphone_core_is_in_conference(lc);
- err=remove_from_conference(lc, rc, active_after_removed);
- break;
- }
- }
- return err;
-}
-
-/**
- * Remove a call from the conference.
- * @param lc the linphone core
- * @param call a call that has been previously merged into the conference.
- *
- * After removing the remote participant belonging to the supplied call, the call becomes a normal call in paused state.
- * If one single remote participant is left alone together with the local user in the conference after the removal, then the conference is
- * automatically transformed into a simple call in StreamsRunning state.
- * The conference's resources are then automatically destroyed.
- *
- * In other words, unless linphone_core_leave_conference() is explicitely called, the last remote participant of a conference is automatically
- * put in a simple call in running state.
- *
- * @return 0 if successful, -1 otherwise.
- **/
-int linphone_core_remove_from_conference(LinphoneCore *lc, LinphoneCall *call){
- int err;
- char * str=linphone_call_get_remote_address_as_string(call);
- ms_message("Removing call %s from the conference", str);
- ms_free(str);
- err=remove_from_conference(lc,call, FALSE);
- if (err){
- ms_error("Error removing participant from conference.");
- return err;
- }
-
- if (remote_participants_count(&lc->conf_ctx)==1){
- ms_message("conference size is 1: need to be converted to plain call");
- err=convert_conference_to_call(lc);
- } else {
- ms_message("the conference need not to be converted as size is %i", remote_participants_count(&lc->conf_ctx));
- }
- return err;
-}
-
-bool_t linphone_core_is_in_conference(const LinphoneCore *lc){
- return lc->conf_ctx.local_participant!=NULL;
-}
-
-/**
- * Moves the local participant out of the conference.
- * @param lc the linphone core
- * When the local participant is out of the conference, the remote participants can continue to talk normally.
- * @return 0 if successful, -1 otherwise.
-**/
-int linphone_core_leave_conference(LinphoneCore *lc){
- LinphoneConference *conf=&lc->conf_ctx;
- if (linphone_core_is_in_conference(lc))
- remove_local_endpoint(conf);
- return 0;
-}
-
-/**
- * Moves the local participant inside the conference.
- * @param lc the linphone core
- *
- * Makes the local participant to join the conference.
- * Typically, the local participant is by default always part of the conference when joining an active call into a conference.
- * However, by calling linphone_core_leave_conference() and linphone_core_enter_conference() the application can decide to temporarily
- * move out and in the local participant from the conference.
- *
- * @return 0 if successful, -1 otherwise
-**/
-int linphone_core_enter_conference(LinphoneCore *lc){
- LinphoneConference *conf;
- if (linphone_core_sound_resources_locked(lc)) {
- return -1;
- }
- if (lc->current_call != NULL) {
- _linphone_core_pause_call(lc, lc->current_call);
- }
- conf=&lc->conf_ctx;
- if (conf->local_participant==NULL) add_local_endpoint(conf,lc);
- return 0;
-}
-
-/**
- * Add all calls into a conference.
- * @param lc the linphone core
- *
- * Merge all established calls (either in LinphoneCallStreamsRunning or LinphoneCallPaused) into a conference.
- *
- * @return 0 if successful, -1 otherwise
-**/
-int linphone_core_add_all_to_conference(LinphoneCore *lc) {
- MSList *calls=lc->calls;
- while (calls) {
- LinphoneCall *call=(LinphoneCall*)calls->data;
- calls=calls->next;
- if (!call->current_params->in_conference) {
- linphone_core_add_to_conference(lc, call);
- }
- }
- linphone_core_enter_conference(lc);
- return 0;
-}
-
-/**
- * Terminates the conference and the calls associated with it.
- * @param lc the linphone core
- *
- * All the calls that were merged to the conference are terminated, and the conference resources are destroyed.
- *
- * @return 0 if successful, -1 otherwise
-**/
-int linphone_core_terminate_conference(LinphoneCore *lc) {
- MSList *calls=lc->calls;
- LinphoneConference *conf=&lc->conf_ctx;
- conf->terminated=TRUE;
-
- while (calls) {
- LinphoneCall *call=(LinphoneCall*)calls->data;
- calls=calls->next;
- if (call->current_params->in_conference) {
- linphone_core_terminate_call(lc, call);
- }
- }
- return 0;
-}
-
-/**
- * Returns the number of participants to the conference, including the local participant.
- * @param lc the linphone core
- *
- * Typically, after merging two calls into the conference, there is total of 3 participants:
- * the local participant (or local user), and two remote participants that were the destinations of the two previously establised calls.
- *
- * @return the number of participants to the conference
-**/
-int linphone_core_get_conference_size(LinphoneCore *lc) {
- LinphoneConference *conf=&lc->conf_ctx;
- return linphone_conference_get_size(conf);
-}
-
-
-int linphone_core_start_conference_recording(LinphoneCore *lc, const char *path){
- LinphoneConference *conf=&lc->conf_ctx;
- if (conf->conf == NULL) {
- ms_warning("linphone_core_start_conference_recording(): no conference now.");
- return -1;
- }
- if (conf->record_endpoint==NULL){
- conf->record_endpoint=ms_audio_endpoint_new_recorder();
- ms_audio_conference_add_member(conf->conf,conf->record_endpoint);
- }
- ms_audio_recorder_endpoint_start(conf->record_endpoint,path);
- return 0;
-}
-
-int linphone_core_stop_conference_recording(LinphoneCore *lc){
- LinphoneConference *conf=&lc->conf_ctx;
- if (conf->conf == NULL) {
- ms_warning("linphone_core_stop_conference_recording(): no conference now.");
- return -1;
- }
- if (conf->record_endpoint==NULL){
- ms_warning("linphone_core_stop_conference_recording(): no record active.");
- return -1;
- }
- ms_audio_recorder_endpoint_stop(conf->record_endpoint);
- return 0;
-}
-
-/**
- * @}
-**/
-
diff --git a/coreapi/conference.cc b/coreapi/conference.cc
new file mode 100644
index 000000000..16b9f82a4
--- /dev/null
+++ b/coreapi/conference.cc
@@ -0,0 +1,1048 @@
+/*******************************************************************************
+ * conference.cc
+ *
+ * Thu Nov 26, 2015
+ * Copyright 2015 Belledonne Communications
+ * Author: Linphone's team
+ * Email info@belledonne-communications.com
+ ******************************************************************************/
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 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 "private.h"
+#include "conference_private.h"
+#include
+#include
+#include
+#include
+
+namespace Linphone {
+
+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);
+ if(m_call) m_call->conf_ref = NULL;
+
+ }
+
+ 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;
+ };
+
+
+ class Params {
+ public:
+ Params(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;
+ }
+
+ private:
+ bool m_enableVideo;
+ LinphoneConferenceStateChangedCb m_stateChangedCb;
+ void *m_userData;
+
+ friend class Conference;
+ };
+
+ Conference(LinphoneCore *core, const Params *params = NULL);
+ virtual ~Conference() {};
+
+ const Params &getCurrentParams() const {return m_currentParams;}
+
+ 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 std::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;}
+ static const char *stateToString(LinphoneConferenceState state);
+
+protected:
+ void setState(LinphoneConferenceState state);
+ Participant *findParticipant(const LinphoneCall *call) const;
+ Participant *findParticipant(const LinphoneAddress *uri) const;
+
+protected:
+ LinphoneCore *m_core;
+ AudioStream *m_localParticipantStream;
+ bool m_isMuted;
+ std::list m_participants;
+ Params m_currentParams;
+ LinphoneConferenceState m_state;
+};
+
+class LocalConference: public Conference {
+public:
+ LocalConference(LinphoneCore *core, const Params *params = NULL);
+ virtual ~LocalConference();
+
+ virtual int addParticipant(LinphoneCall *call);
+ virtual int removeParticipant(LinphoneCall *call);
+ virtual int removeParticipant(const LinphoneAddress *uri);
+ virtual int terminate();
+
+ virtual int enter();
+ virtual int leave();
+ virtual bool isIn() const {return m_localParticipantStream!=NULL;}
+ virtual int getSize() const;
+
+ virtual int startRecording(const char *path);
+ virtual int stopRecording();
+
+ virtual void onCallStreamStarting(LinphoneCall *call, bool isPausedByRemote);
+ virtual void onCallStreamStopping(LinphoneCall *call);
+ virtual void onCallTerminating(LinphoneCall *call);
+
+private:
+ void addLocalEndpoint();
+ int remoteParticipantsCount();
+ void removeLocalEndpoint();
+ int removeFromConference(LinphoneCall *call, bool_t active);
+ int convertConferenceToCall();
+ 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, const Params *params = NULL);
+ virtual ~RemoteConference();
+
+ virtual int addParticipant(LinphoneCall *call);
+ virtual int removeParticipant(LinphoneCall *call) {return -1;}
+ virtual int removeParticipant(const LinphoneAddress *uri);
+ virtual int terminate();
+
+ virtual int enter();
+ virtual int leave();
+ virtual bool isIn() const;
+
+ virtual int startRecording(const char *path) {return 0;}
+ virtual int stopRecording() {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;
+ LinphoneCoreVTable *m_vtable;
+ std::list m_pendingCalls;
+ std::list m_transferingCalls;
+};
+
+};
+
+
+using namespace Linphone;
+using namespace std;
+
+
+Conference::Conference(LinphoneCore *core, const Conference::Params *params):
+ m_core(core),
+ m_localParticipantStream(NULL),
+ m_isMuted(false),
+ m_currentParams(),
+ m_state(LinphoneConferenceStopped) {
+ if(params) m_currentParams = *params;
+}
+
+int Conference::addParticipant(LinphoneCall *call) {
+ Participant *p =new Participant(call);
+ m_participants.push_back(p);
+ call->conf_ref = (LinphoneConference *)this;
+ return 0;
+}
+
+int Conference::removeParticipant(LinphoneCall *call) {
+ Participant *p = findParticipant(call);
+ if(p == NULL) return -1;
+ delete p;
+ m_participants.remove(p);
+ return 0;
+}
+
+int Conference::removeParticipant(const LinphoneAddress *uri) {
+ Participant *p = findParticipant(uri);
+ if(p == NULL) return -1;
+ delete p;
+ m_participants.remove(p);
+ return 0;
+}
+
+int Conference::terminate() {
+ for(list::iterator 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 "Startig 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((LinphoneConference *)this, state, m_currentParams.m_userData);
+ }
+ }
+}
+
+Conference::Participant *Conference::findParticipant(const LinphoneCall *call) const {
+ for(list::const_iterator it=m_participants.begin(); it!=m_participants.end(); it++) {
+ if((*it)->getCall() == call) return *it;
+ }
+ return NULL;
+}
+
+Conference::Participant *Conference::findParticipant(const LinphoneAddress *uri) const {
+ for(list::const_iterator it=m_participants.begin(); it!=m_participants.end(); it++) {
+ if(linphone_address_equal((*it)->getUri(), uri)) return *it;
+ }
+ return NULL;
+}
+
+
+
+
+LocalConference::LocalConference(LinphoneCore *core, const Conference::Params *params):
+ Conference(core, params),
+ m_conf(NULL),
+ m_localEndpoint(NULL),
+ m_recordEndpoint(NULL),
+ m_localDummyProfile(NULL),
+ 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() {
+ terminate();
+ 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() {
+ /*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,
+ NULL,
+ NULL,
+ 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_audio_conference_add_member(m_conf,m_localEndpoint);
+}
+
+int LocalConference::addParticipant(LinphoneCall *call) {
+ if (call->current_params->in_conference){
+ ms_error("Already in conference");
+ return -1;
+ }
+
+ if (call->state==LinphoneCallPaused){
+ call->params->in_conference=TRUE;
+ call->params->has_video=FALSE;
+ linphone_core_resume_call(m_core,call);
+ }else if (call->state==LinphoneCallStreamsRunning){
+ LinphoneCallParams *params=linphone_call_params_copy(linphone_call_get_current_params(call));
+ params->in_conference=TRUE;
+ params->has_video=FALSE;
+
+ if (call->audiostream || call->videostream){
+ linphone_call_stop_media_streams(call); /*free the audio & video local resources*/
+ linphone_call_init_media_streams(call);
+ }
+ if (call==m_core->current_call){
+ m_core->current_call=NULL;
+ }
+ /*this will trigger a reINVITE that will later redraw the streams */
+ /*FIXME probably a bit too much to just redraw streams !*/
+ linphone_core_update_call(m_core,call,params);
+ linphone_call_params_destroy(params);
+ addLocalEndpoint();
+ }else{
+ ms_error("Call is in state %s, it cannot be added to the conference.",linphone_call_state_to_string(call->state));
+ return -1;
+ }
+ return 0;
+}
+
+int LocalConference::removeFromConference(LinphoneCall *call, bool_t active){
+ int err=0;
+ char *str;
+
+ if (!call->current_params->in_conference){
+ if (call->params->in_conference){
+ ms_warning("Not (yet) in conference, be patient");
+ return -1;
+ }else{
+ ms_error("Not in a conference.");
+ return -1;
+ }
+ }
+ call->params->in_conference=FALSE;
+
+ str=linphone_call_get_remote_address_as_string(call);
+ ms_message("%s will be removed from conference", str);
+ ms_free(str);
+ if (active){
+ LinphoneCallParams *params=linphone_call_params_copy(linphone_call_get_current_params(call));
+ params->in_conference=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_core_update_call(m_core,call,params);
+ linphone_call_params_destroy(params);
+ } else{
+ ms_message("Pausing call to actually remove from conference");
+ err=_linphone_core_pause_call(m_core,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(){
+ int err=0;
+ bctbx_list_t *calls=m_core->calls;
+
+ if (remoteParticipantsCount()!=1){
+ ms_error("No unique call remaining in conference.");
+ return -1;
+ }
+
+ while (calls) {
+ LinphoneCall *rc=(LinphoneCall*)calls->data;
+ calls=calls->next;
+ if (rc->params->in_conference) { // not using current_param
+ bool_t active_after_removed=isIn();
+ err=removeFromConference(rc, active_after_removed);
+ break;
+ }
+ }
+ return err;
+}
+
+int LocalConference::removeParticipant(LinphoneCall *call) {
+ int err;
+ char * str=linphone_call_get_remote_address_as_string(call);
+ ms_message("Removing call %s from the conference", str);
+ ms_free(str);
+ err=removeFromConference(call, FALSE);
+ if (err){
+ ms_error("Error removing participant from conference.");
+ return err;
+ }
+
+ if (remoteParticipantsCount()==1){
+ 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 == NULL) return -1;
+ LinphoneCall *call = participant->getCall();
+ if(call == NULL) return -1;
+ return removeParticipant(call);
+}
+
+int LocalConference::terminate() {
+ bctbx_list_t *calls=m_core->calls;
+ m_terminating =TRUE;
+
+ while (calls) {
+ LinphoneCall *call=(LinphoneCall*)calls->data;
+ calls=calls->next;
+ if (call->current_params->in_conference) {
+ linphone_core_terminate_call(m_core, call);
+ }
+ }
+
+ Conference::terminate();
+ m_terminating = FALSE;
+
+ return 0;
+}
+
+int LocalConference::enter() {
+ if (linphone_core_sound_resources_locked(m_core)) {
+ return -1;
+ }
+ if (m_core->current_call != NULL) {
+ _linphone_core_pause_call(m_core, m_core->current_call);
+ }
+ if (m_localParticipantStream==NULL) 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=NULL;
+ audio_stream_stop(m_localParticipantStream);
+ m_localParticipantStream=NULL;
+ rtp_profile_destroy(m_localDummyProfile);
+ }
+}
+
+int LocalConference::leave() {
+ if (isIn())
+ removeLocalEndpoint();
+ return 0;
+}
+
+int LocalConference::getSize() const {
+ if (m_conf == NULL) {
+ return 0;
+ }
+ return ms_audio_conference_get_size(m_conf) - (m_recordEndpoint ? 1 : 0);
+}
+
+int LocalConference::startRecording(const char *path) {
+ if (m_conf == NULL) {
+ ms_warning("linphone_core_start_conference_recording(): no conference now.");
+ return -1;
+ }
+ if (m_recordEndpoint==NULL){
+ 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 == NULL) {
+ ms_warning("linphone_core_stop_conference_recording(): no conference now.");
+ return -1;
+ }
+ if (m_recordEndpoint==NULL){
+ 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) {
+ call->params->has_video = FALSE;
+ call->camera_enabled = FALSE;
+ MSAudioEndpoint *ep=ms_audio_endpoint_get_from_stream(call->audiostream,TRUE);
+ ms_audio_conference_add_member(m_conf,ep);
+ ms_audio_conference_mute_member(m_conf,ep,isPausedByRemote);
+ call->endpoint=ep;
+ setState(LinphoneConferenceRunning);
+ Conference::addParticipant(call);
+}
+
+void LocalConference::onCallStreamStopping(LinphoneCall *call) {
+ ms_audio_conference_remove_member(m_conf,call->endpoint);
+ ms_audio_endpoint_release_from_stream(call->endpoint);
+ call->endpoint=NULL;
+ Conference::removeParticipant(call);
+}
+
+void LocalConference::onCallTerminating(LinphoneCall *call) {
+ int remote_count=remoteParticipantsCount();
+ ms_message("conference_check_uninit(): size=%i", getSize());
+ if (remote_count==1 && !m_terminating){
+ convertConferenceToCall();
+ }
+ if (remote_count==0){
+ if (m_localParticipantStream)
+ removeLocalEndpoint();
+ if (m_recordEndpoint){
+ ms_audio_conference_remove_member(m_conf, m_recordEndpoint);
+ ms_audio_endpoint_destroy(m_recordEndpoint);
+ }
+ setState(LinphoneConferenceStopped);
+ }
+}
+
+
+
+RemoteConference::RemoteConference(LinphoneCore *core, const Conference::Params *params): Conference(core, params) {
+ m_focusAddr = NULL;
+ m_focusContact = NULL;
+ m_focusCall = NULL;
+ m_vtable = NULL;
+ m_focusAddr = lp_config_get_string(m_core->config, "misc", "conference_focus_addr", "");
+ m_vtable = linphone_core_v_table_new();
+ m_vtable->call_state_changed = callStateChangedCb;
+ m_vtable->transfer_state_changed = transferStateChanged;
+ linphone_core_v_table_set_user_data(m_vtable, this);
+ _linphone_core_add_listener(m_core, m_vtable, FALSE, TRUE);
+}
+
+RemoteConference::~RemoteConference() {
+ terminate();
+ linphone_core_remove_listener(m_core, m_vtable);
+ linphone_core_v_table_destroy(m_vtable);
+}
+
+int RemoteConference::addParticipant(LinphoneCall *call) {
+ LinphoneAddress *addr;
+ LinphoneCallParams *params;
+
+ 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) {
+ params = linphone_core_create_call_params(m_core, NULL);
+ linphone_call_params_enable_video(params, m_currentParams.videoRequested());
+ m_focusCall = linphone_core_invite_address_with_params(m_core, addr, params);
+ m_localParticipantStream = m_focusCall->audiostream;
+ m_pendingCalls.push_back(call);
+ LinphoneCallLog *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;
+ } else return -1;
+
+ 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) == NULL) {
+ 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 = sal_call_refer(m_focusCall->op, 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_core_terminate_call(m_core, 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_core_resume_call(m_core, 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_core_pause_call(m_core, 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 == NULL) return false;
+ focusState = linphone_call_get_state(m_focusCall);
+ return focusState == LinphoneCallStreamsRunning || focusState == LinphoneCallPaused;
+}
+
+bool RemoteConference::transferToFocus(LinphoneCall *call) {
+ if(linphone_core_transfer_call(m_core, 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 = NULL;
+ m_focusAddr = NULL;
+ if(m_focusContact) {
+ ms_free(m_focusContact);
+ m_focusContact = NULL;
+ }
+ m_focusCall = NULL;
+ 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_core_transfer_call(m_core, 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 = NULL;
+ 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);
+ }
+}
+
+
+
+const char *linphone_conference_state_to_string(LinphoneConferenceState state) {
+ return Conference::stateToString(state);
+}
+
+LinphoneConferenceParams *linphone_conference_params_new(const LinphoneCore *core) {
+ return (LinphoneConferenceParams *)new Conference::Params(core);
+}
+
+void linphone_conference_params_free(LinphoneConferenceParams *params) {
+ delete (Conference::Params *)params;
+}
+
+LinphoneConferenceParams *linphone_conference_params_clone(const LinphoneConferenceParams *params) {
+ return (LinphoneConferenceParams *)new Conference::Params(*(Conference::Params *)params);
+}
+
+void linphone_conference_params_enable_video(LinphoneConferenceParams *params, bool_t enable) {
+ ((Conference::Params *)params)->enableVideo((enable == TRUE) ? true : false);
+}
+
+bool_t linphone_conference_params_video_requested(const LinphoneConferenceParams *params) {
+ return ((Conference::Params *)params)->videoRequested();
+}
+
+void linphone_conference_params_set_state_changed_callback(LinphoneConferenceParams *params, LinphoneConferenceStateChangedCb cb, void *user_data) {
+ ((Conference::Params *)params)->setStateChangedCallback(cb, user_data);
+}
+
+
+
+LinphoneConference *linphone_local_conference_new(LinphoneCore *core) {
+ return (LinphoneConference *) new LocalConference(core);
+}
+
+LinphoneConference *linphone_local_conference_new_with_params(LinphoneCore *core, const LinphoneConferenceParams *params) {
+ return (LinphoneConference *) new LocalConference(core, (Conference::Params *)params);
+}
+
+LinphoneConference *linphone_remote_conference_new(LinphoneCore *core) {
+ return (LinphoneConference *) new RemoteConference(core);
+}
+
+LinphoneConference *linphone_remote_conference_new_with_params(LinphoneCore *core, const LinphoneConferenceParams *params) {
+ return (LinphoneConference *) new RemoteConference(core, (Conference::Params *)params);
+}
+
+void linphone_conference_free(LinphoneConference *obj) {
+ delete (Conference *)obj;
+}
+
+LinphoneConferenceState linphone_conference_get_state(const LinphoneConference *obj) {
+ return ((Conference *)obj)->getState();
+}
+
+int linphone_conference_add_participant(LinphoneConference *obj, LinphoneCall *call) {
+ return ((Conference *)obj)->addParticipant(call);
+}
+
+int linphone_conference_remove_participant(LinphoneConference *obj, const LinphoneAddress *uri) {
+ return ((Conference *)obj)->removeParticipant(uri);
+}
+
+int linphone_conference_remove_participant_with_call(LinphoneConference *obj, LinphoneCall *call) {
+ return ((Conference *)obj)->removeParticipant(call);
+}
+
+int linphone_conference_terminate(LinphoneConference *obj) {
+ return ((Conference *)obj)->terminate();
+}
+
+int linphone_conference_enter(LinphoneConference *obj) {
+ return ((Conference *)obj)->enter();
+}
+
+int linphone_conference_leave(LinphoneConference *obj) {
+ return ((Conference *)obj)->leave();
+}
+
+bool_t linphone_conference_is_in(const LinphoneConference *obj) {
+ return ((Conference *)obj)->isIn();
+}
+
+AudioStream *linphone_conference_get_audio_stream(const LinphoneConference *obj) {
+ return ((Conference *)obj)->getAudioStream();
+}
+
+int linphone_conference_mute_microphone(LinphoneConference *obj, bool_t val) {
+ return ((Conference *)obj)->muteMicrophone((val == TRUE) ? true : false);
+}
+
+bool_t linphone_conference_microphone_is_muted(const LinphoneConference *obj) {
+ return ((Conference *)obj)->microphoneIsMuted();
+}
+
+float linphone_conference_get_input_volume(const LinphoneConference *obj) {
+ return ((Conference *)obj)->getInputVolume();
+}
+
+int linphone_conference_get_size(const LinphoneConference *obj) {
+ return ((Conference *)obj)->getSize();
+}
+
+bctbx_list_t *linphone_conference_get_participants(const LinphoneConference *obj) {
+ const list &participants = ((Conference *)obj)->getParticipants();
+ bctbx_list_t *participants_list = NULL;
+ for(list::const_iterator 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 ((Conference *)obj)->startRecording(path);
+}
+
+int linphone_conference_stop_recording(LinphoneConference *obj) {
+ return ((Conference *)obj)->stopRecording();
+}
+
+void linphone_conference_on_call_stream_starting(LinphoneConference *obj, LinphoneCall *call, bool_t is_paused_by_remote) {
+ ((Conference *)obj)->onCallStreamStarting(call, (is_paused_by_remote == TRUE) ? true : false);
+}
+
+void linphone_conference_on_call_stream_stopping(LinphoneConference *obj, LinphoneCall *call) {
+ ((Conference *)obj)->onCallStreamStopping(call);
+}
+
+void linphone_conference_on_call_terminating(LinphoneConference *obj, LinphoneCall *call) {
+ ((Conference *)obj)->onCallTerminating(call);
+}
+
+bool_t linphone_conference_check_class(LinphoneConference *obj, LinphoneConferenceClass _class) {
+ switch(_class) {
+ case LinphoneConferenceClassLocal: return typeid(obj) == typeid(LocalConference);
+ case LinphoneConferenceClassRemote: return typeid(obj) == typeid(RemoteConference);
+ default: return FALSE;
+ }
+}
diff --git a/coreapi/conference.h b/coreapi/conference.h
new file mode 100644
index 000000000..269e442f1
--- /dev/null
+++ b/coreapi/conference.h
@@ -0,0 +1,118 @@
+/*******************************************************************************
+ * conference.h
+ *
+ * Thu Nov 26, 2015
+ * Copyright 2015 Belledonne Communications
+ * Author: Linphone's team
+ * Email info@belledonne-communications.com
+ ******************************************************************************/
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 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_H
+#define CONFERENCE_H
+
+#include "linphonecore.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @addtogroup call_control
+ * @{
+ */
+
+/**
+ * LinphoneConference class
+ * The _LinphoneConference struct does not exists, it's the Conference C++ class that is used behind
+ */
+typedef struct _LinphoneConference LinphoneConference;
+
+/**
+ * Parameters for initialization of conferences
+ * The _LinphoneConferenceParams struct does not exists, it's the ConferenceParams C++ class that is used behind
+ */
+typedef struct _LinphoneConferenceParams LinphoneConferenceParams;
+
+
+
+
+/**
+ * Create a #LinphoneConferenceParams with default parameters set.
+ * @param core #LinphoneCore to use to find out the default parameters. Can be NULL.
+ * @return A freshly allocated #LinphoneConferenceParams
+ */
+LINPHONE_PUBLIC LinphoneConferenceParams *linphone_conference_params_new(const LinphoneCore *core);
+
+/**
+ * Free a #LinphoneConferenceParams
+ * @param params #LinphoneConferenceParams to free
+ */
+LINPHONE_PUBLIC void linphone_conference_params_free(LinphoneConferenceParams *params);
+
+/**
+ * Clone a #LinphoneConferenceParams
+ * @param params The #LinphoneConferenceParams to clone
+ * @return An allocated #LinphoneConferenceParams with the same parameters than params
+ */
+LINPHONE_PUBLIC LinphoneConferenceParams *linphone_conference_params_clone(const LinphoneConferenceParams *params);
+
+/**
+ * Enable video when starting a conference
+ * @param params A #LinphoneConferenceParams
+ * @param enable If true, video will be enabled during conference
+ */
+LINPHONE_PUBLIC void linphone_conference_params_enable_video(LinphoneConferenceParams *params, bool_t enable);
+
+/**
+ * Check whether video will be enable at conference starting
+ * @return if true, the video will be enable at conference starting
+ */
+LINPHONE_PUBLIC bool_t linphone_conference_params_video_requested(const LinphoneConferenceParams *params);
+
+
+
+
+
+/**
+ * Remove a participant from a conference
+ * @param obj A #LinphoneConference
+ * @param uri SIP URI of the participant to remove
+ * @warning The passed SIP URI must be one of the URIs returned by linphone_conference_get_participants()
+ * @return 0 if succeeded, -1 if failed
+ */
+LINPHONE_PUBLIC int linphone_conference_remove_participant(LinphoneConference *obj, const LinphoneAddress *uri);
+
+/**
+ * Get URIs of all participants of one conference
+ * The returned bctbx_list_t contains URIs of all participant. That list must be
+ * freed after use and each URI must be unref with linphone_address_unref()
+ * @param obj A #LinphoneConference
+ * @return \mslist{LinphoneAddress}
+ */
+LINPHONE_PUBLIC bctbx_list_t *linphone_conference_get_participants(const LinphoneConference *obj);
+
+/**
+ * @}
+ */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // CONFERENCE_H
diff --git a/coreapi/conference_private.h b/coreapi/conference_private.h
new file mode 100644
index 000000000..eaa649cd1
--- /dev/null
+++ b/coreapi/conference_private.h
@@ -0,0 +1,111 @@
+/*******************************************************************************
+ * conference_private.h
+ *
+ * Tue Jan 12, 2015
+ * Copyright 2015 Belledonne Communications
+ * Author: Linphone's team
+ * Email info@belledonne-communications.com
+ ******************************************************************************/
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 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 "linphonecore.h"
+#include "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);
+void linphone_conference_free(LinphoneConference *obj);
+/**
+ * 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
diff --git a/coreapi/contactprovider.c b/coreapi/contactprovider.c
index 2ee037c78..ce7991a3e 100644
--- a/coreapi/contactprovider.c
+++ b/coreapi/contactprovider.c
@@ -130,6 +130,7 @@ BELLE_SIP_INSTANCIATE_CUSTOM_VPTR_BEGIN(LinphoneContactProvider)
(belle_sip_object_destroy_t) contact_provider_destroy,
NULL,/*no clone*/
NULL,/*no marshal*/
+ BELLE_SIP_DEFAULT_BUFSIZE_HINT
},
"",
// Pure virtual
diff --git a/coreapi/content.c b/coreapi/content.c
index 23eb6c26c..a158d2b9f 100644
--- a/coreapi/content.c
+++ b/coreapi/content.c
@@ -22,23 +22,46 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+static void linphone_content_set_sal_body_handler(LinphoneContent *content, SalBodyHandler *body_handler) {
+ if (content->body_handler != NULL) {
+ sal_body_handler_unref(content->body_handler);
+ content->body_handler = NULL;
+ }
+ content->body_handler = sal_body_handler_ref(body_handler);
+}
+
+static LinphoneContent * linphone_content_new_with_body_handler(SalBodyHandler *body_handler) {
+ LinphoneContent *content = belle_sip_object_new(LinphoneContent);
+ belle_sip_object_ref(content);
+ content->owned_fields = TRUE;
+ content->cryptoContext = NULL; /* this field is managed externally by encryption/decryption functions so be careful to initialise it to NULL */
+ if (body_handler == NULL) {
+ linphone_content_set_sal_body_handler(content, sal_body_handler_new());
+ } else {
+ linphone_content_set_sal_body_handler(content, body_handler);
+ }
+ return content;
+}
+
static void linphone_content_destroy(LinphoneContent *content) {
if (content->owned_fields == TRUE) {
- if (content->lcp.type) belle_sip_free(content->lcp.type);
- if (content->lcp.subtype) belle_sip_free(content->lcp.subtype);
- if (content->lcp.data) belle_sip_free(content->lcp.data);
- if (content->lcp.encoding) belle_sip_free(content->lcp.encoding);
- if (content->lcp.name) belle_sip_free(content->lcp.name);
- if (content->lcp.key) belle_sip_free(content->lcp.key);
+ if (content->body_handler) sal_body_handler_unref(content->body_handler);
+ if (content->name) belle_sip_free(content->name);
+ if (content->key) belle_sip_free(content->key);
/* note : crypto context is allocated/destroyed by the encryption function */
}
}
static void linphone_content_clone(LinphoneContent *obj, const LinphoneContent *ref) {
obj->owned_fields = TRUE;
- linphone_content_set_type(obj, linphone_content_get_type(ref));
- linphone_content_set_subtype(obj, linphone_content_get_subtype(ref));
- linphone_content_set_encoding(obj, linphone_content_get_encoding(ref));
+ linphone_content_set_sal_body_handler(obj, sal_body_handler_new());
+ if ((linphone_content_get_type(ref) != NULL) || (linphone_content_get_subtype(ref) != NULL)) {
+ linphone_content_set_type(obj, linphone_content_get_type(ref));
+ linphone_content_set_subtype(obj, linphone_content_get_subtype(ref));
+ }
+ if (linphone_content_get_encoding(ref) != NULL) {
+ linphone_content_set_encoding(obj, linphone_content_get_encoding(ref));
+ }
linphone_content_set_name(obj, linphone_content_get_name(ref));
linphone_content_set_key(obj, linphone_content_get_key(ref), linphone_content_get_key_size(ref));
if (linphone_content_get_buffer(ref) != NULL) {
@@ -81,163 +104,137 @@ void linphone_content_set_user_data(LinphoneContent *content, void *ud) {
}
const char * linphone_content_get_type(const LinphoneContent *content) {
- return content->lcp.type;
+ return sal_body_handler_get_type(content->body_handler);
}
void linphone_content_set_type(LinphoneContent *content, const char *type) {
- if (content->lcp.type != NULL) {
- belle_sip_free(content->lcp.type);
- content->lcp.type = NULL;
- }
- if (type != NULL) {
- content->lcp.type = belle_sip_strdup(type);
- }
+ sal_body_handler_set_type(content->body_handler, type);
}
const char * linphone_content_get_subtype(const LinphoneContent *content) {
- return content->lcp.subtype;
+ return sal_body_handler_get_subtype(content->body_handler);
}
void linphone_content_set_subtype(LinphoneContent *content, const char *subtype) {
- if (content->lcp.subtype != NULL) {
- belle_sip_free(content->lcp.subtype);
- content->lcp.subtype = NULL;
- }
- if (subtype != NULL) {
- content->lcp.subtype = belle_sip_strdup(subtype);
- }
+ sal_body_handler_set_subtype(content->body_handler, subtype);
}
void * linphone_content_get_buffer(const LinphoneContent *content) {
- return content->lcp.data;
+ return sal_body_handler_get_data(content->body_handler);
}
void linphone_content_set_buffer(LinphoneContent *content, const void *buffer, size_t size) {
- content->lcp.size = size;
- content->lcp.data = belle_sip_malloc(size + 1);
- memcpy(content->lcp.data, buffer, size);
- ((char *)content->lcp.data)[size] = '\0';
+ void *data;
+ sal_body_handler_set_size(content->body_handler, size);
+ data = belle_sip_malloc(size + 1);
+ memcpy(data, buffer, size);
+ ((char *)data)[size] = '\0';
+ sal_body_handler_set_data(content->body_handler, data);
}
const char * linphone_content_get_string_buffer(const LinphoneContent *content) {
- return (char *)content->lcp.data;
+ return (const char *)linphone_content_get_buffer(content);
}
void linphone_content_set_string_buffer(LinphoneContent *content, const char *buffer) {
- content->lcp.size = strlen(buffer);
- content->lcp.data = belle_sip_strdup(buffer);
+ sal_body_handler_set_size(content->body_handler, strlen(buffer));
+ sal_body_handler_set_data(content->body_handler, belle_sip_strdup(buffer));
}
size_t linphone_content_get_size(const LinphoneContent *content) {
- return content->lcp.size;
+ return sal_body_handler_get_size(content->body_handler);
}
void linphone_content_set_size(LinphoneContent *content, size_t size) {
- content->lcp.size = size;
+ sal_body_handler_set_size(content->body_handler, size);
}
const char * linphone_content_get_encoding(const LinphoneContent *content) {
- return content->lcp.encoding;
+ return sal_body_handler_get_encoding(content->body_handler);
}
void linphone_content_set_encoding(LinphoneContent *content, const char *encoding) {
- if (content->lcp.encoding != NULL) {
- belle_sip_free(content->lcp.encoding);
- content->lcp.encoding = NULL;
- }
- if (encoding != NULL) {
- content->lcp.encoding = belle_sip_strdup(encoding);
- }
+ sal_body_handler_set_encoding(content->body_handler, encoding);
}
const char * linphone_content_get_name(const LinphoneContent *content) {
- return content->lcp.name;
+ return content->name;
}
void linphone_content_set_name(LinphoneContent *content, const char *name) {
- if (content->lcp.name != NULL) {
- belle_sip_free(content->lcp.name);
- content->lcp.name = NULL;
+ if (content->name != NULL) {
+ belle_sip_free(content->name);
+ content->name = NULL;
}
if (name != NULL) {
- content->lcp.name = belle_sip_strdup(name);
+ content->name = belle_sip_strdup(name);
}
}
size_t linphone_content_get_key_size(const LinphoneContent *content) {
- return content->lcp.keyLength;
+ return content->keyLength;
}
const char * linphone_content_get_key(const LinphoneContent *content) {
- return content->lcp.key;
+ return content->key;
}
void linphone_content_set_key(LinphoneContent *content, const char *key, const size_t keyLength) {
- if (content->lcp.key != NULL) {
- belle_sip_free(content->lcp.key);
- content->lcp.key = NULL;
+ if (content->key != NULL) {
+ belle_sip_free(content->key);
+ content->key = NULL;
}
if (key != NULL) {
- content->lcp.key = belle_sip_malloc(keyLength);
- memcpy(content->lcp.key, key, keyLength);
+ content->key = belle_sip_malloc(keyLength);
+ memcpy(content->key, key, keyLength);
+ content->keyLength = keyLength;
}
}
/* crypto context is managed(allocated/freed) by the encryption function, so provide the address of field in the private structure */
void ** linphone_content_get_cryptoContext_address(LinphoneContent *content) {
- return &(content->lcp.cryptoContext);
+ return &(content->cryptoContext);
+}
+
+bool_t linphone_content_is_multipart(const LinphoneContent *content) {
+ return sal_body_handler_is_multipart(content->body_handler);
+}
+
+LinphoneContent * linphone_content_get_part(const LinphoneContent *content, int idx) {
+ SalBodyHandler *part_body_handler;
+ if (!linphone_content_is_multipart(content)) return NULL;
+ part_body_handler = sal_body_handler_get_part(content->body_handler, idx);
+ return linphone_content_from_sal_body_handler(part_body_handler);
+}
+
+LinphoneContent * linphone_content_find_part_by_header(const LinphoneContent *content, const char *header_name, const char *header_value) {
+ SalBodyHandler *part_body_handler;
+ if (!linphone_content_is_multipart(content)) return NULL;
+ part_body_handler = sal_body_handler_find_part_by_header(content->body_handler, header_name, header_value);
+ return linphone_content_from_sal_body_handler(part_body_handler);
+}
+
+const char * linphone_content_get_custom_header(const LinphoneContent *content, const char *header_name) {
+ return sal_body_handler_get_header(content->body_handler, header_name);
}
LinphoneContent * linphone_content_new(void) {
- LinphoneContent *content = belle_sip_object_new(LinphoneContent);
- belle_sip_object_ref(content);
- content->owned_fields = TRUE;
- content->lcp.cryptoContext = NULL; /* this field is managed externally by encryption/decryption functions so be careful to initialise it to NULL */
- return content;
+ return linphone_content_new_with_body_handler(NULL);
}
LinphoneContent * linphone_content_copy(const LinphoneContent *ref) {
return (LinphoneContent *)belle_sip_object_ref(belle_sip_object_clone(BELLE_SIP_OBJECT(ref)));
}
-LinphoneContent * linphone_content_from_sal_body(const SalBody *ref) {
- if (ref && ref->type) {
- LinphoneContent *content = linphone_content_new();
- linphone_content_set_type(content, ref->type);
- linphone_content_set_subtype(content, ref->subtype);
- linphone_content_set_encoding(content, ref->encoding);
- if (ref->data != NULL) {
- linphone_content_set_buffer(content, ref->data, ref->size);
- } else {
- linphone_content_set_size(content, ref->size);
- }
- return content;
+LinphoneContent * linphone_content_from_sal_body_handler(SalBodyHandler *body_handler) {
+ if (body_handler) {
+ return linphone_content_new_with_body_handler(body_handler);
}
return NULL;
}
-SalBody *sal_body_from_content(SalBody *body, const LinphoneContent *content) {
- if (content && linphone_content_get_type(content)) {
- body->type = linphone_content_get_type(content);
- body->subtype = linphone_content_get_subtype(content);
- body->data = linphone_content_get_buffer(content);
- body->size = linphone_content_get_size(content);
- body->encoding = linphone_content_get_encoding(content);
- return body;
- }
- return NULL;
-}
-
-
-
-LinphoneContent * linphone_content_private_to_linphone_content(const LinphoneContentPrivate *lcp) {
- LinphoneContent *content = belle_sip_object_new(LinphoneContent);
- memcpy(&content->lcp, lcp, sizeof(LinphoneContentPrivate));
- content->owned_fields = FALSE;
- return content;
-}
-
-LinphoneContentPrivate * linphone_content_to_linphone_content_private(const LinphoneContent *content) {
- return (LinphoneContentPrivate *)&content->lcp;
+SalBodyHandler * sal_body_handler_from_content(const LinphoneContent *content) {
+ if (content == NULL) return NULL;
+ return content->body_handler;
}
diff --git a/coreapi/content.h b/coreapi/content.h
index 02ccc3833..340a815df 100644
--- a/coreapi/content.h
+++ b/coreapi/content.h
@@ -40,51 +40,6 @@ struct _LinphoneContent;
**/
typedef struct _LinphoneContent LinphoneContent;
-/**
- * @deprecated Use LinphoneContent objects instead of this structure.
- */
-struct _LinphoneContentPrivate{
- char *type; /**country!=NULL; dial_plan++) {
+ if (strncmp(dial_plan->ccc,&e164[1],i) == 0) {
+ elected_dial_plan=dial_plan;
+ found++;
+ }
+ }
+ } while ((found>1 || found==0) && i < sizeof(dial_plan->ccc));
+ if (found==1) {
+ return atoi(elected_dial_plan->ccc);
+ } else {
+ return -1; /*not found */
+ }
+
+}
+int linphone_dial_plan_lookup_ccc_from_iso(const char* iso) {
+ LinphoneDialPlan* dial_plan;
+ for (dial_plan=(LinphoneDialPlan*)dial_plans; dial_plan->country!=NULL; dial_plan++) {
+ if (strcmp(iso, dial_plan->iso_country_code)==0) {
+ return atoi(dial_plan->ccc);
+ }
+ }
+ return -1;
+}
+
+const LinphoneDialPlan* linphone_dial_plan_by_ccc(const char *ccc) {
+ int i;
+ if (!ccc) {
+ return &most_common_dialplan;
+ }
+
+ for(i=0;dial_plans[i].country!=NULL;++i){
+ if (strcmp(ccc,dial_plans[i].ccc)==0){
+ return &dial_plans[i];
+ }
+ }
+ /*else return a generic "most common" dial plan*/
+ return &most_common_dialplan;
+}
+
+const LinphoneDialPlan* linphone_dial_plan_get_all() {
+ return dial_plans;
+}
+
+bool_t linphone_dial_plan_is_generic(const LinphoneDialPlan *ccc) {
+ if (strcmp(ccc->country, most_common_dialplan.country) == 0)
+ return TRUE;
+ return FALSE;
+}
diff --git a/coreapi/dict.c b/coreapi/dict.c
index 1dae02c5a..6909ed9cb 100644
--- a/coreapi/dict.c
+++ b/coreapi/dict.c
@@ -103,7 +103,7 @@ int linphone_dictionary_haskey(const LinphoneDictionary* obj, const char* key)
void linphone_dictionary_foreach(const LinphoneDictionary* obj, void (*apply_func)(const char*, void*, void*), void* userdata)
{
- return belle_sip_dict_foreach(obj, apply_func, userdata);
+ belle_sip_dict_foreach(obj, apply_func, userdata);
}
struct lp_config_to_dict {
diff --git a/coreapi/ec-calibrator.c b/coreapi/ec-calibrator.c
index 3ac8b55ef..098fb8b4f 100644
--- a/coreapi/ec-calibrator.c
+++ b/coreapi/ec-calibrator.c
@@ -41,25 +41,25 @@ static void ecc_init_filters(EcCalibrator *ecc){
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_filter_new(MS_RESAMPLE_ID);
+ 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,&ecc_channels);
ms_filter_call_method(ecc->read_resampler,MS_FILTER_SET_OUTPUT_NCHANNELS,&channels);
- ecc->det=ms_filter_new(MS_TONE_DETECTOR_ID);
+ 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_filter_new(MS_VOID_SINK_ID);
+ 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_filter_new(MS_VOID_SOURCE_ID);
- ecc->gen=ms_filter_new(MS_DTMF_GEN_ID);
+ 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_filter_new(MS_RESAMPLE_ID);
+ 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);
@@ -114,8 +114,10 @@ static void ecc_deinit_filters(EcCalibrator *ecc){
static void on_tone_sent(void *data, MSFilter *f, unsigned int event_id, void *arg){
MSDtmfGenEvent *ev=(MSDtmfGenEvent*)arg;
EcCalibrator *ecc=(EcCalibrator*)data;
- ecc->acc-=ev->tone_start_time;
- ms_message("Sent tone at %u",(unsigned int)ev->tone_start_time);
+ 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){
@@ -159,23 +161,23 @@ static void ecc_play_tones(EcCalibrator *ecc){
/* configure the tones to be scanned */
strncpy(expected_tone.tone_name,"freq1",sizeof(expected_tone.tone_name));
- expected_tone.frequency=2000;
+ expected_tone.frequency=(int)2349.32;
expected_tone.min_duration=40;
- expected_tone.min_amplitude=0.1;
+ 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=2300;
+ expected_tone.frequency=(int)2637.02;
expected_tone.min_duration=40;
- expected_tone.min_amplitude=0.1;
+ 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=2500;
+ expected_tone.frequency=(int)2093;
expected_tone.min_duration=40;
- expected_tone.min_amplitude=0.1;
+ expected_tone.min_amplitude=0.1f;
ms_filter_call_method (ecc->det,MS_TONE_DETECTOR_ADD_SCAN,&expected_tone);
@@ -192,23 +194,63 @@ static void ecc_play_tones(EcCalibrator *ecc){
/* play the three tones*/
- tone.frequencies[0]=2000;
- tone.duration=100;
- ms_filter_call_method(ecc->gen,MS_DTMF_GEN_PLAY_CUSTOM,&tone);
- ms_usleep(300000);
- tone.frequencies[0]=2300;
- tone.duration=100;
- ms_filter_call_method(ecc->gen,MS_DTMF_GEN_PLAY_CUSTOM,&tone);
- ms_usleep(300000);
+ 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);
+ }
- tone.frequencies[0]=2500;
- tone.duration=100;
- ms_filter_call_method(ecc->gen,MS_DTMF_GEN_PLAY_CUSTOM,&tone);
ms_sleep(1);
if (ecc->freq1 && ecc->freq2 && ecc->freq3) {
- int delay=ecc->acc/3;
+ int delay=(int)(ecc->acc/3);
if (delay<0){
ms_error("Quite surprising calibration result, delay=%i",delay);
ecc->status=LinphoneEcCalibratorFailed;
@@ -239,7 +281,7 @@ static void * ecc_thread(void *p){
return NULL;
}
-EcCalibrator * ec_calibrator_new(MSSndCard *play_card, MSSndCard *capt_card, unsigned int rate, LinphoneEcCalibrationCallback cb,
+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);
@@ -250,16 +292,20 @@ EcCalibrator * ec_calibrator_new(MSSndCard *play_card, MSSndCard *capt_card, uns
ecc->audio_uninit_cb=audio_uninit_cb;
ecc->capt_card=capt_card;
ecc->play_card=play_card;
- ms_thread_create(&ecc->thread,NULL,ecc_thread,ecc);
+ 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){
- ms_thread_join(ecc->thread,NULL);
+ if (ecc->thread != 0) ms_thread_join(ecc->thread,NULL);
ms_free(ecc);
}
@@ -272,7 +318,9 @@ int linphone_core_start_echo_calibration(LinphoneCore *lc, LinphoneEcCalibration
return -1;
}
rate = lp_config_get_int(lc->config,"sound","echo_cancellation_rate",8000);
- lc->ecc=ec_calibrator_new(lc->sound_conf.play_sndcard,lc->sound_conf.capt_sndcard,rate,cb,audio_init_cb,audio_uninit_cb,cb_data);
+ 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;
}
diff --git a/coreapi/echo-tester.c b/coreapi/echo-tester.c
new file mode 100644
index 000000000..5740c7302
--- /dev/null
+++ b/coreapi/echo-tester.c
@@ -0,0 +1,104 @@
+/*
+linphone
+Copyright (C) 2016 Belledonne Communications SARL
+Author: Erwan CROZE (erwan.croze@belledonne-communications.com)
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 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 "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={0};
+ 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);
+}
+
+int 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;
+}
+
+int 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;
+}
\ No newline at end of file
diff --git a/coreapi/enum.c b/coreapi/enum.c
index e917e0383..2c6857d1d 100644
--- a/coreapi/enum.c
+++ b/coreapi/enum.c
@@ -31,10 +31,10 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
static char *create_enum_domain(const char *number){
- int len=strlen(number);
+ long len=(long)strlen(number);
char *domain=ms_malloc((len*2)+10);
- int i,j;
-
+ long i,j;
+
for (i=0,j=len-1;j>=0;j--){
domain[i]=number[j];
i++;
@@ -105,11 +105,11 @@ int enum_lookup(const char *enum_domain, enum_lookup_res_t **res){
/*
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));
@@ -117,7 +117,7 @@ int enum_lookup(const char *enum_domain, enum_lookup_res_t **res){
}
ns_initparse(dns_answer,DNS_ANSWER_MAX_SIZE,&handle);
count=ns_msg_count(handle,ns_s_an);
-
+
for(i=0;iop) {
+ /*this will stop the refresher*/
+ sal_op_stop_refreshing(lev->op);
+ }
+ linphone_event_unref(lev);
+}
+
static LinphoneEvent * linphone_event_new_base(LinphoneCore *lc, LinphoneSubscriptionDir dir, const char *name, SalOp *op){
- LinphoneEvent *lev=ms_new0(LinphoneEvent,1);
+ LinphoneEvent *lev=belle_sip_object_new(LinphoneEvent);
lev->lc=lc;
lev->dir=dir;
lev->op=op;
- lev->refcnt=1;
lev->name=ms_strdup(name);
sal_op_set_user_pointer(lev->op,lev);
return lev;
@@ -88,13 +106,21 @@ LinphoneEvent *linphone_event_new_with_out_of_dialog_op(LinphoneCore *lc, SalOp
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){
- linphone_event_unref(lev);
+ if (state==LinphoneSubscriptionTerminated || state == LinphoneSubscriptionError){
+ linphone_event_release(lev);
}
}
}
@@ -106,13 +132,13 @@ void linphone_event_set_publish_state(LinphoneEvent *lev, LinphonePublishState s
linphone_core_notify_publish_state_changed(lev->lc,lev,state);
switch(state){
case LinphonePublishCleared:
- if (lev->expires!=-1)
- linphone_event_unref(lev);
+ linphone_event_release(lev);
break;
case LinphonePublishOk:
+ if (lev->oneshot) linphone_event_release(lev);
+ break;
case LinphonePublishError:
- if (lev->expires==-1)
- linphone_event_unref(lev);
+ linphone_event_release(lev);
break;
case LinphonePublishNone:
case LinphonePublishProgress:
@@ -143,15 +169,23 @@ LinphoneEvent *linphone_core_create_subscribe(LinphoneCore *lc, const LinphoneAd
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;
+ sal_op_set_event(lev->op, 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;
}
-
int linphone_event_send_subscribe(LinphoneEvent *lev, const LinphoneContent *body){
- SalBody salbody;
+ SalBodyHandler *body_handler;
int err;
if (lev->dir!=LinphoneSubscriptionOutgoing){
@@ -161,7 +195,7 @@ int linphone_event_send_subscribe(LinphoneEvent *lev, const LinphoneContent *bod
switch (lev->subscription_state){
case LinphoneSubscriptionIncomingReceived:
case LinphoneSubscriptionTerminated:
- case LinphoneSubscriptionOutgoingInit:
+ 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;
@@ -176,13 +210,15 @@ int linphone_event_send_subscribe(LinphoneEvent *lev, const LinphoneContent *bod
if (lev->send_custom_headers){
sal_op_set_sent_custom_header(lev->op,lev->send_custom_headers);
+ sal_custom_header_free(lev->send_custom_headers);
lev->send_custom_headers=NULL;
}else sal_op_set_sent_custom_header(lev->op,NULL);
- err=sal_subscribe(lev->op,NULL,NULL,lev->name,lev->expires,sal_body_from_content(&salbody,body));
+ body_handler = sal_body_handler_from_content(body);
+ err=sal_subscribe(lev->op,NULL,NULL,lev->name,lev->expires,body_handler);
if (err==0){
if (lev->subscription_state==LinphoneSubscriptionNone)
- linphone_event_set_state(lev,LinphoneSubscriptionOutgoingInit);
+ linphone_event_set_state(lev,LinphoneSubscriptionOutgoingProgress);
}
return err;
}
@@ -191,6 +227,10 @@ int linphone_event_update_subscribe(LinphoneEvent *lev, const LinphoneContent *b
return linphone_event_send_subscribe(lev,body);
}
+int linphone_event_refresh_subscribe(LinphoneEvent *lev) {
+ return sal_op_refresh(lev->op);
+}
+
int linphone_event_accept_subscription(LinphoneEvent *lev){
int err;
if (lev->subscription_state!=LinphoneSubscriptionIncomingReceived){
@@ -216,8 +256,8 @@ int linphone_event_deny_subscription(LinphoneEvent *lev, LinphoneReason reason){
}
int linphone_event_notify(LinphoneEvent *lev, const LinphoneContent *body){
- SalBody salbody;
- if (lev->subscription_state!=LinphoneSubscriptionActive){
+ 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;
}
@@ -225,7 +265,8 @@ int linphone_event_notify(LinphoneEvent *lev, const LinphoneContent *body){
ms_error("linphone_event_notify(): cannot notify if not an incoming subscription.");
return -1;
}
- return sal_notify(lev->op,sal_body_from_content(&salbody,body));
+ body_handler = sal_body_handler_from_content(body);
+ return sal_notify(lev->op, body_handler);
}
LinphoneEvent *linphone_core_create_publish(LinphoneCore *lc, const LinphoneAddress *resource, const char *event, int expires){
@@ -235,8 +276,14 @@ LinphoneEvent *linphone_core_create_publish(LinphoneCore *lc, const LinphoneAddr
return lev;
}
+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){
- SalBody salbody;
+ SalBodyHandler *body_handler;
int err;
if (lev->dir!=LinphoneSubscriptionInvalidDir){
@@ -245,9 +292,11 @@ static int _linphone_event_send_publish(LinphoneEvent *lev, const LinphoneConten
}
if (lev->send_custom_headers){
sal_op_set_sent_custom_header(lev->op,lev->send_custom_headers);
+ sal_custom_header_free(lev->send_custom_headers);
lev->send_custom_headers=NULL;
}else sal_op_set_sent_custom_header(lev->op,NULL);
- err=sal_publish(lev->op,NULL,NULL,lev->name,lev->expires,sal_body_from_content(&salbody,body));
+ body_handler = sal_body_handler_from_content(body);
+ err=sal_publish(lev->op,NULL,NULL,lev->name,lev->expires,body_handler);
if (err==0){
linphone_event_set_publish_state(lev,LinphonePublishProgress);
}else if (notify_err){
@@ -276,6 +325,16 @@ int linphone_event_update_publish(LinphoneEvent* lev, const LinphoneContent* bod
return linphone_event_send_publish(lev,body);
}
+int linphone_event_refresh_publish(LinphoneEvent *lev) {
+ return sal_op_refresh(lev->op);
+}
+void linphone_event_pause_publish(LinphoneEvent *lev) {
+ if (lev->op) sal_op_stop_refreshing(lev->op);
+}
+void linphone_event_unpublish(LinphoneEvent *lev) {
+ lev->terminating = TRUE; /* needed to get clear event*/
+ if (lev->op) sal_op_unpublish(lev->op);
+}
void linphone_event_set_user_data(LinphoneEvent *ev, void *up){
ev->userdata=up;
}
@@ -295,7 +354,17 @@ const char* linphone_event_get_custom_header(LinphoneEvent* ev, const char* 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){
sal_notify_close(lev->op);
}else if (lev->dir==LinphoneSubscriptionOutgoing){
@@ -304,8 +373,8 @@ void linphone_event_terminate(LinphoneEvent *lev){
if (lev->publish_state!=LinphonePublishNone){
if (lev->publish_state==LinphonePublishOk && lev->expires!=-1){
- sal_publish(lev->op,NULL,NULL,NULL,0,NULL);
- }else sal_op_stop_refreshing(lev->op);
+ sal_op_unpublish(lev->op);
+ }
linphone_event_set_publish_state(lev,LinphonePublishCleared);
return;
}
@@ -318,20 +387,18 @@ void linphone_event_terminate(LinphoneEvent *lev){
LinphoneEvent *linphone_event_ref(LinphoneEvent *lev){
- lev->refcnt++;
+ belle_sip_object_ref(lev);
return lev;
}
static void linphone_event_destroy(LinphoneEvent *lev){
- if (lev->op)
- sal_op_release(lev->op);
+ if (lev->op) sal_op_release(lev->op);
+ if (lev->send_custom_headers) sal_custom_header_free(lev->send_custom_headers);
ms_free(lev->name);
- ms_free(lev);
}
void linphone_event_unref(LinphoneEvent *lev){
- lev->refcnt--;
- if (lev->refcnt==0) linphone_event_destroy(lev);
+ belle_sip_object_unref(lev);
}
LinphoneSubscriptionDir linphone_event_get_subscription_dir(LinphoneEvent *lev){
@@ -347,7 +414,7 @@ const char *linphone_event_get_name(const LinphoneEvent *lev){
}
const LinphoneAddress *linphone_event_get_from(const LinphoneEvent *lev){
- if (lev->is_out_of_dialog_op){
+ if (lev->is_out_of_dialog_op && lev->dir == LinphoneSubscriptionOutgoing){
return (LinphoneAddress*)sal_op_get_to_address(lev->op);
}else{
return (LinphoneAddress*)sal_op_get_from_address(lev->op);
@@ -355,7 +422,7 @@ const LinphoneAddress *linphone_event_get_from(const LinphoneEvent *lev){
}
const LinphoneAddress *linphone_event_get_resource(const LinphoneEvent *lev){
- if (lev->is_out_of_dialog_op){
+ if (lev->is_out_of_dialog_op && lev->dir == LinphoneSubscriptionOutgoing){
return (LinphoneAddress*)sal_op_get_from_address(lev->op);
}else{
return (LinphoneAddress*)sal_op_get_to_address(lev->op);
@@ -366,3 +433,22 @@ 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;
+}
+
+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
+);
+
diff --git a/coreapi/event.h b/coreapi/event.h
index 07332ca2e..47db9cc79 100644
--- a/coreapi/event.h
+++ b/coreapi/event.h
@@ -49,6 +49,7 @@ typedef enum _LinphoneSubscriptionDir LinphoneSubscriptionDir;
/**
* Enum for subscription states.
+ * LinphoneSubscriptionTerminated and LinphoneSubscriptionError are final states.
**/
enum _LinphoneSubscriptionState{
LinphoneSubscriptionNone, /**< Initial state, should not be used.**/
@@ -57,13 +58,10 @@ enum _LinphoneSubscriptionState{
LinphoneSubscriptionPending, /**refresh_generic_subscribe property is set to 0.*/
};
-/*typo compatibility*/
-#define LinphoneSubscriptionOutoingInit LinphoneSubscriptionOutgoingInit
-#define LinphoneSubscriptionOutgoingInit LinphoneSubscriptionOutgoingProgress
/**
* Typedef for subscription state enum.
**/
@@ -129,6 +127,18 @@ LINPHONE_PUBLIC LinphoneEvent *linphone_core_subscribe(LinphoneCore *lc, const L
**/
LINPHONE_PUBLIC LinphoneEvent *linphone_core_create_subscribe(LinphoneCore *lc, const LinphoneAddress *resource, const char *event, int expires);
+
+/**
+ * Create an out-of-dialog notification, specifying the destination resource, the event name.
+ * The notification can be send with linphone_event_notify().
+ * @param lc the #LinphoneCore
+ * @param resource the destination resource
+ * @param event the event name
+ * @return a LinphoneEvent holding the context of the notification.
+**/
+LINPHONE_PUBLIC LinphoneEvent *linphone_core_create_notify(LinphoneCore *lc, const LinphoneAddress *resource, const char *event);
+
+
/**
* Send a subscription previously created by linphone_core_create_subscribe().
* @param ev the LinphoneEvent
@@ -138,12 +148,19 @@ LINPHONE_PUBLIC LinphoneEvent *linphone_core_create_subscribe(LinphoneCore *lc,
LINPHONE_PUBLIC int linphone_event_send_subscribe(LinphoneEvent *ev, const LinphoneContent *body);
/**
- * Update (refresh) an outgoing subscription.
+ * Update (refresh) an outgoing subscription, changing the body.
* @param lev a LinphoneEvent
* @param body an optional body to include in the subscription update, may be NULL.
**/
LINPHONE_PUBLIC int linphone_event_update_subscribe(LinphoneEvent *lev, const LinphoneContent *body);
+/**
+ * Refresh an outgoing subscription keeping the same body.
+ * @param lev LinphoneEvent object.
+ * @return 0 if successful, -1 otherwise.
+ */
+LINPHONE_PUBLIC int linphone_event_refresh_subscribe(LinphoneEvent *lev);
+
/**
* Accept an incoming subcription.
@@ -187,6 +204,19 @@ LINPHONE_PUBLIC LinphoneEvent *linphone_core_publish(LinphoneCore *lc, const Lin
**/
LINPHONE_PUBLIC LinphoneEvent *linphone_core_create_publish(LinphoneCore *lc, const LinphoneAddress *resource, const char *event, int expires);
+
+/**
+ * Create a publish context for a one-shot publish.
+ * After being created, the publish must be sent using linphone_event_send_publish().
+ * The LinphoneEvent is automatically terminated when the publish transaction is finished, either with success or failure.
+ * The application must not call linphone_event_terminate() for such one-shot publish.
+ * @param lc the #LinphoneCore
+ * @param resource the resource uri for the event
+ * @param event the event name
+ * @return the LinphoneEvent holding the context of the publish.
+**/
+LINPHONE_PUBLIC LinphoneEvent *linphone_core_create_one_shot_publish(LinphoneCore *lc, const LinphoneAddress *resource, const char *event);
+
/**
* Send a publish created by linphone_core_create_publish().
* @param lev the #LinphoneEvent
@@ -201,6 +231,21 @@ LINPHONE_PUBLIC int linphone_event_send_publish(LinphoneEvent *lev, const Linpho
**/
LINPHONE_PUBLIC int linphone_event_update_publish(LinphoneEvent *lev, const LinphoneContent *body);
+/**
+ * Refresh an outgoing publish keeping the same body.
+ * @param lev LinphoneEvent object.
+ * @return 0 if successful, -1 otherwise.
+ */
+LINPHONE_PUBLIC int linphone_event_refresh_publish(LinphoneEvent *lev);
+
+/**
+ * Prevent an event from refreshing its publish.
+ * This is useful to let registrations to expire naturally (or) when the application wants to keep control on when
+ * refreshes are sent.
+ * The refreshing operations can be resumed with linphone_proxy_config_refresh_register().
+ * @param[in] lev #LinphoneEvent object.
+ **/
+LINPHONE_PUBLIC void linphone_event_pause_publish(LinphoneEvent *lev);
/**
* Return reason code (in case of error state reached).
@@ -256,10 +301,8 @@ LINPHONE_PUBLIC const char *linphone_event_get_custom_header(LinphoneEvent *ev,
/**
* Terminate an incoming or outgoing subscription that was previously acccepted, or a previous publication.
- * This function does not unref the object. The core will unref() if it does not need this object anymore.
- *
- * For subscribed event, when the subscription is terminated normally or because of an error, the core will unref.
- * For published events, no unref is performed. This is because it is allowed to re-publish an expired publish, as well as retry it in case of error.
+ * The LinphoneEvent shall not be used anymore after this operation, unless the application explicitely took a reference on the object with
+ * linphone_event_ref().
**/
LINPHONE_PUBLIC void linphone_event_terminate(LinphoneEvent *lev);
diff --git a/coreapi/friend.c b/coreapi/friend.c
index 5dd49bec3..16bf62912 100644
--- a/coreapi/friend.c
+++ b/coreapi/friend.c
@@ -26,6 +26,21 @@
#include "private.h"
#include "lpconfig.h"
+#ifdef SQLITE_STORAGE_ENABLED
+#ifndef _WIN32
+#if !defined(ANDROID) && !defined(__QNXNTO__)
+# include
+# include
+# include
+#endif
+#else
+#include
+#endif
+
+#define MAX_PATH_SIZE 1024
+#include "sqlite3.h"
+#endif
+
const char *linphone_online_status_to_string(LinphoneOnlineStatus ss){
const char *str=NULL;
switch(ss){
@@ -70,71 +85,110 @@ const char *linphone_online_status_to_string(LinphoneOnlineStatus ss){
return str;
}
-static int friend_compare(const void * a, const void * b){
- LinphoneAddress *fa=((LinphoneFriend*)a)->uri;
- LinphoneAddress *fb=((LinphoneFriend*)b)->uri;
- if (linphone_address_weak_equal(fa,fb)) return 0;
- return 1;
+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 = lf->presence_models;
+ while (iterator) {
+ LinphoneFriendPresence *lfp = (LinphoneFriendPresence *)bctbx_list_get_data(iterator);
+ if (strcmp(lfp->uri_or_tel, uri_or_tel) == 0) return lfp;
+ iterator = bctbx_list_next(iterator);
+ }
+ return NULL;
+}
+
+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 = 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);
}
-MSList *linphone_find_friend_by_address(MSList *fl, const LinphoneAddress *addr, LinphoneFriend **lf){
- MSList *res=NULL;
+
+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=ms_list_find_custom(fl,friend_compare,&dummy);
- if (lf!=NULL && res!=NULL) *lf=(LinphoneFriend*)res->data;
+ res=bctbx_list_find_custom(fl,friend_compare,&dummy);
+ if (lf!=NULL && res!=NULL) *lf=(LinphoneFriend*)bctbx_list_get_data(res);
return res;
}
-LinphoneFriend *linphone_find_friend_by_inc_subscribe(MSList *l, SalOp *op){
- MSList *elem;
- for (elem=l;elem!=NULL;elem=elem->next){
- LinphoneFriend *lf=(LinphoneFriend*)elem->data;
- if (ms_list_find(lf->insubs, op)) return lf;
- }
- return NULL;
-}
-
-LinphoneFriend *linphone_find_friend_by_out_subscribe(MSList *l, SalOp *op){
- MSList *elem;
- LinphoneFriend *lf;
- for (elem=l;elem!=NULL;elem=elem->next){
- lf=(LinphoneFriend*)elem->data;
- if (lf->outsub==op) return lf;
- }
- return NULL;
-}
-
void __linphone_friend_do_subscribe(LinphoneFriend *fr){
LinphoneCore *lc=fr->lc;
+ const LinphoneAddress *addr = linphone_friend_get_address(fr);
- if (fr->outsub==NULL){
- /* people for which we don't have yet an answer should appear as offline */
- fr->presence=NULL;
- /*
- if (fr->lc->vtable.notify_recv)
- fr->lc->vtable.notify_recv(fr->lc,(LinphoneFriend*)fr);
- */
- }else{
- sal_op_release(fr->outsub);
- fr->outsub=NULL;
+ 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{
+ sal_op_release(fr->outsub);
+ fr->outsub=NULL;
+ }
+ fr->outsub=sal_op_new(lc->sal);
+ linphone_configure_op(lc,fr->outsub,addr,NULL,TRUE);
+ sal_subscribe_presence(fr->outsub,NULL,NULL,lp_config_get_int(lc->config,"sip","subscribe_expires",600));
+ fr->subscribe_active=TRUE;
}
- fr->outsub=sal_op_new(lc->sal);
- linphone_configure_op(lc,fr->outsub,fr->uri,NULL,TRUE);
- sal_subscribe_presence(fr->outsub,NULL,NULL,lp_config_get_int(lc->config,"sip","subscribe_expires",600));
- fr->subscribe_active=TRUE;
}
-LinphoneFriend * linphone_friend_new(){
- LinphoneFriend *obj=belle_sip_object_new(LinphoneFriend);
- obj->pol=LinphoneSPAccept;
- obj->presence=NULL;
- obj->subscribe=TRUE;
+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;
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;
@@ -145,9 +199,12 @@ LinphoneFriend *linphone_friend_new_with_address(const char *addr){
}
fr=linphone_friend_new();
linphone_friend_set_address(fr,linphone_address);
- linphone_address_destroy(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;
@@ -157,8 +214,8 @@ void* linphone_friend_get_user_data(const LinphoneFriend *lf){
return lf->user_data;
}
-bool_t linphone_friend_in_list(const LinphoneFriend *lf){
- return lf->lc!=NULL;
+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){
@@ -182,7 +239,7 @@ void linphone_core_interpret_friend_uri(LinphoneCore *lc, const char *uri, char
linphone_address_set_display_name(id,NULL);
linphone_address_set_username(id,uri);
*result=linphone_address_as_string(id);
- linphone_address_destroy(id);
+ linphone_address_unref(id);
}
}
if (*result){
@@ -193,25 +250,129 @@ void linphone_core_interpret_friend_uri(LinphoneCore *lc, const char *uri, char
}
}else {
*result=linphone_address_as_string(fr);
- linphone_address_destroy(fr);
+ linphone_address_unref(fr);
}
}
-int linphone_friend_set_address(LinphoneFriend *lf, const LinphoneAddress *addr){
- LinphoneAddress *fr=linphone_address_clone(addr);
+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;
+}
+
+int linphone_friend_set_address(LinphoneFriend *lf, const LinphoneAddress *addr) {
+ LinphoneAddress *fr = linphone_address_clone(addr);
+
linphone_address_clean(fr);
- if (lf->uri!=NULL) linphone_address_destroy(lf->uri);
- lf->uri=fr;
+ if (linphone_core_vcard_supported()) {
+ char *address;
+ 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);
+ }
+ address = linphone_address_as_string_uri_only(fr);
+ linphone_vcard_edit_main_sip_address(lf->vcard, address);
+ ms_free(address);
+ linphone_address_unref(fr);
+ } else {
+ if (lf->uri != NULL) linphone_address_unref(lf->uri);
+ lf->uri = fr;
+ }
+
return 0;
}
-int linphone_friend_set_name(LinphoneFriend *lf, const char *name){
- LinphoneAddress *fr=lf->uri;
- if (fr==NULL){
- ms_error("linphone_friend_set_sip_addr() must be called before linphone_friend_set_name().");
- return -1;
+void linphone_friend_add_address(LinphoneFriend *lf, const LinphoneAddress *addr) {
+ LinphoneAddress *fr;
+
+ if (!lf || !addr) return;
+
+ fr = linphone_address_clone(addr);
+ linphone_address_clean(fr);
+
+ if (linphone_core_vcard_supported()) {
+ if (lf->vcard) {
+ char *address = linphone_address_as_string_uri_only(fr);
+ linphone_vcard_add_sip_address(lf->vcard, address);
+ ms_free(address);
+ linphone_address_unref(fr);
+ }
+ } else {
+ if (lf->uri == NULL) lf->uri = fr;
+ else linphone_address_unref(fr);
+ }
+}
+
+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) {
+ if (!lf || !addr || !lf->vcard) return;
+
+ if (linphone_core_vcard_supported()) {
+ char *address = linphone_address_as_string_uri_only(addr);
+ 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 (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(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 (linphone_core_vcard_supported()) {
+ linphone_vcard_remove_phone_number(lf->vcard, phone);
+ }
+}
+
+int 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);
}
- linphone_address_set_display_name(fr,name);
return 0;
}
@@ -220,81 +381,116 @@ int linphone_friend_enable_subscribes(LinphoneFriend *fr, bool_t val){
return 0;
}
-int linphone_friend_set_inc_subscribe_policy(LinphoneFriend *fr, LinphoneSubscribePolicy pol)
-{
+int linphone_friend_set_inc_subscribe_policy(LinphoneFriend *fr, LinphoneSubscribePolicy pol) {
fr->pol=pol;
return 0;
}
void linphone_friend_notify(LinphoneFriend *lf, LinphonePresenceModel *presence){
- MSList *elem;
+ bctbx_list_t *elem;
if (lf->insubs){
- char *addr=linphone_address_as_string(linphone_friend_get_address(lf));
- ms_message("Want to notify %s",addr);
- ms_free(addr);
+ 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=elem->next){
- SalOp *op = (SalOp*)elem->data;
+ for(elem=lf->insubs; elem!=NULL; elem=bctbx_list_next(elem)){
+ SalOp *op = (SalOp*)bctbx_list_get_data(elem);
sal_notify_presence(op,(SalPresenceModel *)presence);
}
}
void linphone_friend_add_incoming_subscription(LinphoneFriend *lf, SalOp *op){
- lf->insubs = ms_list_append(lf->insubs, 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){
- lf->insubs = ms_list_remove(lf->insubs, op);
+ if (bctbx_list_find(lf->insubs, op)){
+ sal_op_release(op);
+ lf->insubs = bctbx_list_remove(lf->insubs, op);
+ }
}
static void linphone_friend_unsubscribe(LinphoneFriend *lf){
if (lf->outsub!=NULL) {
sal_unsubscribe(lf->outsub);
- lf->subscribe_active=FALSE;
}
+ /* for friend list there is no necessary outsub*/
+ lf->subscribe_active=FALSE;
}
-static void linphone_friend_invalidate_subscription(LinphoneFriend *lf){
+void linphone_friend_invalidate_subscription(LinphoneFriend *lf){
+ bctbx_list_t *iterator;
+ LinphoneCore *lc=lf->lc;
+
if (lf->outsub!=NULL) {
- LinphoneCore *lc=lf->lc;
sal_op_release(lf->outsub);
lf->outsub=NULL;
lf->subscribe_active=FALSE;
- /*notify application that we no longer know the presence activity */
- if (lf->presence != NULL) {
- linphone_presence_model_unref(lf->presence);
- }
- lf->presence = linphone_presence_model_new_with_activity(LinphonePresenceActivityOffline,"unknown activity");
- linphone_core_notify_notify_presence_received(lc,lf);
}
+
+ /* 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_with_activity(LinphonePresenceActivityOffline, "unknown activity");
+ 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)
+ linphone_core_notify_notify_presence_received(lc, lf);
+
lf->initial_subscribes_sent=FALSE;
}
void linphone_friend_close_subscriptions(LinphoneFriend *lf){
linphone_friend_unsubscribe(lf);
- ms_list_for_each(lf->insubs, (MSIterateFunc) sal_notify_presence_close);
-
+ bctbx_list_for_each(lf->insubs, (MSIterateFunc) sal_notify_presence_close);
+ lf->insubs = bctbx_list_free_with_data(lf->insubs, (MSIterateFunc)sal_op_release);
}
-static void _linphone_friend_destroy(LinphoneFriend *lf){
- lf->insubs = ms_list_free_with_data(lf->insubs, (MSIterateFunc) sal_op_release);
+static void _linphone_friend_release_ops(LinphoneFriend *lf){
+ lf->insubs = bctbx_list_free_with_data(lf->insubs, (MSIterateFunc) sal_op_release);
if (lf->outsub){
sal_op_release(lf->outsub);
lf->outsub=NULL;
}
- if (lf->presence != NULL) linphone_presence_model_unref(lf->presence);
- if (lf->uri!=NULL) linphone_address_destroy(lf->uri);
- if (lf->info!=NULL) buddy_info_free(lf->info);
}
-const LinphoneAddress *linphone_friend_get_address(const LinphoneFriend *lf){
- return lf->uri;
+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_free(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) {
- LinphoneAddress *fr = lf->uri;
- if (fr == NULL) return NULL;
- return linphone_address_get_display_name(fr);
+ if (!lf) return NULL;
+
+ if (linphone_core_vcard_supported()) {
+ if (lf->vcard) return linphone_vcard_get_full_name(lf->vcard);
+ } else if (lf->uri) {
+ return linphone_address_get_display_name(lf->uri);
+ }
+ return NULL;
}
bool_t linphone_friend_get_send_subscribe(const LinphoneFriend *lf){
@@ -306,25 +502,30 @@ LinphoneSubscribePolicy linphone_friend_get_inc_subscribe_policy(const LinphoneF
}
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 (lf->presence != NULL) {
- basic_status = linphone_presence_model_get_basic_status(lf->presence);
- nb_activities = linphone_presence_model_get_nb_activities(lf->presence);
+ 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);
+ if (tmp) {
+ ms_free(tmp);
+ }
nb_activities = 1;
}
if (nb_activities == 1) {
- activity = linphone_presence_model_get_activity(lf->presence);
+ 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:
@@ -351,6 +552,12 @@ LinphoneOnlineStatus linphone_friend_get_status(const LinphoneFriend *lf){
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:
@@ -373,7 +580,7 @@ LinphoneOnlineStatus linphone_friend_get_status(const LinphoneFriend *lf){
break;
case LinphonePresenceActivityOnline:
/* Should not happen! */
- ms_warning("LinphonePresenceActivityOnline should not happen here!");
+ /*ms_warning("LinphonePresenceActivityOnline should not happen here!");*/
break;
case LinphonePresenceActivityOffline:
online_status = LinphoneStatusOffline;
@@ -385,8 +592,58 @@ LinphoneOnlineStatus linphone_friend_get_status(const LinphoneFriend *lf){
return online_status;
}
-const LinphonePresenceModel * linphone_friend_get_presence_model(LinphoneFriend *lf) {
- return lf->presence;
+const LinphonePresenceModel * linphone_friend_get_presence_model(const LinphoneFriend *lf) {
+ const LinphonePresenceModel *presence = NULL;
+ LinphoneFriend* fuckconst = (LinphoneFriend*)lf;
+ const bctbx_list_t* addrs = linphone_friend_get_addresses(fuckconst);
+ 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(fuckconst, uri);
+ ms_free(uri);
+ if (presence) break;
+ }
+ if (presence) return presence;
+
+ phones = linphone_friend_get_phone_numbers(fuckconst);
+ for (it = phones; it!= NULL; it = it->next) {
+ presence = linphone_friend_get_presence_model_for_uri_or_tel(fuckconst, it->data);
+ if (presence) break;
+ }
+ bctbx_list_free(phones);
+ return presence;
+}
+
+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 = 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){
@@ -394,23 +651,25 @@ BuddyInfo * linphone_friend_get_info(const LinphoneFriend *lf){
}
/*
- * updates the subscriptions.
+ * 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, LinphoneProxyConfig *proxy, bool_t only_when_registered){
+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)){
- LinphoneProxyConfig *cfg=linphone_core_lookup_known_proxy(fr->lc,fr->uri);
- if (proxy && proxy!=cfg) return;
- if (cfg && cfg->state!=LinphoneRegistrationOk){
- char *tmp=linphone_address_as_string(fr->uri);
- 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;
+ 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){
@@ -424,92 +683,128 @@ void linphone_friend_update_subscribes(LinphoneFriend *fr, LinphoneProxyConfig *
}
}
-void linphone_friend_apply(LinphoneFriend *fr, LinphoneCore *lc){
- LinphonePresenceModel *model;
+void linphone_friend_save(LinphoneFriend *fr, LinphoneCore *lc) {
+ if (!lc) return;
+#ifdef SQLITE_STORAGE_ENABLED
+ if (lc->friends_db_file) {
+ linphone_core_store_friend_in_db(lc, fr);
+ } else {
+ linphone_core_write_friends_config(lc);
+ }
+#else
+ linphone_core_write_friends_config(lc);
+#endif
+}
- if (fr->uri==NULL) {
- ms_warning("No sip url defined.");
+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;
}
- linphone_core_write_friends_config(lc);
-
- if (fr->inc_subscribe_pending){
- switch(fr->pol){
+ 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_friend_notify(fr, model);
linphone_presence_model_unref(model);
break;
case LinphoneSPAccept:
- if (fr->lc!=NULL)
- linphone_friend_notify(fr,fr->lc->presence_model);
+ if (fr->lc)
+ linphone_friend_notify(fr, fr->lc->presence_model);
break;
case LinphoneSPDeny:
- linphone_friend_notify(fr,NULL);
+ linphone_friend_notify(fr, NULL);
break;
}
- fr->inc_subscribe_pending=FALSE;
+ fr->inc_subscribe_pending = FALSE;
}
- if (fr->lc)
- linphone_friend_update_subscribes(fr,NULL,linphone_core_should_subscribe_friends_only_when_registered(fr->lc));
- ms_message("linphone_friend_apply() done.");
+
+ 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){
+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!=NULL);
- if (fr->lc==NULL) return;
- linphone_friend_apply(fr,fr->lc);
+void linphone_friend_done(LinphoneFriend *fr) {
+ ms_return_if_fail(fr);
+ if (!fr->lc) return;
+ linphone_friend_apply(fr, fr->lc);
+ linphone_friend_save(fr, fr->lc);
+
+ 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));
+ }
+ }
+ }
}
+#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) {
- return linphone_friend_new();
+ LinphoneFriend * lf = linphone_friend_new();
+ lf->lc = lc;
+ return lf;
}
LinphoneFriend * linphone_core_create_friend_with_address(LinphoneCore *lc, const char *address) {
- return linphone_friend_new_with_address(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)
-{
- ms_return_if_fail(lf->lc==NULL);
- ms_return_if_fail(lf->uri!=NULL);
- if (ms_list_find(lc->friends,lf)!=NULL){
- char *tmp=NULL;
- const LinphoneAddress *addr=linphone_friend_get_address(lf);
- if (addr) tmp=linphone_address_as_string(addr);
- ms_warning("Friend %s already in list, ignored.", tmp ? tmp : "unknown");
- if (tmp) ms_free(tmp);
- return ;
- }
- lc->friends=ms_list_append(lc->friends,linphone_friend_ref(lf));
- lf->lc=lc;
- if ( linphone_core_ready(lc)) linphone_friend_apply(lf,lc);
- else lf->commit=TRUE;
- return ;
-}
-
-void linphone_core_remove_friend(LinphoneCore *lc, LinphoneFriend* fl){
- MSList *el=ms_list_find(lc->friends,fl);
- if (el!=NULL){
- linphone_friend_destroy((LinphoneFriend*)el->data);
- lc->friends=ms_list_remove_link(lc->friends,el);
- linphone_core_write_friends_config(lc);
- }else{
- ms_error("linphone_core_remove_friend(): friend [%p] is not part of core's list.",fl);
+void linphone_core_add_friend(LinphoneCore *lc, LinphoneFriend *lf) {
+ if (linphone_friend_list_add_friend(linphone_core_get_default_friend_list(lc), 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_update_friends_subscriptions(LinphoneCore *lc, LinphoneProxyConfig *cfg, bool_t only_when_registered){
- const MSList *elem;
- for(elem=lc->friends;elem!=NULL;elem=elem->next){
- LinphoneFriend *f=(LinphoneFriend*)elem->data;
- linphone_friend_update_subscribes(f,cfg,only_when_registered);
+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);
}
}
@@ -517,65 +812,94 @@ bool_t linphone_core_should_subscribe_friends_only_when_registered(const Linphon
return lp_config_get_int(lc->config,"sip","subscribe_presence_only_when_registered",1);
}
-void linphone_core_send_initial_subscribes(LinphoneCore *lc){
+void linphone_core_send_initial_subscribes(LinphoneCore *lc) {
+
if (lc->initial_subscribes_sent) return;
lc->initial_subscribes_sent=TRUE;
- linphone_core_update_friends_subscriptions(lc,NULL,linphone_core_should_subscribe_friends_only_when_registered(lc));
+
+ linphone_core_update_friends_subscriptions(lc);
}
-void linphone_core_invalidate_friend_subscriptions(LinphoneCore *lc){
- const MSList *elem;
- for(elem=lc->friends;elem!=NULL;elem=elem->next){
- LinphoneFriend *f=(LinphoneFriend*)elem->data;
- linphone_friend_invalidate_subscription(f);
+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){
+ if (lf->refkey != NULL) {
ms_free(lf->refkey);
- lf->refkey=NULL;
+ lf->refkey = NULL;
+ }
+ if (key) {
+ lf->refkey = ms_strdup(key);
+ }
+ if (lf->lc) {
+ linphone_friend_save(lf, lf->lc);
}
- if (key)
- lf->refkey=ms_strdup(key);
- if (lf->lc)
- linphone_core_write_friends_config(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){
- LinphoneFriend *lf=NULL;
- MSList *elem;
- for(elem=lc->friends;elem!=NULL;elem=ms_list_next(elem)){
- lf=(LinphoneFriend*)elem->data;
- if (linphone_address_weak_equal(lf->uri,addr))
- break;
- lf=NULL;
+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;
}
-LinphoneFriend *linphone_core_get_friend_by_address(const LinphoneCore *lc, const char *uri){
- LinphoneAddress *puri=linphone_address_new(uri);
- LinphoneFriend *lf=puri ? linphone_core_find_friend(lc,puri) : NULL;
- if (puri) linphone_address_unref(puri);
+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){
- const MSList *elem;
- if (key==NULL) return NULL;
- for(elem=linphone_core_get_friend_list(lc);elem!=NULL;elem=elem->next){
- LinphoneFriend *lf=(LinphoneFriend*)elem->data;
- if (lf->refkey!=NULL && strcmp(lf->refkey,key)==0){
- 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 NULL;
+ 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)
@@ -595,7 +919,7 @@ LinphoneSubscribePolicy __policy_str_to_enum(const char* pol){
}
LinphoneProxyConfig *__index_to_proxy(LinphoneCore *lc, int index){
- if (index>=0) return (LinphoneProxyConfig*)ms_list_nth_data(lc->sip_conf.proxies,index);
+ if (index>=0) return (LinphoneProxyConfig*)bctbx_list_nth_data(lc->sip_conf.proxies,index);
else return NULL;
}
@@ -616,7 +940,7 @@ LinphoneFriend * linphone_friend_new_from_config_file(LinphoneCore *lc, int inde
if (tmp==NULL) {
return NULL;
}
- lf=linphone_friend_new_with_address(tmp);
+ lf=linphone_core_create_friend_with_address(lc, tmp);
if (lf==NULL) {
return NULL;
}
@@ -627,6 +951,8 @@ LinphoneFriend * linphone_friend_new_from_config_file(LinphoneCore *lc, int inde
}
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;
linphone_friend_set_ref_key(lf,lp_config_get_string(config,item,"refkey",NULL));
return lf;
@@ -669,6 +995,7 @@ void linphone_friend_write_to_config_file(LpConfig *config, LinphoneFriend *lf,
}
lp_config_set_string(config,key,"pol",__policy_enum_to_str(lf->pol));
lp_config_set_int(config,key,"subscribe",lf->subscribe);
+ lp_config_set_int(config, key, "presence_received", lf->presence_received);
refkey=linphone_friend_get_ref_key(lf);
if (refkey){
@@ -676,15 +1003,19 @@ void linphone_friend_write_to_config_file(LpConfig *config, LinphoneFriend *lf,
}
}
-void linphone_core_write_friends_config(LinphoneCore* lc)
-{
- MSList *elem;
+void linphone_core_write_friends_config(LinphoneCore* lc) {
+ bctbx_list_t *elem;
int i;
+ int store_friends;
+
if (! linphone_core_ready(lc)) return; /*dont write config when reading it !*/
- for (elem=lc->friends,i=0; elem!=NULL; elem=ms_list_next(elem),i++){
- linphone_friend_write_to_config_file(lc->config,(LinphoneFriend*)elem->data,i);
+ store_friends = lp_config_get_int(lc->config, "misc", "store_friends", 1);
+ if (store_friends) {
+ for (elem=linphone_core_get_default_friend_list(lc)->friends,i=0; elem!=NULL; elem=bctbx_list_next(elem),i++){
+ linphone_friend_write_to_config_file(lc->config,(LinphoneFriend*)bctbx_list_get_data(elem),i);
+ }
+ linphone_friend_write_to_config_file(lc->config,NULL,i); /* set the end */
}
- linphone_friend_write_to_config_file(lc->config,NULL,i); /* set the end */
}
LinphoneCore *linphone_friend_get_core(const LinphoneFriend *fr){
@@ -705,11 +1036,674 @@ void linphone_friend_destroy(LinphoneFriend *lf) {
linphone_friend_unref(lf);
}
+LinphoneVcard* linphone_friend_get_vcard(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;
+
+ if (fr->vcard) linphone_vcard_free(fr->vcard);
+ fr->vcard = vcard;
+ linphone_friend_save(fr, fr->lc);
+}
+
+bool_t linphone_friend_create_vcard(LinphoneFriend *fr, const char *name) {
+ LinphoneVcard *vcard = NULL;
+
+ 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_vcard_new();
+ linphone_vcard_set_full_name(vcard, name);
+ linphone_friend_set_vcard(fr, vcard);
+ 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
- NULL, // marshal
+ _linphone_friend_marshall,
FALSE
-);
\ No newline at end of file
+);
+
+/*******************************************************************************
+ * SQL storage related functions *
+ ******************************************************************************/
+
+#ifdef SQLITE_STORAGE_ENABLED
+
+static void linphone_create_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.\n", 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.\n", errmsg);
+ sqlite3_free(errmsg);
+ }
+}
+
+static bool_t linphone_update_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.\n", 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;
+ const 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.\n", errmsg);
+ sqlite3_close(db);
+ return;
+ }
+
+ linphone_create_table(db);
+ if (linphone_update_table(db)) {
+ // After updating schema, database need to be closed/reopenned
+ sqlite3_close(lc->friends_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) {
+ 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, (void (*)(void*))linphone_friend_list_unref);
+
+ while (friends_lists) {
+ LinphoneFriendList *list = (LinphoneFriendList *)bctbx_list_get_data(friends_lists);
+ linphone_core_add_friend_list(lc, list);
+ friends_lists = bctbx_list_next(friends_lists);
+ }
+ }
+}
+
+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]);
+
+ vcard = linphone_vcard_context_get_vcard_from_buffer(context, argv[6]);
+ if (vcard) {
+ linphone_vcard_set_etag(vcard, argv[7]);
+ linphone_vcard_set_url(vcard, argv[8]);
+ lf = linphone_friend_new_from_vcard(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, 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;
+ }
+}
+
+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_save(lf, lc); /* required if we freshly created vcard but core was not set at this time */
+ }
+ 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;
+}
+
+#else
+
+void linphone_core_friends_storage_init(LinphoneCore *lc) {
+}
+
+void linphone_core_friends_storage_close(LinphoneCore *lc) {
+}
+
+void linphone_core_store_friend_in_db(LinphoneCore *lc, LinphoneFriend *lf) {
+}
+
+void linphone_core_store_friends_list_in_db(LinphoneCore *lc, LinphoneFriendList *list) {
+}
+
+void linphone_core_remove_friend_from_db(LinphoneCore *lc, LinphoneFriend *lf) {
+}
+
+void linphone_core_remove_friends_list_from_db(LinphoneCore *lc, LinphoneFriendList *list) {
+}
+
+bctbx_list_t* linphone_core_fetch_friends_from_db(LinphoneCore *lc, LinphoneFriendList *list) {
+ return NULL;
+}
+
+bctbx_list_t* linphone_core_fetch_friends_lists_from_db(LinphoneCore *lc) {
+ return NULL;
+}
+
+#endif
+
+void linphone_core_set_friends_database_path(LinphoneCore *lc, const char *path) {
+ 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);
+
+ linphone_core_migrate_friends_from_rc_to_db(lc);
+ }
+}
+
+void linphone_core_migrate_friends_from_rc_to_db(LinphoneCore *lc) {
+ LpConfig *lpc = NULL;
+ LinphoneFriend *lf = NULL;
+ LinphoneFriendList *lfl = linphone_core_get_default_friend_list(lc);
+ int i;
+#ifndef SQLITE_STORAGE_ENABLED
+ ms_warning("linphone has been compiled without sqlite, can't migrate friends");
+ return;
+#endif
+ if (!lc) {
+ return;
+ }
+
+ lpc = linphone_core_get_config(lc);
+ if (!lpc) {
+ ms_warning("this core has been started without a rc file, nothing to migrate");
+ return;
+ }
+ if (lp_config_get_int(lpc, "misc", "friends_migration_done", 0) == 1) {
+ ms_warning("the friends migration has already been done, skipping...");
+ return;
+ }
+
+ if (bctbx_list_size(linphone_friend_list_get_friends(lfl)) > 0 && lfl->storage_id == 0) {
+ linphone_core_remove_friend_list(lc, lfl);
+ lfl = linphone_core_create_friend_list(lc);
+ linphone_core_add_friend_list(lc, lfl);
+ linphone_friend_list_unref(lfl);
+ }
+
+ for (i = 0; (lf = linphone_friend_new_from_config_file(lc, i)) != NULL; i++) {
+ char friend_section[32];
+
+ const LinphoneAddress *addr = linphone_friend_get_address(lf);
+ if (addr) {
+ char *address = NULL;
+ const char *displayName = linphone_address_get_display_name(addr);
+ if (!displayName) displayName = linphone_address_get_username(addr);
+
+ address = linphone_address_as_string(addr);
+ if (linphone_core_vcard_supported()) {
+ if (!linphone_friend_create_vcard(lf, displayName)) {
+ ms_warning("Couldn't create vCard for friend %s", address);
+ } else {
+ linphone_vcard_add_sip_address(linphone_friend_get_vcard(lf), address);
+ linphone_address_unref(lf->uri);
+ lf->uri = NULL;
+ }
+ }
+ ms_free(address);
+
+ linphone_friend_list_add_friend(lfl, lf);
+ linphone_friend_unref(lf);
+
+ snprintf(friend_section, sizeof(friend_section), "friend_%i", i);
+ lp_config_clean_section(lpc, friend_section);
+ }
+ }
+
+ ms_debug("friends migration successful: %i friends migrated", i);
+ lp_config_set_int(lpc, "misc", "friends_migration_done", 1);
+}
+
+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;
+ LinphoneAddress *addr;
+ char *normalized_number;
+ char *uri;
+ 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) return lfpnsu->uri;
+ 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;
+ uri = ms_strdup_printf("sip:%s@%s", normalized_number, linphone_proxy_config_get_domain(proxy_config));
+ ms_free(normalized_number);
+ addr = linphone_core_create_address(linphone_friend_get_core(lf), uri);
+ ms_free(uri);
+ if (!addr) return NULL;
+ linphone_address_set_uri_param(addr, "user", "phone");
+ full_uri = linphone_address_as_string_uri_only(addr);
+ linphone_address_unref(addr);
+ 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);
+}
diff --git a/coreapi/friendlist.c b/coreapi/friendlist.c
new file mode 100644
index 000000000..9db5c74e0
--- /dev/null
+++ b/coreapi/friendlist.c
@@ -0,0 +1,1034 @@
+/*
+linphone
+Copyright (C) 2010-2015 Belledonne Communications SARL
+
+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 "linphonecore.h"
+#include "private.h"
+
+#include
+
+BELLE_SIP_DECLARE_NO_IMPLEMENTED_INTERFACES(LinphoneFriendListCbs);
+
+BELLE_SIP_INSTANCIATE_VPTR(LinphoneFriendListCbs, belle_sip_object_t,
+ NULL, // destroy
+ NULL, // clone
+ NULL, // Marshall
+ FALSE
+);
+
+static LinphoneFriendListCbs * linphone_friend_list_cbs_new(void) {
+ return belle_sip_object_new(LinphoneFriendListCbs);
+}
+
+LinphoneFriendListCbs * linphone_friend_list_get_callbacks(const LinphoneFriendList *list) {
+ return list->cbs;
+}
+
+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;
+}
+
+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 bctbx_list_t * uri_list(const LinphoneFriendList *list) {
+ bctbx_list_t * uri_list = NULL;
+ bctbx_list_t * elem = NULL;
+ for (elem = list->friends; elem != NULL; elem = bctbx_list_next(elem)) {
+ LinphoneFriend *lf = (LinphoneFriend *)bctbx_list_get_data(elem);
+ bctbx_list_t *iterator;
+ const bctbx_list_t *addresses = linphone_friend_get_addresses(lf);
+ bctbx_list_t *numbers = linphone_friend_get_phone_numbers(lf);
+ iterator = (bctbx_list_t *)addresses;
+ while (iterator) {
+ LinphoneAddress *addr = (LinphoneAddress *)bctbx_list_get_data(iterator);
+ if (addr) {
+ char *uri = linphone_address_as_string_uri_only(addr);
+ if (uri) {
+ if (bctbx_list_find_custom(uri_list, (bctbx_compare_func)strcmp, uri) == NULL) {
+ uri_list = bctbx_list_insert_sorted(uri_list, uri, (bctbx_compare_func)strcasecmp);
+ }
+ }
+ }
+ iterator = bctbx_list_next(iterator);
+ }
+ iterator = 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) {
+ if (bctbx_list_find_custom(uri_list, (bctbx_compare_func)strcmp, uri) == NULL) {
+ uri_list = bctbx_list_insert_sorted(uri_list, ms_strdup(uri), (bctbx_compare_func)strcasecmp);
+ }
+ }
+ iterator = bctbx_list_next(iterator);
+ }
+ }
+ return uri_list;
+}
+
+static char * create_resource_list_xml(const LinphoneFriendList *list) {
+ char *xml_content = NULL;
+ xmlBufferPtr buf;
+ xmlTextWriterPtr writer;
+ int err;
+
+ if (bctbx_list_size(list->friends) <= 0) 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");
+ }
+
+ {
+ bctbx_list_t* entries = uri_list(list);
+ bctbx_list_t* it;
+ for(it = entries; it != NULL; it = it->next){
+ err = add_uri_entry(writer, err, it->data);
+ }
+ bctbx_list_free_with_data(entries, ms_free);
+ }
+ 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_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 != NULL) {
+ char xpath_str[MAX_XPATH_LENGTH];
+ LinphoneFriend *lf;
+ LinphoneContent *presence_part;
+ xmlXPathObjectPtr resource_object;
+ const char *version_str = NULL;
+ const char *full_state_str = NULL;
+ const char *uri = NULL;
+ bool_t full_state = FALSE;
+ int version;
+ int i;
+
+ 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 == NULL) {
+ 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) {
+ ms_warning("rlmi+xml: Discarding received notification with version %d because %d was expected", version, list->expected_notification_version);
+ linphone_friend_list_update_subscriptions(list); /* Refresh subscription to get new full state notify. */
+ goto end;
+ }
+
+ full_state_str = linphone_get_xml_attribute_text_content(xml_ctx, "/rlmi:list", "fullState");
+ if (full_state_str == NULL) {
+ 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 == FALSE)) {
+ ms_warning("rlmi+xml: Notification with version 0 is not full state, this is not valid");
+ goto end;
+ }
+ list->expected_notification_version = version + 1;
+
+ resource_object = linphone_get_xml_xpath_object_for_node_list(xml_ctx, "/rlmi:list/rlmi:resource");
+ if ((resource_object != NULL) && (resource_object->nodesetval != NULL)) {
+ for (i = 1; i <= resource_object->nodesetval->nodeNr; i++) {
+ LinphoneAddress* addr;
+ snprintf(xpath_str, sizeof(xpath_str), "/rlmi:list/rlmi:resource[%i]/@uri", i);
+ uri = linphone_get_xml_text_content(xml_ctx, xpath_str);
+ if (uri == NULL) 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 != NULL) {
+ const char *state = NULL;
+ snprintf(xpath_str, sizeof(xpath_str),"/rlmi:list/rlmi:resource[%i]/rlmi:instance/@state", i);
+ state = linphone_get_xml_text_content(xml_ctx, xpath_str);
+ if ((state != NULL) && (strcmp(state, "active") == 0)) {
+ const char *cid = NULL;
+ snprintf(xpath_str, sizeof(xpath_str),"/rlmi:list/rlmi:resource[%i]/rlmi:instance/@cid", i);
+ cid = linphone_get_xml_text_content(xml_ctx, xpath_str);
+ if (cid != NULL) {
+ presence_part = linphone_content_find_part_by_header(body, "Content-Id", cid);
+ if (presence_part == NULL) {
+ 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 != NULL) {
+ const char *phone_number = linphone_friend_sip_uri_to_phone_number(lf, uri);
+ lf->presence_received = TRUE;
+ if (phone_number) linphone_friend_set_presence_model_for_uri_or_tel(lf, phone_number, (LinphonePresenceModel *)presence);
+ else linphone_friend_set_presence_model_for_uri_or_tel(lf, uri, (LinphonePresenceModel *)presence);
+ if (full_state == FALSE) {
+ if (phone_number)
+ linphone_core_notify_notify_presence_received_for_uri_or_tel(list->lc, lf, phone_number, (LinphonePresenceModel *)presence);
+ else
+ linphone_core_notify_notify_presence_received_for_uri_or_tel(list->lc, lf, uri, (LinphonePresenceModel *)presence);
+ linphone_core_notify_notify_presence_received(list->lc, lf);
+ }
+ }
+ linphone_content_unref(presence_part);
+ }
+ }
+ if (cid != NULL) linphone_free_xml_text_content(cid);
+ }
+ if (state != NULL) linphone_free_xml_text_content(state);
+ lf->subscribe_active = TRUE;
+ }
+ linphone_free_xml_text_content(uri);
+ }
+ }
+ if (resource_object != NULL) xmlXPathFreeObject(resource_object);
+
+ if (full_state == TRUE) {
+ const bctbx_list_t *addresses;
+ bctbx_list_t *numbers;
+ bctbx_list_t *iterator;
+ bctbx_list_t *l = list->friends;
+ for (; l != NULL; l = bctbx_list_next(l)) {
+ lf = (LinphoneFriend *)bctbx_list_get_data(l);
+ addresses = linphone_friend_get_addresses(lf);
+ numbers = linphone_friend_get_phone_numbers(lf);
+ iterator = (bctbx_list_t *)addresses;
+ while (iterator) {
+ LinphoneAddress *addr = (LinphoneAddress *)bctbx_list_get_data(iterator);
+ char *uri = linphone_address_as_string_uri_only(addr);
+ const LinphonePresenceModel *presence = linphone_friend_get_presence_model_for_uri_or_tel(lf, uri);
+ if (presence) linphone_core_notify_notify_presence_received_for_uri_or_tel(list->lc, lf, uri, presence);
+ ms_free(uri);
+ iterator = bctbx_list_next(iterator);
+ }
+ iterator = numbers;
+ while (iterator) {
+ const char *number = (const char *)bctbx_list_get_data(iterator);
+ const LinphonePresenceModel *presence = linphone_friend_get_presence_model_for_uri_or_tel(lf, number);
+ if (presence) linphone_core_notify_notify_presence_received_for_uri_or_tel(list->lc, lf, number, presence);
+ iterator = bctbx_list_next(iterator);
+ }
+ if (numbers) bctbx_list_free(numbers);
+ if (linphone_friend_is_presence_received(lf) == TRUE) {
+ linphone_core_notify_notify_presence_received(list->lc, lf);
+ }
+ }
+ }
+ } 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) {
+ 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 = TRUE;
+ belle_sip_object_ref(list);
+ 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);
+ 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);
+}
+
+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
+ TRUE
+);
+
+
+LinphoneFriendList * linphone_core_create_friend_list(LinphoneCore *lc) {
+ LinphoneFriendList *list = linphone_friend_list_new();
+ list->lc = lc;
+ 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)
+ 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_destroy(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 (bctbx_list_find(list->friends, lf) != NULL) {
+ 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 == NULL) {
+ /* 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);
+}
+
+LinphoneFriendListStatus linphone_friend_list_import_friend(LinphoneFriendList *list, LinphoneFriend *lf, bool_t synchronize) {
+ if (lf->friend_list) {
+ 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_append(list->friends, linphone_friend_ref(lf));
+ if (synchronize) {
+ list->dirty_friends_to_update = bctbx_list_append(list->dirty_friends_to_update, linphone_friend_ref(lf));
+ }
+ return LinphoneFriendListOK;
+}
+
+static void carddav_done(LinphoneCardDavContext *cdc, bool_t success, const char *msg) {
+ if (cdc && cdc->friend_list->cbs->sync_state_changed_cb) {
+ cdc->friend_list->cbs->sync_state_changed_cb(cdc->friend_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 *elem = bctbx_list_find(list->friends, lf);
+ if (elem == NULL) return LinphoneFriendListNonExistentFriend;
+
+#ifdef SQLITE_STORAGE_ENABLED
+ if (lf && lf->lc && lf->lc->friends_db) {
+ linphone_core_remove_friend_from_db(lf->lc, lf);
+ }
+#endif
+ 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);
+ }
+ linphone_carddav_delete_vcard(cdc, lf);
+ }
+ }
+ }
+ if (!list->lc->friends_db_file) {
+ linphone_core_write_friends_config(list->lc);
+ }
+
+ lf->friend_list = NULL;
+ linphone_friend_unref(lf);
+ list->friends = bctbx_list_erase_link(list->friends, elem);
+ 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);
+ }
+ 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 *lfl = cdc->friend_list;
+ linphone_friend_list_import_friend(lfl, lf, FALSE);
+ if (cdc->friend_list->cbs->contact_created_cb) {
+ cdc->friend_list->cbs->contact_created_cb(lfl, lf);
+ }
+ }
+}
+
+static void carddav_removed(LinphoneCardDavContext *cdc, LinphoneFriend *lf) {
+ if (cdc) {
+ LinphoneFriendList *lfl = cdc->friend_list;
+ _linphone_friend_list_remove_friend(lfl, lf, FALSE);
+ if (cdc->friend_list->cbs->contact_deleted_cb) {
+ cdc->friend_list->cbs->contact_deleted_cb(lfl, lf);
+ }
+ }
+}
+
+static void carddav_updated(LinphoneCardDavContext *cdc, LinphoneFriend *lf_new, LinphoneFriend *lf_old) {
+ if (cdc) {
+ LinphoneFriendList *lfl = cdc->friend_list;
+ bctbx_list_t *elem = bctbx_list_find(lfl->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(lfl, 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);
+ }
+ linphone_carddav_synchronize(cdc);
+ }
+}
+
+LinphoneFriend * linphone_friend_list_find_friend_by_address(const LinphoneFriendList *list, const LinphoneAddress *address) {
+ LinphoneFriend *result = NULL;
+ const bctbx_list_t *elem;
+ const char *param = linphone_address_get_uri_param(address, "user");
+ bool_t find_phone_number = (param && (strcmp(param, "phone") == 0));
+
+ for (elem = list->friends; (elem != NULL) && (result == NULL); elem = bctbx_list_next(elem)) {
+ bctbx_list_t *iterator;
+ LinphoneFriend *lf = (LinphoneFriend *)bctbx_list_get_data(elem);
+ if (find_phone_number == TRUE) {
+ char *uri = linphone_address_as_string_uri_only(address);
+ const char *phone_number = linphone_friend_sip_uri_to_phone_number(lf, uri);
+ bctbx_list_t *phone_numbers = linphone_friend_get_phone_numbers(lf);
+ iterator = phone_numbers;
+ ms_free(uri);
+ if (!phone_number) continue;
+ while (iterator && (result == NULL)) {
+ const char *number = (const char *)bctbx_list_get_data(iterator);
+ if (strcmp(number, phone_number) == 0) result = lf;
+ iterator = bctbx_list_next(iterator);
+ }
+ } else {
+ const bctbx_list_t *addresses = linphone_friend_get_addresses(lf);
+ iterator = (bctbx_list_t *)addresses;
+ while (iterator && (result == NULL)) {
+ LinphoneAddress *lfaddr = (LinphoneAddress *)bctbx_list_get_data(iterator);
+ if (linphone_address_weak_equal(lfaddr, address)) result = lf;
+ iterator = bctbx_list_next(iterator);
+ }
+ }
+ }
+ return result;
+}
+
+LinphoneFriend * linphone_friend_list_find_friend_by_uri(const LinphoneFriendList *list, const char *uri) {
+ LinphoneAddress *address = linphone_address_new(uri);
+ LinphoneFriend *lf = address ? linphone_friend_list_find_friend_by_address(list, address) : NULL;
+ if (address) linphone_address_unref(address);
+ return lf;
+}
+
+LinphoneFriend * linphone_friend_list_find_friend_by_ref_key(const LinphoneFriendList *list, const char *ref_key) {
+ const bctbx_list_t *elem;
+ if (ref_key == NULL) return NULL;
+ for (elem = list->friends; elem != NULL; elem = bctbx_list_next(elem)) {
+ LinphoneFriend *lf = (LinphoneFriend *)bctbx_list_get_data(elem);
+ if ((lf->refkey != NULL) && (strcmp(lf->refkey, ref_key) == 0)) return lf;
+ }
+ return NULL;
+}
+
+LinphoneFriend * linphone_friend_list_find_friend_by_inc_subscribe(const LinphoneFriendList *list, 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, 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) || sal_op_is_forked_of(lf->outsub, 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(LinphoneFriendList *list){
+ const LinphoneAddress *address = _linphone_friend_list_get_rls_address(list);
+ char *xml_content = create_resource_list_xml(list);
+ if ((address != NULL) && (xml_content != NULL) && (linphone_friend_list_has_subscribe_inactive(list) == TRUE)) {
+ unsigned char digest[16];
+ bctbx_md5((unsigned char *)xml_content, strlen(xml_content), digest);
+ if ((list->event != NULL) && (list->content_digest != NULL) && (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;
+ int expires = lp_config_get_int(list->lc->config, "sip", "rls_presence_expires", 3600);
+ list->expected_notification_version = 0;
+ if (list->content_digest != NULL) ms_free(list->content_digest);
+ list->content_digest = ms_malloc(sizeof(digest));
+ memcpy(list->content_digest, digest, sizeof(digest));
+ if (list->event != NULL) {
+ 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");
+ }
+ linphone_event_send_subscribe(list->event, content);
+ linphone_content_unref(content);
+ linphone_event_set_user_data(list->event, list);
+ }
+ }
+ if (xml_content != NULL) ms_free(xml_content);
+}
+
+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);
+ 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;
+ 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)) {
+ 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'");
+ 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);
+ }
+}
+
+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("Resseting version count for friend list [%p]",list);
+ list->expected_notification_version = 0;
+ }
+ }
+}
+
+LinphoneCore* linphone_friend_list_get_core(const LinphoneFriendList *list) {
+ return list->lc;
+}
+
+int linphone_friend_list_import_friends_from_vcard4_file(LinphoneFriendList *list, const char *vcard_file) {
+ bctbx_list_t *vcards = NULL;
+ 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 = linphone_vcard_context_get_vcard_list_from_file(list->lc->vcard_context, vcard_file);
+ vcards_iterator = vcards;
+ if (!vcards) {
+ ms_error("Failed to parse the file %s", vcard_file);
+ return -1;
+ }
+
+ 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);
+ if (lf) {
+ if (LinphoneFriendListOK == linphone_friend_list_import_friend(list, lf, TRUE)) {
+ linphone_friend_save(lf, lf->lc);
+ count++;
+ }
+ linphone_friend_unref(lf);
+ } else {
+ linphone_vcard_free(vcard);
+ }
+ vcards_iterator = bctbx_list_next(vcards_iterator);
+ }
+ bctbx_list_free(vcards);
+ linphone_core_store_friends_list_in_db(list->lc, list);
+ return count;
+}
+
+int linphone_friend_list_import_friends_from_vcard4_buffer(LinphoneFriendList *list, const char *vcard_buffer) {
+ bctbx_list_t *vcards = NULL;
+ 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 = linphone_vcard_context_get_vcard_list_from_buffer(list->lc->vcard_context, vcard_buffer);
+ vcards_iterator = vcards;
+ if (!vcards) {
+ ms_error("Failed to parse the buffer");
+ return -1;
+ }
+
+ 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);
+ if (lf) {
+ if (LinphoneFriendListOK == linphone_friend_list_import_friend(list, lf, TRUE)) {
+ count++;
+ }
+ linphone_friend_unref(lf);
+ } else {
+ linphone_vcard_free(vcard);
+ }
+ vcards_iterator = bctbx_list_next(vcards_iterator);
+ }
+ bctbx_list_free(vcards);
+ linphone_core_store_friends_list_in_db(list->lc, list);
+ return count;
+}
+
+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);
+ }
+
+ }
+}
diff --git a/coreapi/friendlist.h b/coreapi/friendlist.h
new file mode 100644
index 000000000..78ff3ca2f
--- /dev/null
+++ b/coreapi/friendlist.h
@@ -0,0 +1,444 @@
+/*
+friendlist.h
+Copyright (C) 2010-2015 Belledonne Communications SARL
+
+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.
+*/
+
+
+#ifndef LINPHONE_FRIENDLIST_H_
+#define LINPHONE_FRIENDLIST_H_
+
+
+#ifdef IN_LINPHONE
+#include "linphonefriend.h"
+#include "linphonepresence.h"
+#else
+#include "linphone/linphonefriend.h"
+#include "linphone/linphonepresence.h"
+#endif
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @addtogroup buddy_list
+ * @{
+ */
+
+/**
+* Enum describing the status of a LinphoneFriendList operation.
+**/
+typedef enum _LinphoneFriendListStatus {
+ LinphoneFriendListOK,
+ LinphoneFriendListNonExistentFriend,
+ LinphoneFriendListInvalidFriend
+} LinphoneFriendListStatus;
+
+/**
+ * Enum describing the status of a CardDAV synchronization
+ */
+typedef enum _LinphoneFriendListSyncStatus {
+ LinphoneFriendListSyncStarted,
+ LinphoneFriendListSyncSuccessful,
+ LinphoneFriendListSyncFailure
+} LinphoneFriendListSyncStatus;
+
+/**
+ * The LinphoneFriendList object representing a list of friends.
+**/
+typedef struct _LinphoneFriendList LinphoneFriendList;
+
+/**
+ * Create a new empty LinphoneFriendList object.
+ * @param[in] lc LinphoneCore object.
+ * @return A new LinphoneFriendList object.
+**/
+LINPHONE_PUBLIC LinphoneFriendList * linphone_core_create_friend_list(LinphoneCore *lc);
+
+/**
+ * Add a friend list.
+ * @param[in] lc LinphoneCore object
+ * @param[in] list LinphoneFriendList object
+ */
+LINPHONE_PUBLIC void linphone_core_add_friend_list(LinphoneCore *lc, LinphoneFriendList *list);
+
+/**
+ * Removes a friend list.
+ * @param[in] lc LinphoneCore object
+ * @param[in] list LinphoneFriendList object
+ */
+LINPHONE_PUBLIC void linphone_core_remove_friend_list(LinphoneCore *lc, LinphoneFriendList *list);
+
+/**
+ * Retrieves the list of LinphoneFriendList from the core.
+ * @param[in] lc LinphoneCore object
+ * @return \mslist{LinphoneFriendList} a list of LinphoneFriendList
+ */
+LINPHONE_PUBLIC const bctbx_list_t * linphone_core_get_friends_lists(const LinphoneCore *lc);
+
+/**
+ * Retrieves the first list of LinphoneFriend from the core.
+ * @param[in] lc LinphoneCore object
+ * @return the first LinphoneFriendList object or NULL
+ */
+LINPHONE_PUBLIC LinphoneFriendList * linphone_core_get_default_friend_list(const LinphoneCore *lc);
+
+/**
+ * Acquire a reference to the friend list.
+ * @param[in] list LinphoneFriendList object.
+ * @return The same LinphoneFriendList object.
+**/
+LINPHONE_PUBLIC LinphoneFriendList * linphone_friend_list_ref(LinphoneFriendList *list);
+
+/**
+ * Release reference to the friend list.
+ * @param[in] list LinphoneFriendList object.
+**/
+LINPHONE_PUBLIC void linphone_friend_list_unref(LinphoneFriendList *list);
+
+/**
+ * Retrieve the user pointer associated with the friend list.
+ * @param[in] list LinphoneFriendList object.
+ * @return The user pointer associated with the friend list.
+**/
+LINPHONE_PUBLIC void * linphone_friend_list_get_user_data(const LinphoneFriendList *list);
+
+/**
+ * Assign a user pointer to the friend list.
+ * @param[in] list LinphoneFriendList object.
+ * @param[in] ud The user pointer to associate with the friend list.
+**/
+LINPHONE_PUBLIC void linphone_friend_list_set_user_data(LinphoneFriendList *list, void *ud);
+
+/**
+ * Get the display name of the friend list.
+ * @param[in] list LinphoneFriendList object.
+ * @return The display name of the friend list.
+**/
+LINPHONE_PUBLIC const char * linphone_friend_list_get_display_name(const LinphoneFriendList *list);
+
+/**
+ * Set the display name of the friend list.
+ * @param[in] list LinphoneFriendList object.
+ * @param[in] display_name The new display name of the friend list.
+**/
+LINPHONE_PUBLIC void linphone_friend_list_set_display_name(LinphoneFriendList *list, const char *display_name);
+
+/**
+ * Get the RLS (Resource List Server) URI associated with the friend list to subscribe to these friends presence.
+ * @param[in] list LinphoneFriendList object.
+ * @return The RLS URI associated with the friend list.
+**/
+LINPHONE_PUBLIC const char * linphone_friend_list_get_rls_uri(const LinphoneFriendList *list);
+
+/**
+ * Set the RLS (Resource List Server) URI associated with the friend list to subscribe to these friends presence.
+ * @param[in] list LinphoneFriendList object.
+ * @param[in] rls_uri The RLS URI to associate with the friend list.
+**/
+LINPHONE_PUBLIC void linphone_friend_list_set_rls_uri(LinphoneFriendList *list, const char *rls_uri);
+
+
+/**
+ * Get the RLS (Resource List Server) URI associated with the friend list to subscribe to these friends presence.
+ * @param[in] list LinphoneFriendList object.
+ * @return The RLS URI associated with the friend list.
+**/
+LINPHONE_PUBLIC const LinphoneAddress * linphone_friend_list_get_rls_address(const LinphoneFriendList *list);
+
+/**
+ * Set the RLS (Resource List Server) URI associated with the friend list to subscribe to these friends presence.
+ * @param[in] list LinphoneFriendList object.
+ * @param[in] rls_addr The RLS URI to associate with the friend list.
+**/
+LINPHONE_PUBLIC void linphone_friend_list_set_rls_address(LinphoneFriendList *list, const LinphoneAddress *rls_addr);
+
+/**
+ * Add a friend to a friend list. If or when a remote CardDAV server will be attached to the list, the friend will be sent to the server.
+ * @param[in] list LinphoneFriendList object.
+ * @param[in] lf LinphoneFriend object to add to the friend list.
+ * @return LinphoneFriendListOK if successfully added, LinphoneFriendListInvalidFriend if the friend is not valid.
+**/
+LINPHONE_PUBLIC LinphoneFriendListStatus linphone_friend_list_add_friend(LinphoneFriendList *list, LinphoneFriend *lf);
+
+/**
+ * Add a friend to a friend list. The friend will never be sent to a remote CardDAV server.
+ * Warning! LinphoneFriends added this way will be removed on the next synchronization, and the callback contact_deleted will be called.
+ * @param[in] list LinphoneFriendList object.
+ * @param[in] lf LinphoneFriend object to add to the friend list.
+ * @return LinphoneFriendListOK if successfully added, LinphoneFriendListInvalidFriend if the friend is not valid.
+**/
+LINPHONE_PUBLIC LinphoneFriendListStatus linphone_friend_list_add_local_friend(LinphoneFriendList *list, LinphoneFriend *lf);
+
+/**
+ * Remove a friend from a friend list.
+ * @param[in] list LinphoneFriendList object.
+ * @param[in] lf LinphoneFriend object to remove from the friend list.
+ * @return LinphoneFriendListOK if removed successfully, LinphoneFriendListNonExistentFriend if the friend is not in the list.
+**/
+LINPHONE_PUBLIC LinphoneFriendListStatus linphone_friend_list_remove_friend(LinphoneFriendList *list, LinphoneFriend *lf);
+
+/**
+ * Retrieves the list of LinphoneFriend from this LinphoneFriendList.
+ * @param[in] list LinphoneFriendList object
+ * @return \mslist{LinphoneFriend} a list of LinphoneFriend
+ */
+LINPHONE_PUBLIC const bctbx_list_t * linphone_friend_list_get_friends(const LinphoneFriendList *list);
+
+/**
+ * Find a friend in the friend list using a LinphoneAddress.
+ * @param[in] list LinphoneFriendList object.
+ * @param[in] address LinphoneAddress object of the friend we want to search for.
+ * @return A LinphoneFriend if found, NULL otherwise.
+**/
+LINPHONE_PUBLIC LinphoneFriend * linphone_friend_list_find_friend_by_address(const LinphoneFriendList *list, const LinphoneAddress *address);
+
+/**
+ * Find a friend in the friend list using an URI string.
+ * @param[in] list LinphoneFriendList object.
+ * @param[in] uri A string containing the URI of the friend we want to search for.
+ * @return A LinphoneFriend if found, NULL otherwise.
+**/
+LINPHONE_PUBLIC LinphoneFriend * linphone_friend_list_find_friend_by_uri(const LinphoneFriendList *list, const char *uri);
+
+/**
+ * Find a friend in the friend list using a ref key.
+ * @param[in] list LinphoneFriendList object.
+ * @param[in] ref_key The ref key string of the friend we want to search for.
+ * @return A LinphoneFriend if found, NULL otherwise.
+**/
+LINPHONE_PUBLIC LinphoneFriend * linphone_friend_list_find_friend_by_ref_key(const LinphoneFriendList *list, const char *ref_key);
+
+/**
+ * Update presence subscriptions for the entire list. Calling this function is necessary when list subscriptions are enabled,
+ * ie when a RLS presence server is used.
+ * @param[in] list the friend list
+**/
+LINPHONE_PUBLIC void linphone_friend_list_update_subscriptions(LinphoneFriendList *list);
+
+/**
+ * Notify our presence to all the friends in the friend list that have subscribed to our presence directly (not using a RLS).
+ * @param[in] list LinphoneFriendList object.
+ * @param[in] presence LinphonePresenceModel object.
+**/
+LINPHONE_PUBLIC void linphone_friend_list_notify_presence(LinphoneFriendList *list, LinphonePresenceModel *presence);
+
+/**
+ * Get the URI associated with the friend list.
+ * @param[in] list LinphoneFriendList object.
+ * @return The URI associated with the friend list.
+**/
+LINPHONE_PUBLIC const char * linphone_friend_list_get_uri(const LinphoneFriendList *list);
+
+/**
+ * Set the URI associated with the friend list.
+ * @param[in] list LinphoneFriendList object.
+ * @param[in] uri The URI to associate with the friend list.
+**/
+LINPHONE_PUBLIC void linphone_friend_list_set_uri(LinphoneFriendList *list, const char *uri);
+
+/**
+ * Sets the revision from the last synchronization.
+ * @param[in] list LinphoneFriendList object.
+ * @param[in] rev The revision
+ */
+void linphone_friend_list_update_revision(LinphoneFriendList *list, int rev);
+
+/**
+ * An object to handle the callbacks for LinphoneFriend synchronization.
+**/
+typedef struct _LinphoneFriendListCbs LinphoneFriendListCbs;
+
+/**
+ * Callback used to notify a new contact has been created on the CardDAV server and downloaded locally
+ * @param list The LinphoneFriendList object the new contact is added to
+ * @param lf The LinphoneFriend object that has been created
+**/
+typedef void (*LinphoneFriendListCbsContactCreatedCb)(LinphoneFriendList *list, LinphoneFriend *lf);
+
+/**
+ * Callback used to notify a contact has been deleted on the CardDAV server
+ * @param list The LinphoneFriendList object a contact has been removed from
+ * @param lf The LinphoneFriend object that has been deleted
+**/
+typedef void (*LinphoneFriendListCbsContactDeletedCb)(LinphoneFriendList *list, LinphoneFriend *lf);
+
+/**
+ * Callback used to notify a contact has been updated on the CardDAV server
+ * @param list The LinphoneFriendList object in which a contact has been updated
+ * @param new_friend The new LinphoneFriend object corresponding to the updated contact
+ * @param old_friend The old LinphoneFriend object before update
+**/
+typedef void (*LinphoneFriendListCbsContactUpdatedCb)(LinphoneFriendList *list, LinphoneFriend *new_friend, LinphoneFriend *old_friend);
+
+/**
+ * Callback used to notify the status of the synchronization has changed
+ * @param list The LinphoneFriendList object for which the status has changed
+ * @param status The new synchronisation status
+ * @param msg An additional information on the status update
+**/
+typedef void (*LinphoneFriendListCbsSyncStateChangedCb)(LinphoneFriendList *list, LinphoneFriendListSyncStatus status, const char *msg);
+
+/**
+ * Get the LinphoneFriendListCbs object associated with a LinphoneFriendList.
+ * @param[in] list LinphoneFriendList object
+ * @return The LinphoneFriendListCbs object associated with the LinphoneFriendList.
+**/
+LINPHONE_PUBLIC LinphoneFriendListCbs * linphone_friend_list_get_callbacks(const LinphoneFriendList *list);
+
+/**
+ * Acquire a reference to a LinphoneFriendListCbs object.
+ * @param[in] cbs LinphoneFriendListCbs object.
+ * @return The same LinphoneFriendListCbs object.
+**/
+LINPHONE_PUBLIC LinphoneFriendListCbs * linphone_friend_list_cbs_ref(LinphoneFriendListCbs *cbs);
+
+/**
+ * Release a reference to a LinphoneFriendListCbs object.
+ * @param[in] cbs LinphoneFriendListCbs object.
+**/
+LINPHONE_PUBLIC void linphone_friend_list_cbs_unref(LinphoneFriendListCbs *cbs);
+
+/**
+ * Retrieve the user pointer associated with a LinphoneFriendListCbs object.
+ * @param[in] cbs LinphoneFriendListCbs object.
+ * @return The user pointer associated with the LinphoneFriendListCbs object.
+**/
+LINPHONE_PUBLIC void *linphone_friend_list_cbs_get_user_data(const LinphoneFriendListCbs *cbs);
+
+/**
+ * Assign a user pointer to a LinphoneFriendListCbs object.
+ * @param[in] cbs LinphoneFriendListCbs object.
+ * @param[in] ud The user pointer to associate with the LinphoneFriendListCbs object.
+**/
+LINPHONE_PUBLIC void linphone_friend_list_cbs_set_user_data(LinphoneFriendListCbs *cbs, void *ud);
+
+/**
+ * Get the contact created callback.
+ * @param[in] cbs LinphoneFriendListCbs object.
+ * @return The current contact created callback.
+**/
+LINPHONE_PUBLIC LinphoneFriendListCbsContactCreatedCb linphone_friend_list_cbs_get_contact_created(const LinphoneFriendListCbs *cbs);
+
+/**
+ * Set the contact created callback.
+ * @param[in] cbs LinphoneFriendListCbs object.
+ * @param[in] cb The contact created to be used.
+**/
+LINPHONE_PUBLIC void linphone_friend_list_cbs_set_contact_created(LinphoneFriendListCbs *cbs, LinphoneFriendListCbsContactCreatedCb cb);
+
+/**
+ * Get the contact deleted callback.
+ * @param[in] cbs LinphoneFriendListCbs object.
+ * @return The current contact deleted callback.
+**/
+LINPHONE_PUBLIC LinphoneFriendListCbsContactDeletedCb linphone_friend_list_cbs_get_contact_deleted(const LinphoneFriendListCbs *cbs);
+
+/**
+ * Set the contact deleted callback.
+ * @param[in] cbs LinphoneFriendListCbs object.
+ * @param[in] cb The contact deleted to be used.
+**/
+LINPHONE_PUBLIC void linphone_friend_list_cbs_set_contact_deleted(LinphoneFriendListCbs *cbs, LinphoneFriendListCbsContactDeletedCb cb);
+
+/**
+ * Get the contact updated callback.
+ * @param[in] cbs LinphoneFriendListCbs object.
+ * @return The current contact updated callback.
+**/
+LINPHONE_PUBLIC LinphoneFriendListCbsContactUpdatedCb linphone_friend_list_cbs_get_contact_updated(const LinphoneFriendListCbs *cbs);
+
+/**
+ * Set the contact updated callback.
+ * @param[in] cbs LinphoneFriendListCbs object.
+ * @param[in] cb The contact updated to be used.
+**/
+LINPHONE_PUBLIC void linphone_friend_list_cbs_set_contact_updated(LinphoneFriendListCbs *cbs, LinphoneFriendListCbsContactUpdatedCb cb);
+
+/**
+ * Get the sync status changed callback.
+ * @param[in] cbs LinphoneFriendListCbs object.
+ * @return The current sync status changedcallback.
+**/
+LINPHONE_PUBLIC LinphoneFriendListCbsSyncStateChangedCb linphone_friend_list_cbs_get_sync_status_changed(const LinphoneFriendListCbs *cbs);
+
+/**
+ * Set the contact updated callback.
+ * @param[in] cbs LinphoneFriendListCbs object.
+ * @param[in] cb The sync status changed to be used.
+**/
+LINPHONE_PUBLIC void linphone_friend_list_cbs_set_sync_status_changed(LinphoneFriendListCbs *cbs, LinphoneFriendListCbsSyncStateChangedCb cb);
+
+/**
+ * Starts a CardDAV synchronization using value set using linphone_friend_list_set_uri.
+ * @param[in] list LinphoneFriendList object.
+ */
+LINPHONE_PUBLIC void linphone_friend_list_synchronize_friends_from_server(LinphoneFriendList *list);
+
+/**
+ * Goes through all the LinphoneFriend that are dirty and does a CardDAV PUT to update the server.
+ * @param[in] list LinphoneFriendList object.
+ */
+void linphone_friend_list_update_dirty_friends(LinphoneFriendList *list);
+
+/**
+ * Returns the LinphoneCore object attached to this LinphoneFriendList.
+ * @param[in] list LinphoneFriendList object.
+ * @return a LinphoneCore object
+ */
+LINPHONE_PUBLIC LinphoneCore* linphone_friend_list_get_core(const LinphoneFriendList *list);
+
+/**
+ * Creates and adds LinphoneFriend objects to LinphoneFriendList from a file that contains the vCard(s) to parse
+ * @param[in] list the LinphoneFriendList object
+ * @param[in] vcard_file the path to a file that contains the vCard(s) to parse
+ * @return the amount of linphone friends created
+ */
+LINPHONE_PUBLIC int linphone_friend_list_import_friends_from_vcard4_file(LinphoneFriendList *list, const char *vcard_file);
+
+/**
+ * Creates and adds LinphoneFriend objects to LinphoneFriendList from a buffer that contains the vCard(s) to parse
+ * @param[in] list the LinphoneFriendList object
+ * @param[in] vcard_buffer the buffer that contains the vCard(s) to parse
+ * @return the amount of linphone friends created
+ */
+LINPHONE_PUBLIC int linphone_friend_list_import_friends_from_vcard4_buffer(LinphoneFriendList *list, const char *vcard_buffer);
+
+/**
+ * Creates and export LinphoneFriend objects from LinphoneFriendList to a file using vCard 4 format
+ * @param[in] list the LinphoneFriendList object
+ * @param[in] vcard_file the path to a file that will contain the vCards
+ */
+LINPHONE_PUBLIC void linphone_friend_list_export_friends_as_vcard4_file(LinphoneFriendList *list, const char *vcard_file);
+
+/**
+ * Enable subscription to NOTIFYes of all friends list
+ * @param[in] list the LinphoneFriendList object
+ * @param[in] enabled should subscription be enabled or not
+ */
+LINPHONE_PUBLIC void linphone_friend_list_enable_subscriptions(LinphoneFriendList *list, bool_t enabled);
+
+/**
+ * @}
+ */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LINPHONE_FRIENDLIST_H_ */
diff --git a/coreapi/gitversion.cmake b/coreapi/gitversion.cmake
index e626873a4..ca9284bae 100644
--- a/coreapi/gitversion.cmake
+++ b/coreapi/gitversion.cmake
@@ -21,19 +21,32 @@
############################################################################
if(GIT_EXECUTABLE)
- execute_process(
- COMMAND ${GIT_EXECUTABLE} describe --always
- WORKING_DIRECTORY ${WORK_DIR}
- OUTPUT_VARIABLE GIT_REVISION
- OUTPUT_STRIP_TRAILING_WHITESPACE
- )
- execute_process(
- COMMAND ${CMAKE_COMMAND} -E echo "#define LIBLINPHONE_GIT_VERSION \"${GIT_REVISION}\""
- OUTPUT_FILE ${OUTPUT_DIR}/liblinphone_gitversion.h
- )
-else()
- execute_process(
- COMMAND ${CMAKE_COMMAND} -E echo "#define LIBLINPHONE_GIT_VERSION \"unknown\""
- OUTPUT_FILE ${OUTPUT_DIR}/liblinphone_gitversion.h
- )
+ macro(GIT_COMMAND OUTPUT_VAR)
+ set(GIT_ARGS ${ARGN})
+ execute_process(
+ COMMAND ${GIT_EXECUTABLE} ${ARGN}
+ WORKING_DIRECTORY ${WORK_DIR}
+ OUTPUT_VARIABLE ${OUTPUT_VAR}
+ OUTPUT_STRIP_TRAILING_WHITESPACE
+ )
+ endmacro()
+
+ GIT_COMMAND(GIT_DESCRIBE describe --always)
+ GIT_COMMAND(GIT_TAG describe --abbrev=0)
+ GIT_COMMAND(GIT_REVISION rev-parse HEAD)
+endif()
+
+if(GIT_DESCRIBE)
+ if(NOT GIT_TAG STREQUAL LINPHONE_VERSION)
+ message(FATAL_ERROR "LINPHONE_VERSION (${LINPHONE_VERSION}) and git tag (${GIT_TAG}) differ. Please put them identical")
+ endif()
+ set(LIBLINPHONE_GIT_VERSION "${GIT_DESCRIBE}")
+ configure_file("${WORK_DIR}/gitversion.h.in" "${OUTPUT_DIR}/liblinphone_gitversion.h" @ONLY)
+elseif(GIT_REVISION)
+ set(LIBLINPHONE_GIT_VERSION "${LINPHONE_VERSION}_${GIT_REVISION}")
+ configure_file("${WORK_DIR}/gitversion.h.in" "${OUTPUT_DIR}/liblinphone_gitversion.h" @ONLY)
+else()
+ if(NOT EXISTS "${OUTPUT_DIR}/liblinphone_gitversion.h")
+ execute_process(COMMAND ${CMAKE_COMMAND} -E touch "${OUTPUT_DIR}/liblinphone_gitversion.h")
+ endif()
endif()
diff --git a/coreapi/gitversion.h.in b/coreapi/gitversion.h.in
new file mode 100644
index 000000000..ab0efd602
--- /dev/null
+++ b/coreapi/gitversion.h.in
@@ -0,0 +1,21 @@
+/*
+linphone
+Copyright (C) 2010-2014 Belledonne Communications SARL
+
+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 LIBLINPHONE_GIT_VERSION "@LIBLINPHONE_GIT_VERSION@"
\ No newline at end of file
diff --git a/coreapi/help/CMakeLists.txt b/coreapi/help/CMakeLists.txt
index 0ee3b6561..64a18c024 100644
--- a/coreapi/help/CMakeLists.txt
+++ b/coreapi/help/CMakeLists.txt
@@ -20,26 +20,70 @@
#
############################################################################
-find_package(Doxygen)
-
-if(DOXYGEN_FOUND)
- if(DOXYGEN_DOT_FOUND)
- set(top_srcdir ${CMAKE_SOURCE_DIR})
- configure_file(${CMAKE_CURRENT_SOURCE_DIR}/Doxyfile.in ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile)
- file(GLOB DOC_INPUT_FILES
- [^.]*.c
- [^.]*.dox
- ../[^.]*.h
- ../[^.]*.c
- )
- add_custom_command(OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/doc/html/index.html"
- COMMAND ${DOXYGEN_EXECUTABLE} ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile
- DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile ${DOC_INPUT_FILES}
- )
- add_custom_target(doc ALL DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/doc/html/index.html")
- install(DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/doc/html" "${CMAKE_CURRENT_BINARY_DIR}/doc/xml"
- DESTINATION "share/doc/linphone-${LINPHONE_VERSION}")
- else()
- message(WARNING "The dot program is needed to generate the linphone documentation. You can get it from http://www.graphviz.org/.")
+if (ENABLE_DOC)
+ find_package(Doxygen)
+ if(DOXYGEN_FOUND)
+ if(DOXYGEN_DOT_FOUND)
+ set(top_srcdir "${CMAKE_CURRENT_LIST_DIR}/../../")
+ configure_file(${CMAKE_CURRENT_SOURCE_DIR}/Doxyfile.in ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile)
+ file(GLOB DOC_INPUT_FILES
+ [^.]*.c
+ [^.]*.dox
+ ../[^.]*.h
+ ../[^.]*.c
+ )
+ add_custom_command(OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/doc/html/index.html"
+ COMMAND ${DOXYGEN_EXECUTABLE} ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile
+ DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile ${DOC_INPUT_FILES}
+ )
+ add_custom_target(linphone-doc ALL DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/doc/html/index.html")
+ install(DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/doc/html" "${CMAKE_CURRENT_BINARY_DIR}/doc/xml"
+ DESTINATION "${CMAKE_INSTALL_DATADIR}/doc/linphone-${LINPHONE_VERSION}")
+ else()
+ message(WARNING "The dot program is needed to generate the linphone documentation. You can get it from http://www.graphviz.org/.")
+ endif()
endif()
endif()
+
+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()
+
+if (ENABLE_TOOLS)
+ set(USE_BUNDLE )
+ if (IOS)
+ set(USE_BUNDLE MACOSX_BUNDLE)
+ endif()
+ add_definitions(-DIN_LINPHONE)
+ file(GLOB EXECUTABLES_SOURCE RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "*.c")
+ foreach(EXECUTABLE ${EXECUTABLES_SOURCE})
+ string(REPLACE ".c" "" EXECUTABLE_NAME ${EXECUTABLE})
+ apply_compile_flags(${EXECUTABLE} "CPP" "C")
+ add_executable(${EXECUTABLE_NAME} ${USE_BUNDLE} ${EXECUTABLE})
+ target_link_libraries(${EXECUTABLE_NAME} ${LINPHONE_LIBS_FOR_TOOLS} ${MEDIASTREAMER2_LIBRARIES})
+ set_target_properties(${EXECUTABLE_NAME} PROPERTIES LINK_FLAGS "${LINPHONE_LDFLAGS}")
+ if (NOT IOS)
+ install(TARGETS ${EXECUTABLE_NAME}
+ RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+ LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+ ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
+ PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE
+ )
+ endif()
+ endforeach()
+endif()
diff --git a/coreapi/help/Doxyfile.in b/coreapi/help/Doxyfile.in
index 12facea9b..b0230913f 100644
--- a/coreapi/help/Doxyfile.in
+++ b/coreapi/help/Doxyfile.in
@@ -1,233 +1,2412 @@
-# Doxyfile 1.4.1
+# 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
#---------------------------------------------------------------------------
-PROJECT_NAME = liblinphone
+
+# 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 = doc
+
+# 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
-USE_WINDOWS_ENCODING = NO
+
+# 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
-ABBREVIATE_BRIEF = "The $name class" \
- "The $name widget" \
- "The $name file" \
- is \
- provides \
- specifies \
- contains \
- represents \
- a \
- an \
- the
+
+# 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
-STRIP_FROM_PATH =
-STRIP_FROM_INC_PATH =
+
+# 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 = NO
+
+# 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
-DETAILS_AT_TOP = 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
-DISTRIBUTE_GROUP_DOC = NO
+
+# 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
-ALIASES = mslist{1}="A list of \ref \1 objects. \xmlonly \1 \endxmlonly"
+
+# 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 = "mslist{1}=A list of \ref \1 objects. \xmlonly \1 \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
#---------------------------------------------------------------------------
-EXTRACT_ALL = NO
-EXTRACT_PRIVATE = NO
-EXTRACT_STATIC = NO
-EXTRACT_LOCAL_CLASSES = YES
-EXTRACT_LOCAL_METHODS = NO
-HIDE_UNDOC_MEMBERS = YES
-HIDE_UNDOC_CLASSES = YES
-HIDE_FRIEND_COMPOUNDS = NO
-HIDE_IN_BODY_DOCS = NO
-INTERNAL_DOCS = NO
-CASE_SENSE_NAMES = YES
-HIDE_SCOPE_NAMES = NO
-SHOW_INCLUDE_FILES = YES
-INLINE_INFO = YES
-SORT_MEMBER_DOCS = NO
-SORT_BRIEF_DOCS = NO
-SORT_BY_SCOPE_NAME = NO
-GENERATE_TODOLIST = YES
-GENERATE_TESTLIST = YES
-GENERATE_BUGLIST = YES
-GENERATE_DEPRECATEDLIST= YES
-ENABLED_SECTIONS =
-MAX_INITIALIZER_LINES = 30
-SHOW_USED_FILES = YES
-SHOW_DIRECTORIES = YES
-FILE_VERSION_FILTER =
-#---------------------------------------------------------------------------
-# configuration options related to warning and progress messages
-#---------------------------------------------------------------------------
-QUIET = NO
-WARNINGS = YES
-WARN_IF_UNDOCUMENTED = YES
-WARN_IF_DOC_ERROR = YES
-WARN_NO_PARAMDOC = NO
-WARN_FORMAT = "$file:$line: $text"
-WARN_LOGFILE =
-#---------------------------------------------------------------------------
-# configuration options related to the input files
-#---------------------------------------------------------------------------
-INPUT = @top_srcdir@/coreapi @top_srcdir@/coreapi/help
-FILE_PATTERNS = *.h \
- *.c \
+# 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 = @top_srcdir@/coreapi \
+ @top_srcdir@/coreapi/help
+
+# 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
-EXCLUDE =
+
+# 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
-EXCLUDE_PATTERNS =
-EXAMPLE_PATH = @top_srcdir@ @top_srcdir@/coreapi/help
-EXAMPLE_PATTERNS =
+
+# 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
+
+# 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
-IMAGE_PATH =
-INPUT_FILTER =
-FILTER_PATTERNS =
+
+# 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
+# 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
+# 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
-IGNORE_PREFIX =
+
+# 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
+# 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 = YES
+
+# 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
-HTML_HEADER =
-HTML_FOOTER =
-HTML_STYLESHEET =
-HTML_ALIGN_MEMBERS = YES
+
+# 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
-CHM_FILE =
-HHC_LOCATION =
+
+# 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
-ENUM_VALUES_PER_LINE = 1
+
+# 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
-#---------------------------------------------------------------------------
-# configuration options related to the LaTeX output
-#---------------------------------------------------------------------------
-GENERATE_LATEX = YES
-LATEX_OUTPUT = latex
-LATEX_CMD_NAME = latex
-MAKEINDEX_CMD_NAME = makeindex
-COMPACT_LATEX = NO
-PAPER_TYPE = a4wide
-EXTRA_PACKAGES =
-LATEX_HEADER =
-PDF_HYPERLINKS = NO
-USE_PDFLATEX = NO
-LATEX_BATCHMODE = NO
-LATEX_HIDE_INDICES = NO
-#---------------------------------------------------------------------------
-# configuration options related to the RTF output
-#---------------------------------------------------------------------------
-GENERATE_RTF = NO
-RTF_OUTPUT = rtf
-COMPACT_RTF = NO
-RTF_HYPERLINKS = NO
-RTF_STYLESHEET_FILE =
-RTF_EXTENSIONS_FILE =
-#---------------------------------------------------------------------------
-# configuration options related to the man page output
-#---------------------------------------------------------------------------
-GENERATE_MAN = YES
-MAN_OUTPUT = man
-MAN_EXTENSION = .3
-MAN_LINKS = NO
-#---------------------------------------------------------------------------
-# configuration options related to the XML output
-#---------------------------------------------------------------------------
-GENERATE_XML = YES
-XML_OUTPUT = xml
-XML_SCHEMA =
-XML_DTD =
-XML_PROGRAMLISTING = YES
-#---------------------------------------------------------------------------
-# configuration options for the AutoGen Definitions output
-#---------------------------------------------------------------------------
-GENERATE_AUTOGEN_DEF = NO
-#---------------------------------------------------------------------------
-# configuration options related to the Perl module output
-#---------------------------------------------------------------------------
-GENERATE_PERLMOD = NO
-PERLMOD_LATEX = NO
-PERLMOD_PRETTY = YES
-PERLMOD_MAKEVAR_PREFIX =
-#---------------------------------------------------------------------------
-# Configuration options related to the preprocessor
-#---------------------------------------------------------------------------
-ENABLE_PREPROCESSING = YES
-MACRO_EXPANSION = YES
-EXPAND_ONLY_PREDEF = NO
-SEARCH_INCLUDES = YES
-INCLUDE_PATH = .
-INCLUDE_FILE_PATTERNS = *.h
-PREDEFINED = DOXYGEN MS2_PUBLIC= LINPHONE_PUBLIC=
-EXPAND_AS_DEFINED =
-SKIP_FUNCTION_MACROS = YES
-#---------------------------------------------------------------------------
-# Configuration::additions related to external references
-#---------------------------------------------------------------------------
-TAGFILES =
-GENERATE_TAGFILE =
-ALLEXTERNALS = NO
-EXTERNAL_GROUPS = YES
-PERL_PATH = /usr/bin/perl
-#---------------------------------------------------------------------------
-# Configuration options related to the dot tool
-#---------------------------------------------------------------------------
-CLASS_DIAGRAMS = YES
-HIDE_UNDOC_RELATIONS = YES
-HAVE_DOT = NO
-CLASS_GRAPH = YES
-COLLABORATION_GRAPH = YES
-GROUP_GRAPHS = YES
-UML_LOOK = NO
-TEMPLATE_RELATIONS = YES
-INCLUDE_GRAPH = YES
-INCLUDED_BY_GRAPH = YES
-CALL_GRAPH = NO
-GRAPHICAL_HIERARCHY = YES
-DIRECTORY_GRAPH = YES
-DOT_IMAGE_FORMAT = png
-DOT_PATH =
-DOTFILE_DIRS =
-MAX_DOT_GRAPH_WIDTH = 1024
-MAX_DOT_GRAPH_HEIGHT = 1024
-MAX_DOT_GRAPH_DEPTH = 1000
-DOT_TRANSPARENT = NO
-DOT_MULTI_TARGETS = NO
-GENERATE_LEGEND = YES
-DOT_CLEANUP = YES
-#---------------------------------------------------------------------------
-# Configuration::additions related to the search engine
-#---------------------------------------------------------------------------
+
+# 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
+# , /