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 +# , /